0
# Nextcord Client and Connection Management
1
2
## Overview
3
4
The nextcord library provides robust client classes for creating Discord bots with sophisticated connection management, event handling, and sharding capabilities. This documentation covers the core client classes and their connection management features.
5
6
## Client Class
7
8
The main bot client for Discord interactions, providing lifecycle management, event handling, and Discord API operations.
9
10
### Basic Client Setup { .api }
11
12
```python
13
import nextcord
14
from nextcord import Intents
15
16
# Basic client initialization
17
intents = Intents.default()
18
intents.message_content = True # Required for message content access
19
20
client = nextcord.Client(intents=intents)
21
22
@client.event
23
async def on_ready():
24
print(f'{client.user} has connected to Discord!')
25
26
# Run the client
27
client.run('your_bot_token')
28
```
29
30
### Client Constructor { .api }
31
32
```python
33
class Client:
34
def __init__(
35
self,
36
*,
37
max_messages: Optional[int] = 1000,
38
connector: Optional[aiohttp.BaseConnector] = None,
39
proxy: Optional[str] = None,
40
proxy_auth: Optional[aiohttp.BasicAuth] = None,
41
shard_id: Optional[int] = None,
42
shard_count: Optional[int] = None,
43
application_id: Optional[int] = None,
44
intents: Intents = Intents.default(),
45
member_cache_flags: MemberCacheFlags = MISSING,
46
chunk_guilds_at_startup: bool = MISSING,
47
status: Optional[Status] = None,
48
activity: Optional[BaseActivity] = None,
49
allowed_mentions: Optional[AllowedMentions] = None,
50
heartbeat_timeout: float = 60.0,
51
guild_ready_timeout: float = 2.0,
52
assume_unsync_clock: bool = True,
53
enable_debug_events: bool = False,
54
loop: Optional[asyncio.AbstractEventLoop] = None,
55
lazy_load_commands: bool = True,
56
rollout_associate_known: bool = True,
57
rollout_delete_unknown: bool = True,
58
rollout_register_new: bool = True,
59
rollout_update_known: bool = True,
60
rollout_all_guilds: bool = False,
61
default_guild_ids: Optional[List[int]] = None,
62
) -> None:
63
```
64
65
**Parameters:**
66
- `max_messages`: Maximum number of messages to store in cache (default: 1000, None disables)
67
- `intents`: Gateway intents controlling which events the bot receives
68
- `heartbeat_timeout`: Maximum seconds before timing out heartbeat (default: 60.0)
69
- `guild_ready_timeout`: Maximum seconds to wait for GUILD_CREATE stream (default: 2.0)
70
- `status`: Initial status upon login (online, idle, dnd, invisible)
71
- `activity`: Initial activity/presence upon login
72
- `allowed_mentions`: Global mention configuration for all messages
73
- `enable_debug_events`: Enable debug events like socket receive/send
74
- `shard_id`/`shard_count`: Manual sharding configuration (use AutoShardedClient instead)
75
76
## Lifecycle Methods
77
78
### Authentication and Connection { .api }
79
80
```python
81
# Login with bot token
82
async def login(self, token: str) -> None:
83
"""Logs in the client with the specified credentials."""
84
85
# Connect to Discord gateway
86
async def connect(self, *, reconnect: bool = True) -> None:
87
"""Creates websocket connection and handles events."""
88
89
# Combined login + connect
90
async def start(self, token: str, *, reconnect: bool = True) -> None:
91
"""Shorthand for login() followed by connect()."""
92
93
# Blocking run method
94
def run(self, token: str, *, reconnect: bool = True) -> None:
95
"""Blocking call that handles the event loop automatically."""
96
```
97
98
### Connection Management { .api }
99
100
```python
101
# Close connection
102
async def close(self) -> None:
103
"""Closes the connection to Discord."""
104
105
# Clear internal state for reconnection
106
def clear(self) -> None:
107
"""Clears internal state - bot can be considered 're-opened'."""
108
109
# Check connection status
110
def is_closed(self) -> bool:
111
"""Returns True if the websocket connection is closed."""
112
113
def is_ready(self) -> bool:
114
"""Returns True if the client's internal cache is ready."""
115
116
# Wait for ready state
117
async def wait_until_ready(self) -> None:
118
"""Waits until the client's internal cache is ready."""
119
```
120
121
## Connection State Properties { .api }
122
123
```python
124
@property
125
def latency(self) -> float:
126
"""Latency between HEARTBEAT and HEARTBEAT_ACK in seconds."""
127
128
@property
129
def user(self) -> Optional[ClientUser]:
130
"""The connected client user. None if not logged in."""
131
132
@property
133
def guilds(self) -> List[Guild]:
134
"""Guilds the connected client is a member of."""
135
136
@property
137
def voice_clients(self) -> List[VoiceProtocol]:
138
"""List of voice connections (usually VoiceClient instances)."""
139
140
def is_ws_ratelimited(self) -> bool:
141
"""Whether the websocket is currently rate limited."""
142
```
143
144
### Data Fetching Methods { .api }
145
146
Core methods for fetching Discord objects by ID, bypassing local cache for fresh data from Discord's API.
147
148
```python
149
async def fetch_user(self, user_id: int, /) -> User:
150
"""
151
Retrieves a User based on their ID from Discord's API.
152
153
Parameters:
154
- user_id: The ID of the user to fetch
155
156
Returns:
157
User: The user object
158
159
Raises:
160
NotFound: User not found
161
HTTPException: Fetching failed
162
"""
163
164
async def fetch_guild(self, guild_id: int, /, *, with_counts: bool = True) -> Guild:
165
"""
166
Retrieves a Guild by ID from Discord's API.
167
168
Parameters:
169
- guild_id: The ID of the guild to fetch
170
- with_counts: Whether to include approximate member/presence counts
171
172
Returns:
173
Guild: The guild object
174
"""
175
176
async def fetch_channel(self, channel_id: int, /) -> Union[GuildChannel, PrivateChannel, Thread]:
177
"""
178
Retrieves a Channel by ID from Discord's API.
179
180
Parameters:
181
- channel_id: The ID of the channel to fetch
182
183
Returns:
184
Union[GuildChannel, PrivateChannel, Thread]: The channel object
185
"""
186
187
async def fetch_guild_preview(self, guild_id: int, /) -> GuildPreview:
188
"""
189
Retrieves a GuildPreview for a public guild.
190
191
Parameters:
192
- guild_id: The ID of the guild to preview
193
194
Returns:
195
GuildPreview: The guild preview
196
"""
197
```
198
199
### Application Command Management { .api }
200
201
Methods for managing and syncing application commands across Discord.
202
203
```python
204
async def sync_all_application_commands(
205
self,
206
data: Optional[Dict[Optional[int], List[ApplicationCommandPayload]]] = None,
207
*,
208
use_rollout: bool = True,
209
associate_known: bool = True,
210
delete_unknown: bool = True,
211
) -> None:
212
"""
213
Syncs all application commands with Discord.
214
215
Parameters:
216
- data: Optional command data to sync
217
- use_rollout: Whether to use command rollout features
218
- associate_known: Whether to associate known commands
219
- delete_unknown: Whether to delete unknown commands from Discord
220
"""
221
222
async def deploy_application_commands(
223
self,
224
*,
225
guild_id: Optional[int] = None,
226
use_rollout: bool = True,
227
) -> None:
228
"""
229
Deploys application commands to Discord.
230
231
Parameters:
232
- guild_id: Optional guild ID to deploy to (None for global)
233
- use_rollout: Whether to use rollout features
234
"""
235
```
236
237
## Event System
238
239
### Event Registration { .api }
240
241
```python
242
# Decorator for event registration
243
def event(self, coro: Callable) -> Callable:
244
"""Decorator to register an event listener."""
245
246
@client.event
247
async def on_message(message):
248
if message.author == client.user:
249
return
250
if message.content.startswith('!hello'):
251
await message.channel.send('Hello!')
252
253
# Wait for specific events
254
async def wait_for(
255
self,
256
event: str,
257
*,
258
check: Optional[Callable[..., bool]] = None,
259
timeout: Optional[float] = None,
260
) -> Any:
261
"""Waits for a WebSocket event to be dispatched."""
262
263
# Example: Wait for user response
264
@client.event
265
async def on_message(message):
266
if message.content == '!question':
267
await message.channel.send('What is your favorite color?')
268
269
def check(m):
270
return m.author == message.author and m.channel == message.channel
271
272
try:
273
response = await client.wait_for('message', check=check, timeout=30.0)
274
await message.channel.send(f'Your favorite color is {response.content}!')
275
except asyncio.TimeoutError:
276
await message.channel.send('You took too long to respond!')
277
```
278
279
### Event Dispatch { .api }
280
281
```python
282
def dispatch(self, event: str, *args: Any, **kwargs: Any) -> None:
283
"""Dispatches an event to registered listeners."""
284
285
# Error handling
286
async def on_error(self, event_method: str, *args: Any, **kwargs: Any) -> None:
287
"""Default error handler - override for custom error handling."""
288
```
289
290
## AutoShardedClient
291
292
Automatically manages multiple shards for large bots (1000+ guilds).
293
294
### AutoShardedClient Setup { .api }
295
296
```python
297
import nextcord
298
from nextcord import AutoShardedClient, Intents
299
300
intents = Intents.default()
301
intents.message_content = True
302
303
# Automatic sharding - library determines shard count
304
client = AutoShardedClient(intents=intents)
305
306
# Manual shard configuration
307
client = AutoShardedClient(
308
intents=intents,
309
shard_count=4,
310
shard_ids=[0, 1] # Only run shards 0 and 1
311
)
312
313
@client.event
314
async def on_ready():
315
print(f'Logged in as {client.user}')
316
print(f'Shard count: {len(client.shards)}')
317
for shard_id, shard in client.shards.items():
318
print(f'Shard {shard_id}: {shard.latency:.2f}s latency')
319
320
client.run('your_bot_token')
321
```
322
323
### AutoShardedClient Constructor { .api }
324
325
```python
326
class AutoShardedClient(Client):
327
def __init__(
328
self,
329
*,
330
shard_ids: Optional[List[int]] = None,
331
shard_count: Optional[int] = None,
332
# ... all Client parameters ...
333
) -> None:
334
```
335
336
**Additional Parameters:**
337
- `shard_ids`: Specific shard IDs to launch (requires shard_count)
338
- `shard_count`: Total number of shards (auto-detected if None)
339
340
### Shard Management { .api }
341
342
```python
343
# Shard information access
344
@property
345
def shards(self) -> Dict[int, ShardInfo]:
346
"""Mapping of shard IDs to ShardInfo objects."""
347
348
def get_shard(self, shard_id: int) -> Optional[ShardInfo]:
349
"""Gets shard information for a specific shard ID."""
350
351
@property
352
def latency(self) -> float:
353
"""Average latency across all shards."""
354
355
@property
356
def latencies(self) -> List[Tuple[int, float]]:
357
"""List of (shard_id, latency) tuples for all shards."""
358
359
# Per-shard presence changes
360
async def change_presence(
361
self,
362
*,
363
activity: Optional[BaseActivity] = None,
364
status: Optional[Status] = None,
365
shard_id: Optional[int] = None, # None = all shards
366
) -> None:
367
"""Changes presence for specific shard or all shards."""
368
```
369
370
### ShardInfo Class { .api }
371
372
```python
373
class ShardInfo:
374
"""Information and control over a specific shard."""
375
376
@property
377
def id(self) -> int:
378
"""The shard ID."""
379
380
@property
381
def shard_count(self) -> Optional[int]:
382
"""Total shard count for this cluster."""
383
384
@property
385
def latency(self) -> float:
386
"""Latency for this specific shard."""
387
388
def is_closed(self) -> bool:
389
"""Whether the shard connection is closed."""
390
391
def is_ws_ratelimited(self) -> bool:
392
"""Whether this shard's websocket is rate limited."""
393
394
async def disconnect(self) -> None:
395
"""Disconnects this shard."""
396
397
async def reconnect(self) -> None:
398
"""Disconnects and reconnects this shard."""
399
400
async def connect(self) -> None:
401
"""Connects this shard if disconnected."""
402
```
403
404
## Connection Management
405
406
### Gateway Connection { .api }
407
408
The client uses WebSocket connections to Discord's gateway for real-time events.
409
410
```python
411
# Connection properties
412
@property
413
def ws(self) -> Optional[DiscordWebSocket]:
414
"""Current websocket gateway connection."""
415
416
# Rate limiting
417
def is_ws_ratelimited(self) -> bool:
418
"""Check if websocket is rate limited."""
419
420
# Reconnection handling
421
async def connect(self, *, reconnect: bool = True) -> None:
422
"""
423
Connect with automatic reconnection handling.
424
425
Parameters:
426
- reconnect: Whether to attempt reconnection on failure
427
"""
428
```
429
430
### Connection Events { .api }
431
432
```python
433
# Connection lifecycle events
434
@client.event
435
async def on_connect():
436
"""Called when client connects to Discord."""
437
438
@client.event
439
async def on_ready():
440
"""Called when client is ready to receive events."""
441
442
@client.event
443
async def on_disconnect():
444
"""Called when client disconnects from Discord."""
445
446
@client.event
447
async def on_resume():
448
"""Called when websocket resumes a previous connection."""
449
450
# Shard-specific events (AutoShardedClient only)
451
@client.event
452
async def on_shard_connect(shard_id: int):
453
"""Called when a shard connects."""
454
455
@client.event
456
async def on_shard_ready(shard_id: int):
457
"""Called when a shard is ready."""
458
459
@client.event
460
async def on_shard_disconnect(shard_id: int):
461
"""Called when a shard disconnects."""
462
463
@client.event
464
async def on_shard_resume(shard_id: int):
465
"""Called when a shard resumes."""
466
```
467
468
## Configuration Options
469
470
### Intents Configuration { .api }
471
472
Intents control which gateway events your bot receives. Configure them based on your bot's needs.
473
474
```python
475
from nextcord import Intents
476
477
# Default intents (recommended starting point)
478
intents = Intents.default() # Excludes privileged intents
479
480
# All intents (requires privileged intents approval)
481
intents = Intents.all()
482
483
# No intents
484
intents = Intents.none()
485
486
# Custom intent configuration
487
intents = Intents.default()
488
intents.members = True # Privileged - member events
489
intents.presences = True # Privileged - presence updates
490
intents.message_content = True # Privileged - message content access
491
intents.guilds = True # Guild events (recommended)
492
intents.guild_messages = True # Guild message events
493
intents.dm_messages = True # DM message events
494
intents.guild_reactions = True # Reaction events
495
intents.voice_states = True # Required for voice features
496
```
497
498
### Intent Flags Reference { .api }
499
500
```python
501
class Intents:
502
# Basic intents
503
guilds: bool # Guild events (join, leave, updates)
504
guild_messages: bool # Guild message events
505
dm_messages: bool # Direct message events
506
guild_reactions: bool # Guild reaction events
507
dm_reactions: bool # DM reaction events
508
guild_typing: bool # Guild typing events
509
dm_typing: bool # DM typing events
510
511
# Privileged intents (require approval for verified bots)
512
members: bool # Member join/leave/update events
513
presences: bool # Member presence/status updates
514
message_content: bool # Access to message content
515
516
# Other intents
517
moderation: bool # Moderation events (bans, audit logs)
518
emojis_and_stickers: bool # Emoji and sticker events
519
integrations: bool # Integration events
520
webhooks: bool # Webhook events
521
invites: bool # Invite create/delete events
522
voice_states: bool # Voice state updates (required for voice)
523
guild_scheduled_events: bool # Scheduled event updates
524
auto_moderation_configuration: bool # Auto-mod rule changes
525
auto_moderation_execution: bool # Auto-mod actions
526
527
# Convenience properties
528
messages: bool # Both guild_messages and dm_messages
529
reactions: bool # Both guild_reactions and dm_reactions
530
typing: bool # Both guild_typing and dm_typing
531
auto_moderation: bool # Both auto_moderation intents
532
```
533
534
### Presence Configuration { .api }
535
536
```python
537
from nextcord import Status, Activity, ActivityType
538
539
# Status configuration
540
client = nextcord.Client(
541
status=Status.idle, # online, idle, dnd, invisible
542
activity=Activity(type=ActivityType.playing, name="with the Discord API")
543
)
544
545
# Change presence after connection
546
await client.change_presence(
547
status=Status.dnd,
548
activity=Activity(type=ActivityType.listening, name="user commands")
549
)
550
551
# Activity types
552
ActivityType.playing # "Playing {name}"
553
ActivityType.listening # "Listening to {name}"
554
ActivityType.watching # "Watching {name}"
555
ActivityType.competing # "Competing in {name}"
556
ActivityType.streaming # "Streaming {name}" (requires url)
557
ActivityType.custom # Custom status
558
```
559
560
### Message Cache Configuration { .api }
561
562
```python
563
# Configure message caching
564
client = nextcord.Client(
565
max_messages=5000, # Cache up to 5000 messages
566
# max_messages=None, # Disable message caching
567
# max_messages=0, # No message caching
568
)
569
570
# Access cached messages
571
@client.event
572
async def on_ready():
573
print(f"Cached {len(client.cached_messages)} messages")
574
for message in client.cached_messages:
575
print(f"{message.author}: {message.content}")
576
```
577
578
### Advanced Configuration { .api }
579
580
```python
581
import aiohttp
582
583
# Advanced client configuration
584
connector = aiohttp.TCPConnector(limit=100)
585
586
client = nextcord.Client(
587
connector=connector, # Custom aiohttp connector
588
proxy="http://proxy.example.com:8080", # HTTP proxy
589
proxy_auth=aiohttp.BasicAuth("user", "pass"), # Proxy authentication
590
heartbeat_timeout=90.0, # Heartbeat timeout (seconds)
591
guild_ready_timeout=5.0, # Guild ready timeout (seconds)
592
assume_unsync_clock=False, # Use system clock for rate limits
593
enable_debug_events=True, # Enable socket debug events
594
chunk_guilds_at_startup=False, # Don't chunk members at startup
595
)
596
597
# Member cache configuration
598
from nextcord import MemberCacheFlags
599
600
member_cache = MemberCacheFlags.all()
601
member_cache.joined = False # Don't cache join times
602
603
client = nextcord.Client(
604
intents=intents,
605
member_cache_flags=member_cache
606
)
607
```
608
609
## Connection Hooks
610
611
### Before Identify Hook { .api }
612
613
```python
614
# Customize identify behavior (useful for large bots)
615
async def before_identify_hook(self, shard_id: Optional[int], *, initial: bool = False) -> None:
616
"""
617
Called before IDENTIFY for each shard.
618
619
Parameters:
620
- shard_id: The shard ID being identified
621
- initial: Whether this is the first IDENTIFY
622
"""
623
if not initial:
624
await asyncio.sleep(5.0) # Default: wait 5 seconds between identifies
625
626
# Custom logic for identify timing
627
if shard_id is not None and shard_id > 0:
628
await asyncio.sleep(shard_id * 2) # Staggered identification
629
```
630
631
## Error Handling
632
633
### Connection Errors { .api }
634
635
```python
636
from nextcord import ConnectionClosed, GatewayNotFound, PrivilegedIntentsRequired
637
638
try:
639
await client.start(token)
640
except ConnectionClosed as e:
641
print(f"Connection closed: {e.code} - {e.reason}")
642
except GatewayNotFound:
643
print("Could not find Discord gateway (API outage?)")
644
except PrivilegedIntentsRequired as e:
645
print(f"Privileged intents required for shard {e.shard_id}")
646
647
# Custom error handling
648
@client.event
649
async def on_error(event, *args, **kwargs):
650
import traceback
651
print(f'Error in {event}:')
652
traceback.print_exc()
653
```
654
655
## Best Practices
656
657
### Production Setup { .api }
658
659
```python
660
import logging
661
import nextcord
662
from nextcord import Intents
663
664
# Configure logging
665
logging.basicConfig(level=logging.INFO)
666
667
# Production client setup
668
intents = Intents.default()
669
intents.message_content = True # Only if needed
670
671
client = nextcord.Client(
672
intents=intents,
673
max_messages=1000, # Reasonable message cache
674
heartbeat_timeout=60.0, # Default heartbeat timeout
675
chunk_guilds_at_startup=False, # Faster startup for large bots
676
)
677
678
@client.event
679
async def on_ready():
680
logging.info(f'{client.user} is ready!')
681
logging.info(f'Latency: {client.latency:.2f}s')
682
logging.info(f'Guilds: {len(client.guilds)}')
683
684
@client.event
685
async def on_error(event, *args, **kwargs):
686
logging.exception(f'Error in event {event}')
687
688
# Graceful shutdown
689
import signal
690
691
def signal_handler(signum, frame):
692
logging.info('Received shutdown signal')
693
client.loop.create_task(client.close())
694
695
signal.signal(signal.SIGINT, signal_handler)
696
signal.signal(signal.SIGTERM, signal_handler)
697
698
client.run(token)
699
```
700
701
### Large Bot Configuration { .api }
702
703
```python
704
# For bots in 1000+ guilds
705
from nextcord import AutoShardedClient, Intents
706
707
intents = Intents.default()
708
intents.members = False # Disable if not needed (reduces memory)
709
intents.presences = False # Disable if not needed (reduces memory)
710
intents.message_content = True # Only if needed
711
712
client = AutoShardedClient(
713
intents=intents,
714
max_messages=500, # Lower message cache per shard
715
chunk_guilds_at_startup=False, # Essential for large bots
716
member_cache_flags=MemberCacheFlags.none(), # Minimize memory usage
717
)
718
719
@client.event
720
async def on_ready():
721
total_guilds = len(client.guilds)
722
shard_count = len(client.shards)
723
avg_latency = client.latency
724
725
print(f'Bot ready: {total_guilds} guilds across {shard_count} shards')
726
print(f'Average latency: {avg_latency:.2f}s')
727
728
# Log per-shard info
729
for shard_id, shard in client.shards.items():
730
guilds_on_shard = len([g for g in client.guilds if g.shard_id == shard_id])
731
print(f'Shard {shard_id}: {guilds_on_shard} guilds, {shard.latency:.2f}s latency')
732
733
client.run(token)
734
```
735
736
This comprehensive guide covers all aspects of nextcord's client and connection management capabilities, providing both basic usage examples and advanced configuration options for production deployments.