or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

application-commands.mdchannels.mdclient.mdcommands.mderrors.mdevents.mdguild.mdindex.mdmessages.mdpermissions.mdtasks.mdui.mdusers.mdutilities.mdvoice.mdwebhooks.md

utilities.mddocs/

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.