0
# Nextcord Utilities
1
2
Utility functions, helper classes, and convenience methods for common Discord bot operations, providing powerful tools for bot development.
3
4
## Core Utilities
5
6
Essential utility functions for searching, filtering, and manipulating Discord objects.
7
8
### Search and Filter Functions { .api }
9
10
```python
11
import nextcord
12
from nextcord import utils
13
from typing import Any, Callable, Iterable, Optional, TypeVar, Union, List
14
import re
15
16
T = TypeVar('T')
17
18
def find(predicate: Callable[[T], Any], iterable: Iterable[T]) -> Optional[T]:
19
"""Find the first element in an iterable that matches the predicate.
20
21
Parameters
22
----------
23
predicate: Callable[[T], Any]
24
A function that returns True for the desired element.
25
iterable: Iterable[T]
26
The iterable to search through.
27
28
Returns
29
-------
30
Optional[T]
31
The first matching element, or None if no match found.
32
33
Examples
34
--------
35
# Find a member by name
36
member = nextcord.utils.find(lambda m: m.name == 'Alice', guild.members)
37
38
# Find a channel by name
39
channel = nextcord.utils.find(lambda c: c.name == 'general', guild.channels)
40
41
# Find a role with specific permissions
42
admin_role = nextcord.utils.find(lambda r: r.permissions.administrator, guild.roles)
43
"""
44
return next((item for item in iterable if predicate(item)), None)
45
46
def get(iterable: Iterable[T], **attrs) -> Optional[T]:
47
"""Get the first element that matches all the given attributes.
48
49
Parameters
50
----------
51
iterable: Iterable[T]
52
The iterable to search through.
53
**attrs
54
Keyword arguments representing attributes to match.
55
56
Returns
57
-------
58
Optional[T]
59
The first matching element, or None if no match found.
60
61
Examples
62
--------
63
# Get a member by name and discriminator
64
member = nextcord.utils.get(guild.members, name='Alice', discriminator='1234')
65
66
# Get a channel by name and type
67
voice_channel = nextcord.utils.get(guild.channels, name='General', type=nextcord.ChannelType.voice)
68
69
# Get a role by name
70
role = nextcord.utils.get(guild.roles, name='Moderator')
71
72
# Get a message by author and content
73
message = nextcord.utils.get(channel.history(limit=100), author=user, content='Hello')
74
"""
75
def predicate(item):
76
return all(getattr(item, attr, None) == value for attr, value in attrs.items())
77
78
return find(predicate, iterable)
79
80
# Advanced search functions
81
def search_members_by_name(
82
guild: nextcord.Guild,
83
query: str,
84
limit: int = 10,
85
case_sensitive: bool = False
86
) -> List[nextcord.Member]:
87
"""Search for members by name with fuzzy matching.
88
89
Parameters
90
----------
91
guild: nextcord.Guild
92
The guild to search in.
93
query: str
94
The search query.
95
limit: int
96
Maximum number of results to return.
97
case_sensitive: bool
98
Whether the search should be case sensitive.
99
100
Returns
101
-------
102
List[nextcord.Member]
103
List of matching members, sorted by relevance.
104
"""
105
if not case_sensitive:
106
query = query.lower()
107
108
exact_matches = []
109
startswith_matches = []
110
contains_matches = []
111
112
for member in guild.members:
113
name = member.display_name if not case_sensitive else member.display_name
114
username = member.name.lower() if not case_sensitive else member.name
115
116
if not case_sensitive:
117
name = name.lower()
118
119
# Exact match (highest priority)
120
if name == query or username == query:
121
exact_matches.append(member)
122
# Starts with query (medium priority)
123
elif name.startswith(query) or username.startswith(query):
124
startswith_matches.append(member)
125
# Contains query (lowest priority)
126
elif query in name or query in username:
127
contains_matches.append(member)
128
129
# Combine results with priority order
130
results = exact_matches + startswith_matches + contains_matches
131
return results[:limit]
132
133
def filter_channels_by_permissions(
134
channels: List[nextcord.abc.GuildChannel],
135
member: nextcord.Member,
136
**required_permissions
137
) -> List[nextcord.abc.GuildChannel]:
138
"""Filter channels by member permissions.
139
140
Parameters
141
----------
142
channels: List[nextcord.abc.GuildChannel]
143
The channels to filter.
144
member: nextcord.Member
145
The member to check permissions for.
146
**required_permissions
147
The required permissions as keyword arguments.
148
149
Returns
150
-------
151
List[nextcord.abc.GuildChannel]
152
Channels where the member has all required permissions.
153
"""
154
filtered_channels = []
155
156
for channel in channels:
157
member_perms = channel.permissions_for(member)
158
159
# Check if member has all required permissions
160
if all(getattr(member_perms, perm, False) == value for perm, value in required_permissions.items()):
161
filtered_channels.append(channel)
162
163
return filtered_channels
164
165
# Usage examples
166
async def utility_examples(guild: nextcord.Guild):
167
"""Examples of utility function usage."""
168
169
# Find examples
170
owner = nextcord.utils.find(lambda m: m.id == guild.owner_id, guild.members)
171
general_channel = nextcord.utils.find(lambda c: 'general' in c.name.lower(), guild.text_channels)
172
muted_role = nextcord.utils.find(lambda r: 'mute' in r.name.lower(), guild.roles)
173
174
# Get examples
175
member = nextcord.utils.get(guild.members, name='Alice')
176
voice_channel = nextcord.utils.get(guild.channels, name='General', type=nextcord.ChannelType.voice)
177
admin_role = nextcord.utils.get(guild.roles, permissions=nextcord.Permissions(administrator=True))
178
179
# Advanced search examples
180
search_results = search_members_by_name(guild, 'ali', limit=5, case_sensitive=False)
181
182
# Filter channels by permissions
183
readable_channels = filter_channels_by_permissions(
184
guild.text_channels,
185
member,
186
read_messages=True,
187
send_messages=True
188
)
189
190
return {
191
'owner': owner,
192
'general_channel': general_channel,
193
'search_results': search_results,
194
'readable_channels': readable_channels
195
}
196
```
197
198
### Time and Date Utilities { .api }
199
200
```python
201
from datetime import datetime, timedelta, timezone
202
import time
203
from typing import Optional, Union
204
205
def utcnow() -> datetime:
206
"""Get the current UTC datetime.
207
208
Returns
209
-------
210
datetime
211
Current UTC datetime.
212
"""
213
return datetime.now(timezone.utc)
214
215
def snowflake_time(snowflake_id: int) -> datetime:
216
"""Extract the creation time from a Discord snowflake ID.
217
218
Parameters
219
----------
220
snowflake_id: int
221
The Discord snowflake ID.
222
223
Returns
224
-------
225
datetime
226
The creation time of the snowflake.
227
"""
228
# Discord epoch (2015-01-01T00:00:00.000Z)
229
discord_epoch = 1420070400000
230
231
# Extract timestamp from snowflake
232
timestamp = ((snowflake_id >> 22) + discord_epoch) / 1000
233
234
return datetime.fromtimestamp(timestamp, tz=timezone.utc)
235
236
def format_dt(dt: datetime, format_type: str = 'f') -> str:
237
"""Format a datetime for Discord's timestamp formatting.
238
239
Parameters
240
----------
241
dt: datetime
242
The datetime to format.
243
format_type: str
244
The Discord timestamp format type:
245
- 't': Short time (16:20)
246
- 'T': Long time (16:20:30)
247
- 'd': Short date (20/04/2021)
248
- 'D': Long date (20 April 2021)
249
- 'f': Short date/time (20 April 2021 16:20)
250
- 'F': Long date/time (Tuesday, 20 April 2021 16:20)
251
- 'R': Relative time (2 months ago)
252
253
Returns
254
-------
255
str
256
Discord-formatted timestamp string.
257
"""
258
timestamp = int(dt.timestamp())
259
return f"<t:{timestamp}:{format_type}>"
260
261
def parse_duration(duration_str: str) -> Optional[timedelta]:
262
"""Parse a human-readable duration string.
263
264
Parameters
265
----------
266
duration_str: str
267
Duration string like '1h30m', '2d', '45s', etc.
268
269
Returns
270
-------
271
Optional[timedelta]
272
Parsed timedelta or None if invalid.
273
274
Examples
275
--------
276
>>> parse_duration('1h30m')
277
datetime.timedelta(seconds=5400)
278
>>> parse_duration('2d12h')
279
datetime.timedelta(days=2, seconds=43200)
280
"""
281
import re
282
283
# Pattern to match time units
284
pattern = r'(?:(\d+)d)?(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?'
285
match = re.match(pattern, duration_str.lower().strip())
286
287
if not match:
288
return None
289
290
days, hours, minutes, seconds = match.groups()
291
292
total_seconds = 0
293
if days:
294
total_seconds += int(days) * 86400
295
if hours:
296
total_seconds += int(hours) * 3600
297
if minutes:
298
total_seconds += int(minutes) * 60
299
if seconds:
300
total_seconds += int(seconds)
301
302
return timedelta(seconds=total_seconds) if total_seconds > 0 else None
303
304
def format_duration(td: timedelta, precision: str = 'seconds') -> str:
305
"""Format a timedelta into a human-readable string.
306
307
Parameters
308
----------
309
td: timedelta
310
The timedelta to format.
311
precision: str
312
The precision level: 'days', 'hours', 'minutes', or 'seconds'.
313
314
Returns
315
-------
316
str
317
Human-readable duration string.
318
"""
319
total_seconds = int(td.total_seconds())
320
321
if total_seconds < 0:
322
return "0 seconds"
323
324
days, remainder = divmod(total_seconds, 86400)
325
hours, remainder = divmod(remainder, 3600)
326
minutes, seconds = divmod(remainder, 60)
327
328
parts = []
329
330
if days and precision in ['days', 'hours', 'minutes', 'seconds']:
331
parts.append(f"{days} day{'s' if days != 1 else ''}")
332
333
if hours and precision in ['hours', 'minutes', 'seconds']:
334
parts.append(f"{hours} hour{'s' if hours != 1 else ''}")
335
336
if minutes and precision in ['minutes', 'seconds']:
337
parts.append(f"{minutes} minute{'s' if minutes != 1 else ''}")
338
339
if seconds and precision == 'seconds':
340
parts.append(f"{seconds} second{'s' if seconds != 1 else ''}")
341
342
if not parts:
343
return "0 seconds"
344
345
return ", ".join(parts)
346
347
def time_since(dt: datetime) -> str:
348
"""Get a human-readable 'time since' string.
349
350
Parameters
351
----------
352
dt: datetime
353
The datetime to compare against now.
354
355
Returns
356
-------
357
str
358
Human-readable time difference.
359
"""
360
now = utcnow()
361
if dt.tzinfo is None:
362
dt = dt.replace(tzinfo=timezone.utc)
363
364
diff = now - dt
365
return format_duration(diff, precision='seconds')
366
367
# Time utility examples
368
def time_utility_examples():
369
"""Examples of time utility usage."""
370
371
# Current time
372
now = utcnow()
373
print(f"Current UTC time: {now}")
374
375
# Snowflake time extraction
376
message_id = 123456789012345678 # Example snowflake
377
creation_time = snowflake_time(message_id)
378
print(f"Message created at: {creation_time}")
379
380
# Discord timestamp formatting
381
future_time = now + timedelta(hours=2)
382
discord_timestamp = format_dt(future_time, 'R')
383
print(f"Future time: {discord_timestamp}")
384
385
# Duration parsing
386
duration = parse_duration('1h30m15s')
387
if duration:
388
print(f"Parsed duration: {duration}")
389
print(f"Formatted: {format_duration(duration)}")
390
391
# Time since
392
past_time = now - timedelta(days=3, hours=2, minutes=30)
393
time_ago = time_since(past_time)
394
print(f"Time since past event: {time_ago}")
395
```
396
397
## OAuth and Invite Utilities
398
399
Functions for generating OAuth URLs and managing invites.
400
401
### OAuth URL Generation { .api }
402
403
```python
404
def oauth_url(
405
client_id: int,
406
*,
407
permissions: Optional[nextcord.Permissions] = None,
408
guild: Optional[nextcord.abc.Snowflake] = None,
409
redirect_uri: Optional[str] = None,
410
scopes: Optional[List[str]] = None,
411
disable_guild_select: bool = False,
412
state: Optional[str] = None
413
) -> str:
414
"""Generate an OAuth2 authorization URL for inviting a bot.
415
416
Parameters
417
----------
418
client_id: int
419
The client ID of the application.
420
permissions: Optional[nextcord.Permissions]
421
The permissions to request.
422
guild: Optional[nextcord.abc.Snowflake]
423
The guild to pre-select for invitation.
424
redirect_uri: Optional[str]
425
The redirect URI after authorization.
426
scopes: Optional[List[str]]
427
The OAuth2 scopes to request.
428
disable_guild_select: bool
429
Whether to disable guild selection.
430
state: Optional[str]
431
The state parameter for security.
432
433
Returns
434
-------
435
str
436
The OAuth2 authorization URL.
437
438
Examples
439
--------
440
# Basic bot invite URL
441
invite_url = nextcord.utils.oauth_url(
442
client_id=123456789,
443
permissions=nextcord.Permissions(send_messages=True, read_messages=True)
444
)
445
446
# Admin bot invite URL
447
admin_url = nextcord.utils.oauth_url(
448
client_id=123456789,
449
permissions=nextcord.Permissions(administrator=True)
450
)
451
"""
452
from urllib.parse import urlencode
453
454
base_url = "https://discord.com/api/oauth2/authorize"
455
456
params = {
457
'client_id': client_id,
458
'scope': ' '.join(scopes or ['bot', 'applications.commands'])
459
}
460
461
if permissions is not None:
462
params['permissions'] = permissions.value
463
464
if guild is not None:
465
params['guild_id'] = guild.id
466
467
if redirect_uri is not None:
468
params['redirect_uri'] = redirect_uri
469
470
if disable_guild_select:
471
params['disable_guild_select'] = 'true'
472
473
if state is not None:
474
params['state'] = state
475
476
params['response_type'] = 'code'
477
478
return f"{base_url}?{urlencode(params)}"
479
480
def create_invite_embed(
481
bot_name: str,
482
client_id: int,
483
permissions: Optional[nextcord.Permissions] = None,
484
description: Optional[str] = None
485
) -> nextcord.Embed:
486
"""Create an embed with bot invite information.
487
488
Parameters
489
----------
490
bot_name: str
491
The name of the bot.
492
client_id: int
493
The client ID of the bot.
494
permissions: Optional[nextcord.Permissions]
495
The permissions the bot needs.
496
description: Optional[str]
497
Additional description for the bot.
498
499
Returns
500
-------
501
nextcord.Embed
502
Embed containing invite information.
503
"""
504
invite_url = oauth_url(client_id, permissions=permissions)
505
506
embed = nextcord.Embed(
507
title=f"π€ Invite {bot_name}",
508
description=description or f"Click the link below to add {bot_name} to your server!",
509
color=nextcord.Color.blue()
510
)
511
512
embed.add_field(
513
name="π Invite Link",
514
value=f"[Click here to invite]({invite_url})",
515
inline=False
516
)
517
518
if permissions:
519
# List key permissions
520
key_perms = []
521
perm_names = {
522
'administrator': 'π Administrator',
523
'manage_guild': 'βοΈ Manage Server',
524
'manage_roles': 'π Manage Roles',
525
'manage_channels': 'π Manage Channels',
526
'kick_members': 'π’ Kick Members',
527
'ban_members': 'π¨ Ban Members',
528
'send_messages': 'π¬ Send Messages',
529
'read_messages': 'π Read Messages',
530
'manage_messages': 'ποΈ Manage Messages',
531
'connect': 'π Connect to Voice',
532
'speak': 'π€ Speak in Voice'
533
}
534
535
for perm, display_name in perm_names.items():
536
if getattr(permissions, perm, False):
537
key_perms.append(display_name)
538
539
if key_perms:
540
embed.add_field(
541
name="π Key Permissions",
542
value="\n".join(key_perms[:10]), # Show first 10
543
inline=True
544
)
545
546
embed.set_footer(text="Make sure you have Manage Server permission in the target server")
547
548
return embed
549
550
# Invite management utilities
551
async def get_invite_info(invite_code: str) -> Optional[dict]:
552
"""Get information about an invite.
553
554
Parameters
555
----------
556
invite_code: str
557
The invite code (e.g., 'abc123' from discord.gg/abc123).
558
559
Returns
560
-------
561
Optional[dict]
562
Dictionary containing invite information.
563
"""
564
try:
565
invite = await bot.fetch_invite(invite_code)
566
567
return {
568
'code': invite.code,
569
'guild_name': invite.guild.name if invite.guild else None,
570
'guild_id': invite.guild.id if invite.guild else None,
571
'channel_name': invite.channel.name if invite.channel else None,
572
'inviter': str(invite.inviter) if invite.inviter else None,
573
'member_count': invite.approximate_member_count,
574
'presence_count': invite.approximate_presence_count,
575
'expires_at': invite.expires_at,
576
'max_uses': invite.max_uses,
577
'uses': invite.uses,
578
'temporary': invite.temporary,
579
'created_at': invite.created_at,
580
'url': invite.url
581
}
582
except nextcord.NotFound:
583
return None
584
except nextcord.Forbidden:
585
return None
586
587
async def create_guild_invite(
588
channel: nextcord.TextChannel,
589
max_age: int = 0,
590
max_uses: int = 0,
591
temporary: bool = False,
592
unique: bool = True,
593
reason: Optional[str] = None
594
) -> Optional[nextcord.Invite]:
595
"""Create a guild invite with specified parameters.
596
597
Parameters
598
----------
599
channel: nextcord.TextChannel
600
The channel to create the invite for.
601
max_age: int
602
How long the invite should last in seconds (0 = forever).
603
max_uses: int
604
How many times the invite can be used (0 = unlimited).
605
temporary: bool
606
Whether members should be kicked when they disconnect without a role.
607
unique: bool
608
Whether to create a unique invite or reuse existing.
609
reason: Optional[str]
610
The reason for creating the invite.
611
612
Returns
613
-------
614
Optional[nextcord.Invite]
615
The created invite or None if failed.
616
"""
617
try:
618
invite = await channel.create_invite(
619
max_age=max_age,
620
max_uses=max_uses,
621
temporary=temporary,
622
unique=unique,
623
reason=reason
624
)
625
return invite
626
except nextcord.Forbidden:
627
return None
628
except nextcord.HTTPException:
629
return None
630
```
631
632
## Text Processing Utilities
633
634
Functions for text manipulation, formatting, and cleaning.
635
636
### Text Formatting and Cleaning { .api }
637
638
```python
639
import re
640
from typing import List, Optional, Dict, Any
641
642
def escape_markdown(text: str) -> str:
643
"""Escape Discord markdown formatting in text.
644
645
Parameters
646
----------
647
text: str
648
The text to escape.
649
650
Returns
651
-------
652
str
653
Text with Discord markdown characters escaped.
654
"""
655
# Characters that need escaping in Discord markdown
656
chars_to_escape = ['*', '_', '`', '~', '|', '\\']
657
658
for char in chars_to_escape:
659
text = text.replace(char, '\\' + char)
660
661
return text
662
663
def escape_mentions(text: str) -> str:
664
"""Escape Discord mentions in text.
665
666
Parameters
667
----------
668
text: str
669
The text to escape mentions in.
670
671
Returns
672
-------
673
str
674
Text with mentions escaped.
675
"""
676
# Escape user mentions
677
text = re.sub(r'<@!?(\d+)>', r'<@\\\1>', text)
678
679
# Escape role mentions
680
text = re.sub(r'<@&(\d+)>', r'<@&\\\1>', text)
681
682
# Escape channel mentions
683
text = re.sub(r'<#(\d+)>', r'<#\\\1>', text)
684
685
# Escape @everyone and @here
686
text = text.replace('@everyone', '@\u200beveryone')
687
text = text.replace('@here', '@\u200bhere')
688
689
return text
690
691
def clean_content(content: str, *, guild: Optional[nextcord.Guild] = None) -> str:
692
"""Clean message content by replacing mentions with names.
693
694
Parameters
695
----------
696
content: str
697
The message content to clean.
698
guild: Optional[nextcord.Guild]
699
The guild context for resolving mentions.
700
701
Returns
702
-------
703
str
704
Cleaned content with mentions replaced by names.
705
"""
706
if not guild:
707
return content
708
709
# Replace user mentions
710
def replace_user_mention(match):
711
user_id = int(match.group(1))
712
member = guild.get_member(user_id)
713
return f'@{member.display_name}' if member else match.group(0)
714
715
content = re.sub(r'<@!?(\d+)>', replace_user_mention, content)
716
717
# Replace role mentions
718
def replace_role_mention(match):
719
role_id = int(match.group(1))
720
role = guild.get_role(role_id)
721
return f'@{role.name}' if role else match.group(0)
722
723
content = re.sub(r'<@&(\d+)>', replace_role_mention, content)
724
725
# Replace channel mentions
726
def replace_channel_mention(match):
727
channel_id = int(match.group(1))
728
channel = guild.get_channel(channel_id)
729
return f'#{channel.name}' if channel else match.group(0)
730
731
content = re.sub(r'<#(\d+)>', replace_channel_mention, content)
732
733
return content
734
735
def truncate_text(text: str, max_length: int, suffix: str = '...') -> str:
736
"""Truncate text to a maximum length.
737
738
Parameters
739
----------
740
text: str
741
The text to truncate.
742
max_length: int
743
Maximum length including suffix.
744
suffix: str
745
Suffix to add when truncating.
746
747
Returns
748
-------
749
str
750
Truncated text.
751
"""
752
if len(text) <= max_length:
753
return text
754
755
return text[:max_length - len(suffix)] + suffix
756
757
def format_list(
758
items: List[str],
759
max_items: int = 10,
760
conjunction: str = 'and',
761
format_func: Optional[callable] = None
762
) -> str:
763
"""Format a list of items into a readable string.
764
765
Parameters
766
----------
767
items: List[str]
768
The items to format.
769
max_items: int
770
Maximum number of items to display.
771
conjunction: str
772
Word to use before the last item ('and' or 'or').
773
format_func: Optional[callable]
774
Function to format each item.
775
776
Returns
777
-------
778
str
779
Formatted list string.
780
"""
781
if not items:
782
return "none"
783
784
if format_func:
785
items = [format_func(item) for item in items]
786
787
# Truncate if too many items
788
displayed_items = items[:max_items]
789
remaining = len(items) - max_items
790
791
if len(displayed_items) == 1:
792
result = displayed_items[0]
793
elif len(displayed_items) == 2:
794
result = f"{displayed_items[0]} {conjunction} {displayed_items[1]}"
795
else:
796
result = ", ".join(displayed_items[:-1]) + f", {conjunction} {displayed_items[-1]}"
797
798
if remaining > 0:
799
result += f" {conjunction} {remaining} more"
800
801
return result
802
803
def pluralize(count: int, singular: str, plural: Optional[str] = None) -> str:
804
"""Return the singular or plural form based on count.
805
806
Parameters
807
----------
808
count: int
809
The count to check.
810
singular: str
811
The singular form.
812
plural: Optional[str]
813
The plural form (defaults to singular + 's').
814
815
Returns
816
-------
817
str
818
The appropriate form.
819
"""
820
if plural is None:
821
plural = singular + 's'
822
823
return singular if count == 1 else plural
824
825
def extract_code_blocks(content: str) -> List[Dict[str, str]]:
826
"""Extract code blocks from message content.
827
828
Parameters
829
----------
830
content: str
831
The message content.
832
833
Returns
834
-------
835
List[Dict[str, str]]
836
List of code blocks with 'language' and 'code' keys.
837
"""
838
# Pattern for code blocks with optional language
839
pattern = r'```(?:(\w+)\n)?(.*?)```'
840
841
blocks = []
842
for match in re.finditer(pattern, content, re.DOTALL):
843
language = match.group(1) or 'text'
844
code = match.group(2).strip()
845
846
blocks.append({
847
'language': language,
848
'code': code
849
})
850
851
return blocks
852
853
def create_progress_bar(
854
current: int,
855
total: int,
856
length: int = 20,
857
fill_char: str = 'β',
858
empty_char: str = 'β'
859
) -> str:
860
"""Create a text-based progress bar.
861
862
Parameters
863
----------
864
current: int
865
Current progress value.
866
total: int
867
Total/maximum value.
868
length: int
869
Length of the progress bar.
870
fill_char: str
871
Character to use for filled portion.
872
empty_char: str
873
Character to use for empty portion.
874
875
Returns
876
-------
877
str
878
Progress bar string.
879
"""
880
if total <= 0:
881
return empty_char * length
882
883
progress = min(current / total, 1.0) # Clamp to 1.0
884
filled_length = int(length * progress)
885
886
bar = fill_char * filled_length + empty_char * (length - filled_length)
887
percentage = int(progress * 100)
888
889
return f"{bar} {percentage}%"
890
891
# Text processing examples
892
def text_processing_examples():
893
"""Examples of text processing utilities."""
894
895
# Markdown escaping
896
text_with_markdown = "This has **bold** and *italic* text"
897
escaped = escape_markdown(text_with_markdown)
898
print(f"Escaped: {escaped}")
899
900
# Mention escaping
901
text_with_mentions = "Hello <@123456> and @everyone!"
902
safe_text = escape_mentions(text_with_mentions)
903
print(f"Safe text: {safe_text}")
904
905
# Text truncation
906
long_text = "This is a very long piece of text that needs to be truncated"
907
short_text = truncate_text(long_text, 30)
908
print(f"Truncated: {short_text}")
909
910
# List formatting
911
items = ["apple", "banana", "cherry", "date"]
912
formatted_list = format_list(items, conjunction='and')
913
print(f"Formatted list: {formatted_list}")
914
915
# Pluralization
916
count = 5
917
word = pluralize(count, "item")
918
print(f"You have {count} {word}")
919
920
# Progress bar
921
progress = create_progress_bar(75, 100, length=15)
922
print(f"Progress: {progress}")
923
924
# Code block extraction
925
message_content = """
926
Here's some Python code:
927
```python
928
def hello():
929
print("Hello, world!")
930
```
931
932
And some JavaScript:
933
```js
934
console.log("Hello, world!");
935
```
936
"""
937
938
code_blocks = extract_code_blocks(message_content)
939
for block in code_blocks:
940
print(f"Language: {block['language']}")
941
print(f"Code: {block['code']}")
942
```
943
944
## Embed Utilities
945
946
Helper functions for creating and manipulating embeds.
947
948
### Embed Creation Helpers { .api }
949
950
```python
951
def create_error_embed(
952
title: str = "Error",
953
description: str = "An error occurred",
954
**kwargs
955
) -> nextcord.Embed:
956
"""Create a standardized error embed.
957
958
Parameters
959
----------
960
title: str
961
The error title.
962
description: str
963
The error description.
964
**kwargs
965
Additional embed parameters.
966
967
Returns
968
-------
969
nextcord.Embed
970
Error embed with red color.
971
"""
972
embed = nextcord.Embed(
973
title=f"β {title}",
974
description=description,
975
color=nextcord.Color.red(),
976
**kwargs
977
)
978
return embed
979
980
def create_success_embed(
981
title: str = "Success",
982
description: str = "Operation completed successfully",
983
**kwargs
984
) -> nextcord.Embed:
985
"""Create a standardized success embed.
986
987
Parameters
988
----------
989
title: str
990
The success title.
991
description: str
992
The success description.
993
**kwargs
994
Additional embed parameters.
995
996
Returns
997
-------
998
nextcord.Embed
999
Success embed with green color.
1000
"""
1001
embed = nextcord.Embed(
1002
title=f"β {title}",
1003
description=description,
1004
color=nextcord.Color.green(),
1005
**kwargs
1006
)
1007
return embed
1008
1009
def create_info_embed(
1010
title: str = "Information",
1011
description: str = "Here's some information",
1012
**kwargs
1013
) -> nextcord.Embed:
1014
"""Create a standardized info embed.
1015
1016
Parameters
1017
----------
1018
title: str
1019
The info title.
1020
description: str
1021
The info description.
1022
**kwargs
1023
Additional embed parameters.
1024
1025
Returns
1026
-------
1027
nextcord.Embed
1028
Info embed with blue color.
1029
"""
1030
embed = nextcord.Embed(
1031
title=f"βΉοΈ {title}",
1032
description=description,
1033
color=nextcord.Color.blue(),
1034
**kwargs
1035
)
1036
return embed
1037
1038
def create_warning_embed(
1039
title: str = "Warning",
1040
description: str = "Please pay attention to this warning",
1041
**kwargs
1042
) -> nextcord.Embed:
1043
"""Create a standardized warning embed.
1044
1045
Parameters
1046
----------
1047
title: str
1048
The warning title.
1049
description: str
1050
The warning description.
1051
**kwargs
1052
Additional embed parameters.
1053
1054
Returns
1055
-------
1056
nextcord.Embed
1057
Warning embed with orange color.
1058
"""
1059
embed = nextcord.Embed(
1060
title=f"β οΈ {title}",
1061
description=description,
1062
color=nextcord.Color.orange(),
1063
**kwargs
1064
)
1065
return embed
1066
1067
def paginate_embed_fields(
1068
title: str,
1069
fields: List[Dict[str, Any]],
1070
fields_per_page: int = 25,
1071
**embed_kwargs
1072
) -> List[nextcord.Embed]:
1073
"""Paginate embed fields across multiple embeds.
1074
1075
Parameters
1076
----------
1077
title: str
1078
Base title for all embeds.
1079
fields: List[Dict[str, Any]]
1080
List of field dictionaries with 'name', 'value', and optional 'inline' keys.
1081
fields_per_page: int
1082
Maximum fields per embed (Discord limit is 25).
1083
**embed_kwargs
1084
Additional embed parameters.
1085
1086
Returns
1087
-------
1088
List[nextcord.Embed]
1089
List of paginated embeds.
1090
"""
1091
if fields_per_page > 25:
1092
fields_per_page = 25 # Discord limit
1093
1094
embeds = []
1095
total_pages = (len(fields) + fields_per_page - 1) // fields_per_page
1096
1097
for page in range(total_pages):
1098
start_idx = page * fields_per_page
1099
end_idx = start_idx + fields_per_page
1100
page_fields = fields[start_idx:end_idx]
1101
1102
embed = nextcord.Embed(
1103
title=f"{title} (Page {page + 1}/{total_pages})",
1104
**embed_kwargs
1105
)
1106
1107
for field in page_fields:
1108
embed.add_field(
1109
name=field['name'],
1110
value=field['value'],
1111
inline=field.get('inline', True)
1112
)
1113
1114
embeds.append(embed)
1115
1116
return embeds
1117
1118
def create_user_embed(user: nextcord.User, **kwargs) -> nextcord.Embed:
1119
"""Create an embed with user information.
1120
1121
Parameters
1122
----------
1123
user: nextcord.User
1124
The user to create an embed for.
1125
**kwargs
1126
Additional embed parameters.
1127
1128
Returns
1129
-------
1130
nextcord.Embed
1131
User information embed.
1132
"""
1133
embed = nextcord.Embed(
1134
title=f"User: {user.display_name}",
1135
color=getattr(user, 'color', nextcord.Color.blue()),
1136
**kwargs
1137
)
1138
1139
embed.set_thumbnail(url=user.display_avatar.url)
1140
embed.add_field(name="Username", value=str(user), inline=True)
1141
embed.add_field(name="ID", value=user.id, inline=True)
1142
embed.add_field(name="Bot", value="Yes" if user.bot else "No", inline=True)
1143
embed.add_field(name="Created", value=format_dt(user.created_at, 'D'), inline=True)
1144
1145
return embed
1146
1147
def create_guild_embed(guild: nextcord.Guild, **kwargs) -> nextcord.Embed:
1148
"""Create an embed with guild information.
1149
1150
Parameters
1151
----------
1152
guild: nextcord.Guild
1153
The guild to create an embed for.
1154
**kwargs
1155
Additional embed parameters.
1156
1157
Returns
1158
-------
1159
nextcord.Embed
1160
Guild information embed.
1161
"""
1162
embed = nextcord.Embed(
1163
title=f"Server: {guild.name}",
1164
description=guild.description or "No description",
1165
color=nextcord.Color.blue(),
1166
**kwargs
1167
)
1168
1169
if guild.icon:
1170
embed.set_thumbnail(url=guild.icon.url)
1171
1172
embed.add_field(name="Owner", value=guild.owner.mention if guild.owner else "Unknown", inline=True)
1173
embed.add_field(name="Members", value=f"{guild.member_count:,}", inline=True)
1174
embed.add_field(name="Channels", value=len(guild.channels), inline=True)
1175
embed.add_field(name="Roles", value=len(guild.roles), inline=True)
1176
embed.add_field(name="Boosts", value=guild.premium_subscription_count, inline=True)
1177
embed.add_field(name="Boost Level", value=guild.premium_tier, inline=True)
1178
embed.add_field(name="Created", value=format_dt(guild.created_at, 'D'), inline=True)
1179
1180
return embed
1181
1182
# Embed utility examples
1183
def embed_utility_examples():
1184
"""Examples of embed utility usage."""
1185
1186
# Standard embed types
1187
error_embed = create_error_embed("Command Failed", "Invalid arguments provided")
1188
success_embed = create_success_embed("Action Complete", "User has been banned successfully")
1189
info_embed = create_info_embed("Bot Information", "This bot helps manage your server")
1190
warning_embed = create_warning_embed("Rate Limit", "You're sending messages too quickly")
1191
1192
# Paginated fields
1193
many_fields = [
1194
{"name": f"Field {i}", "value": f"Value {i}", "inline": True}
1195
for i in range(50)
1196
]
1197
1198
paginated_embeds = paginate_embed_fields(
1199
"Large Dataset",
1200
many_fields,
1201
fields_per_page=10,
1202
color=nextcord.Color.purple()
1203
)
1204
1205
print(f"Created {len(paginated_embeds)} paginated embeds")
1206
1207
return {
1208
'error': error_embed,
1209
'success': success_embed,
1210
'info': info_embed,
1211
'warning': warning_embed,
1212
'paginated': paginated_embeds
1213
}
1214
```
1215
1216
## File and Data Utilities
1217
1218
Helper functions for file handling and data processing.
1219
1220
### File Operations { .api }
1221
1222
```python
1223
import json
1224
import csv
1225
import io
1226
from pathlib import Path
1227
from typing import Any, Dict, List, Union, Optional
1228
1229
def safe_json_load(file_path: Union[str, Path], default: Any = None) -> Any:
1230
"""Safely load JSON from a file.
1231
1232
Parameters
1233
----------
1234
file_path: Union[str, Path]
1235
Path to the JSON file.
1236
default: Any
1237
Default value to return if file doesn't exist or is invalid.
1238
1239
Returns
1240
-------
1241
Any
1242
Loaded JSON data or default value.
1243
"""
1244
try:
1245
with open(file_path, 'r', encoding='utf-8') as f:
1246
return json.load(f)
1247
except (FileNotFoundError, json.JSONDecodeError, IOError):
1248
return default
1249
1250
def safe_json_save(data: Any, file_path: Union[str, Path], indent: int = 2) -> bool:
1251
"""Safely save data to a JSON file.
1252
1253
Parameters
1254
----------
1255
data: Any
1256
Data to save.
1257
file_path: Union[str, Path]
1258
Path to save the JSON file.
1259
indent: int
1260
JSON indentation.
1261
1262
Returns
1263
-------
1264
bool
1265
True if successful, False otherwise.
1266
"""
1267
try:
1268
# Ensure directory exists
1269
Path(file_path).parent.mkdir(parents=True, exist_ok=True)
1270
1271
with open(file_path, 'w', encoding='utf-8') as f:
1272
json.dump(data, f, indent=indent, ensure_ascii=False)
1273
return True
1274
except (IOError, TypeError):
1275
return False
1276
1277
def create_data_file(
1278
data: List[Dict[str, Any]],
1279
filename: str,
1280
format_type: str = 'json'
1281
) -> nextcord.File:
1282
"""Create a Discord file from data.
1283
1284
Parameters
1285
----------
1286
data: List[Dict[str, Any]]
1287
Data to include in the file.
1288
filename: str
1289
Name for the file.
1290
format_type: str
1291
Format type: 'json' or 'csv'.
1292
1293
Returns
1294
-------
1295
nextcord.File
1296
Discord file object.
1297
"""
1298
if format_type.lower() == 'json':
1299
content = json.dumps(data, indent=2, ensure_ascii=False)
1300
file_extension = '.json'
1301
1302
elif format_type.lower() == 'csv':
1303
if not data:
1304
content = ""
1305
else:
1306
output = io.StringIO()
1307
writer = csv.DictWriter(output, fieldnames=data[0].keys())
1308
writer.writeheader()
1309
writer.writerows(data)
1310
content = output.getvalue()
1311
file_extension = '.csv'
1312
1313
else:
1314
raise ValueError(f"Unsupported format: {format_type}")
1315
1316
# Add extension if not present
1317
if not filename.endswith(file_extension):
1318
filename += file_extension
1319
1320
file_bytes = content.encode('utf-8')
1321
return nextcord.File(io.BytesIO(file_bytes), filename=filename)
1322
1323
def format_file_size(size_bytes: int) -> str:
1324
"""Format file size in human-readable format.
1325
1326
Parameters
1327
----------
1328
size_bytes: int
1329
Size in bytes.
1330
1331
Returns
1332
-------
1333
str
1334
Human-readable size string.
1335
"""
1336
if size_bytes == 0:
1337
return "0 B"
1338
1339
size_names = ["B", "KB", "MB", "GB", "TB"]
1340
i = 0
1341
1342
while size_bytes >= 1024 and i < len(size_names) - 1:
1343
size_bytes /= 1024.0
1344
i += 1
1345
1346
return f"{size_bytes:.1f} {size_names[i]}"
1347
1348
def backup_data(data: Dict[str, Any], backup_dir: str = "backups") -> bool:
1349
"""Create a timestamped backup of data.
1350
1351
Parameters
1352
----------
1353
data: Dict[str, Any]
1354
Data to backup.
1355
backup_dir: str
1356
Directory to store backups.
1357
1358
Returns
1359
-------
1360
bool
1361
True if backup was successful.
1362
"""
1363
from datetime import datetime
1364
1365
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
1366
backup_path = Path(backup_dir) / f"backup_{timestamp}.json"
1367
1368
return safe_json_save(data, backup_path)
1369
1370
# Data processing utilities
1371
def chunk_list(lst: List[Any], chunk_size: int) -> List[List[Any]]:
1372
"""Split a list into chunks of specified size.
1373
1374
Parameters
1375
----------
1376
lst: List[Any]
1377
The list to chunk.
1378
chunk_size: int
1379
Size of each chunk.
1380
1381
Returns
1382
-------
1383
List[List[Any]]
1384
List of chunks.
1385
"""
1386
return [lst[i:i + chunk_size] for i in range(0, len(lst), chunk_size)]
1387
1388
def flatten_dict(d: Dict[str, Any], parent_key: str = '', sep: str = '.') -> Dict[str, Any]:
1389
"""Flatten a nested dictionary.
1390
1391
Parameters
1392
----------
1393
d: Dict[str, Any]
1394
Dictionary to flatten.
1395
parent_key: str
1396
Parent key prefix.
1397
sep: str
1398
Separator for nested keys.
1399
1400
Returns
1401
-------
1402
Dict[str, Any]
1403
Flattened dictionary.
1404
"""
1405
items = []
1406
1407
for k, v in d.items():
1408
new_key = f"{parent_key}{sep}{k}" if parent_key else k
1409
1410
if isinstance(v, dict):
1411
items.extend(flatten_dict(v, new_key, sep=sep).items())
1412
else:
1413
items.append((new_key, v))
1414
1415
return dict(items)
1416
1417
# File utility examples
1418
async def file_utility_examples(channel: nextcord.TextChannel):
1419
"""Examples of file utility usage."""
1420
1421
# Create data file
1422
sample_data = [
1423
{"name": "Alice", "age": 25, "role": "Admin"},
1424
{"name": "Bob", "age": 30, "role": "Moderator"},
1425
{"name": "Charlie", "age": 22, "role": "Member"}
1426
]
1427
1428
# Create JSON file
1429
json_file = create_data_file(sample_data, "members", "json")
1430
await channel.send("Here's the member data:", file=json_file)
1431
1432
# Create CSV file
1433
csv_file = create_data_file(sample_data, "members", "csv")
1434
await channel.send("Here's the same data in CSV format:", file=csv_file)
1435
1436
# File size formatting
1437
file_size = format_file_size(1024 * 1024 * 2.5) # 2.5 MB
1438
print(f"File size: {file_size}")
1439
1440
# List chunking
1441
large_list = list(range(100))
1442
chunks = chunk_list(large_list, 10)
1443
print(f"Split list into {len(chunks)} chunks of 10")
1444
1445
# Dictionary flattening
1446
nested_dict = {
1447
"user": {
1448
"profile": {
1449
"name": "Alice",
1450
"age": 25
1451
},
1452
"settings": {
1453
"theme": "dark",
1454
"notifications": True
1455
}
1456
}
1457
}
1458
1459
flat_dict = flatten_dict(nested_dict)
1460
print("Flattened dictionary:", flat_dict)
1461
```
1462
1463
This comprehensive documentation covers all major utility functions in nextcord, providing developers with powerful tools for common bot development tasks including search operations, text processing, embed creation, time handling, and file operations.