1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
|
# This example requires the 'members' privileged intent to use the Member converter.
# This example also requires the 'message_content' privileged intent to function.
import traceback
import typing
import discord
from discord.ext import commands
intents = discord.Intents.default()
intents.members = True
intents.message_content = True
bot = commands.Bot('!', intents=intents)
@bot.command()
async def userinfo(ctx: commands.Context, user: discord.User):
# In the command signature above, you can see that the `user`
# parameter is typehinted to `discord.User`. This means that
# during command invocation we will attempt to convert
# the value passed as `user` to a `discord.User` instance.
# The documentation notes what can be converted, in the case of `discord.User`
# you pass an ID, mention or username (discrim optional)
# E.g. 80088516616269824, @Danny or Danny#0007
# NOTE: typehinting acts as a converter within the `commands` framework only.
# In standard Python, it is use for documentation and IDE assistance purposes.
# If the conversion is successful, we will have a `discord.User` instance
# and can do the following:
user_id = user.id
username = user.name
avatar = user.display_avatar.url
await ctx.send(f'User found: {user_id} -- {username}\n{avatar}')
@userinfo.error
async def userinfo_error(ctx: commands.Context, error: commands.CommandError):
# If the conversion above fails for any reason, it will raise `commands.BadArgument`
# so we handle this in this error handler:
if isinstance(error, commands.BadArgument):
return await ctx.send('Couldn\'t find that user.')
# The default `on_command_error` will ignore errors from this command
# because we made our own command-specific error handler,
# so we need to log tracebacks ourselves.
else:
traceback.print_exception(type(error), error, error.__traceback__)
# Custom Converter here
class ChannelOrMemberConverter(commands.Converter):
async def convert(self, ctx: commands.Context, argument: str):
# In this example we have made a custom converter.
# This checks if an input is convertible to a
# `discord.Member` or `discord.TextChannel` instance from the
# input the user has given us using the pre-existing converters
# that the library provides.
member_converter = commands.MemberConverter()
try:
# Try and convert to a Member instance.
# If this fails, then an exception is raised.
# Otherwise, we just return the converted member value.
member = await member_converter.convert(ctx, argument)
except commands.MemberNotFound:
pass
else:
return member
# Do the same for TextChannel...
textchannel_converter = commands.TextChannelConverter()
try:
channel = await textchannel_converter.convert(ctx, argument)
except commands.ChannelNotFound:
pass
else:
return channel
# If the value could not be converted we can raise an error
# so our error handlers can deal with it in one place.
# The error has to be CommandError derived, so BadArgument works fine here.
raise commands.BadArgument(f'No Member or TextChannel could be converted from "{argument}"')
# Make it so the converter is friendly to type checkers
# The first parameter of typing.Annotated is the type we tell the type checker
# The second parameter is what converter the library uses
ChannelOrMember = typing.Annotated[typing.Union[discord.Member, discord.TextChannel], ChannelOrMemberConverter]
@bot.command()
async def notify(ctx: commands.Context, target: ChannelOrMember):
# This command signature utilises the custom converter written above
# What will happen during command invocation is that the `target` above will be passed to
# the `argument` parameter of the `ChannelOrMemberConverter.convert` method and
# the conversion will go through the process defined there.
await target.send(f'Hello, {target.name}!')
@bot.command()
async def ignore(ctx: commands.Context, target: typing.Union[discord.Member, discord.TextChannel]):
# This command signature utilises the `typing.Union` typehint.
# The `commands` framework attempts a conversion of each type in this Union *in order*.
# So, it will attempt to convert whatever is passed to `target` to a `discord.Member` instance.
# If that fails, it will attempt to convert it to a `discord.TextChannel` instance.
# See: https://discordpy.readthedocs.io/en/latest/ext/commands/commands.html#typing-union
# NOTE: If a Union typehint converter fails it will raise `commands.BadUnionArgument`
# instead of `commands.BadArgument`.
# To check the resulting type, `isinstance` is used
if isinstance(target, discord.Member):
await ctx.send(f'Member found: {target.mention}, adding them to the ignore list.')
elif isinstance(target, discord.TextChannel): # this could be an `else` but for completeness' sake.
await ctx.send(f'Channel found: {target.mention}, adding it to the ignore list.')
# Built-in type converters.
@bot.command()
async def multiply(ctx: commands.Context, number: int, maybe: bool):
# We want an `int` and a `bool` parameter here.
# `bool` is a slightly special case, as shown here:
# See: https://discordpy.readthedocs.io/en/latest/ext/commands/commands.html#bool
if maybe is True:
return await ctx.send(str(number * 2))
await ctx.send(str(number * 5))
bot.run('token')
|