Fix type errors in all examples

This commit is contained in:
Rapptz 2025-08-16 05:21:19 -04:00
parent 7e2ca02fd1
commit 4f539b710f
26 changed files with 124 additions and 16 deletions

View File

@ -68,7 +68,7 @@ if TYPE_CHECKING:
SelectCallbackDecorator = Callable[[ItemCallbackType['S', BaseSelectT]], BaseSelectT]
S = TypeVar('S', bound='ActionRow', covariant=True)
S = TypeVar('S', bound=Union['ActionRow', 'LayoutView'], covariant=True)
V = TypeVar('V', bound='LayoutView', covariant=True)
__all__ = ('ActionRow',)

View File

@ -5,7 +5,6 @@
import asyncio
import logging
import logging.handlers
import os
from typing import List, Optional

View File

@ -8,6 +8,9 @@ MY_GUILD = discord.Object(id=0) # replace with your guild id
class MyClient(discord.Client):
# Suppress error on the User attribute being None since it fills up later
user: discord.ClientUser
def __init__(self, *, intents: discord.Intents):
super().__init__(intents=intents)
# A CommandTree is a special type that holds all the application command
@ -72,10 +75,17 @@ async def send(interaction: discord.Interaction, text_to_send: str):
async def joined(interaction: discord.Interaction, member: Optional[discord.Member] = None):
"""Says when a member joined."""
# If no member is explicitly provided then we use the command user here
member = member or interaction.user
user = member or interaction.user
# Tell the type checker that this is a Member
assert isinstance(user, discord.Member)
# The format_dt function formats the date time into a human readable representation in the official client
await interaction.response.send_message(f'{member} joined {discord.utils.format_dt(member.joined_at)}')
# Joined at can be None in very bizarre cases so just handle that as well
if user.joined_at is None:
await interaction.response.send_message(f'{user} has no join date.')
else:
await interaction.response.send_message(f'{user} joined {discord.utils.format_dt(user.joined_at)}')
# A Context Menu command is an app command that can be run on a member or on a message by
@ -86,6 +96,11 @@ async def joined(interaction: discord.Interaction, member: Optional[discord.Memb
@client.tree.context_menu(name='Show Join Date')
async def show_join_date(interaction: discord.Interaction, member: discord.Member):
# The format_dt function formats the date time into a human readable representation in the official client
# Joined at can be None in very bizarre cases so just handle that as well
if member.joined_at is None:
await interaction.response.send_message(f'{member} has no join date.')
else:
await interaction.response.send_message(f'{member} joined at {discord.utils.format_dt(member.joined_at)}')
@ -97,9 +112,18 @@ async def report_message(interaction: discord.Interaction, message: discord.Mess
f'Thanks for reporting this message by {message.author.mention} to our moderators.', ephemeral=True
)
# Make sure that we're inside a guild
if interaction.guild is None:
await interaction.response.send_message('This command can only be used in a server.', ephemeral=True)
return
# Handle report by sending it into a log channel
log_channel = interaction.guild.get_channel(0) # replace with your channel id
if log_channel is None or not isinstance(log_channel, discord.abc.Messageable):
await interaction.response.send_message('Log channel not found or not messageable.', ephemeral=True)
return
embed = discord.Embed(title='Reported Message')
if message.content:
embed.description = message.content

View File

@ -12,6 +12,9 @@ MY_GUILD = discord.Object(id=0) # replace with your guild id
class MyClient(discord.Client):
# Suppress error on the User attribute being None since it fills up later
user: discord.ClientUser
def __init__(self):
super().__init__(intents=discord.Intents.default())
self.tree = app_commands.CommandTree(self)

View File

@ -4,6 +4,9 @@ import discord
class MyClient(discord.Client):
# Suppress error on the User attribute being None since it fills up later
user: discord.ClientUser
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@ -21,8 +24,11 @@ class MyClient(discord.Client):
@tasks.loop(seconds=60) # task runs every 60 seconds
async def my_background_task(self):
channel = self.get_channel(1234567) # channel ID goes here
# Tell the type checker that this is a messageable channel
assert isinstance(channel, discord.abc.Messageable)
self.counter += 1
await channel.send(self.counter)
await channel.send(str(self.counter))
@my_background_task.before_loop
async def before_my_task(self):

View File

@ -3,6 +3,9 @@ import asyncio
class MyClient(discord.Client):
# Suppress error on the User attribute being None since it fills up later
user: discord.ClientUser
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@ -18,9 +21,13 @@ class MyClient(discord.Client):
await self.wait_until_ready()
counter = 0
channel = self.get_channel(1234567) # channel ID goes here
# Tell the type checker that this is a messageable channel
assert isinstance(channel, discord.abc.Messageable)
while not self.is_closed():
counter += 1
await channel.send(counter)
await channel.send(str(counter))
await asyncio.sleep(60) # task runs every 60 seconds

View File

@ -18,6 +18,9 @@ bot = commands.Bot(command_prefix='?', description=description, intents=intents)
@bot.event
async def on_ready():
# Tell the type checker that User is filled up at this point
assert bot.user is not None
print(f'Logged in as {bot.user} (ID: {bot.user.id})')
print('------')
@ -57,7 +60,11 @@ async def repeat(ctx, times: int, content='repeating...'):
@bot.command()
async def joined(ctx, member: discord.Member):
"""Says when a member joined."""
await ctx.send(f'{member.name} joined {discord.utils.format_dt(member.joined_at)}')
# Joined at can be None in very bizarre cases so just handle that as well
if member.joined_at is None:
await ctx.send(f'{member} has no join date.')
else:
await ctx.send(f'{member} joined {discord.utils.format_dt(member.joined_at)}')
@bot.group()

View File

@ -25,10 +25,6 @@ ytdl_format_options = {
'source_address': '0.0.0.0', # bind to ipv4 since ipv6 addresses cause issues sometimes
}
ffmpeg_options = {
'options': '-vn',
}
ytdl = youtube_dl.YoutubeDL(ytdl_format_options)
@ -51,7 +47,7 @@ class YTDLSource(discord.PCMVolumeTransformer):
data = data['entries'][0]
filename = data['url'] if stream else ytdl.prepare_filename(data)
return cls(discord.FFmpegPCMAudio(filename, **ffmpeg_options), data=data)
return cls(discord.FFmpegPCMAudio(filename, options='-vn'), data=data)
class Music(commands.Cog):
@ -138,6 +134,9 @@ bot = commands.Bot(
@bot.event
async def on_ready():
# Tell the type checker that User is filled up at this point
assert bot.user is not None
print(f'Logged in as {bot.user} (ID: {bot.user.id})')
print('------')

View File

@ -83,8 +83,14 @@ class ChannelOrMemberConverter(commands.Converter):
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: ChannelOrMemberConverter):
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
@ -118,8 +124,8 @@ async def multiply(ctx: commands.Context, number: int, maybe: bool):
# See: https://discordpy.readthedocs.io/en/latest/ext/commands/commands.html#bool
if maybe is True:
return await ctx.send(number * 2)
await ctx.send(number * 5)
return await ctx.send(str(number * 2))
await ctx.send(str(number * 5))
bot.run('token')

View File

@ -4,6 +4,9 @@ import discord
class MyClient(discord.Client):
# Suppress error on the User attribute being None since it fills up later
user: discord.ClientUser
async def on_ready(self):
print(f'Logged in as {self.user} (ID: {self.user.id})')
print('------')

View File

@ -5,6 +5,9 @@ import asyncio
class MyClient(discord.Client):
# Suppress error on the User attribute being None since it fills up later
user: discord.ClientUser
async def on_ready(self):
print(f'Logged in as {self.user} (ID: {self.user.id})')
print('------')

View File

@ -6,6 +6,9 @@ import asyncio
class MyClient(discord.Client):
# Suppress error on the User attribute being None since it fills up later
user: discord.ClientUser
async def on_ready(self):
print(f'Logged in as {self.user} (ID: {self.user.id})')
print('------')

View File

@ -9,6 +9,9 @@ TEST_GUILD = discord.Object(0)
class MyClient(discord.Client):
# Suppress error on the User attribute being None since it fills up later
user: discord.ClientUser
def __init__(self) -> None:
# Just default intents and a `discord.Client` instance
# We don't need a `commands.Bot` instance because we are not

View File

@ -4,6 +4,9 @@ import discord
class MyClient(discord.Client):
# Suppress error on the User attribute being None since it fills up later
user: discord.ClientUser
async def on_ready(self):
print(f'Logged in as {self.user} (ID: {self.user.id})')
print('------')

View File

@ -4,6 +4,9 @@ import discord
class MyClient(discord.Client):
# Suppress error on the User attribute being None since it fills up later
user: discord.ClientUser
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@ -36,6 +39,9 @@ class MyClient(discord.Client):
# Make sure the role still exists and is valid.
return
# Tell the type checker that RawReactionActionEvent.member is not none during REACTION_ADD
assert payload.member is not None
try:
# Finally, add the role.
await payload.member.add_roles(role)

View File

@ -4,6 +4,9 @@ import discord
class MyClient(discord.Client):
# Suppress error on the User attribute being None since it fills up later
user: discord.ClientUser
async def on_ready(self):
print(f'Logged in as {self.user} (ID: {self.user.id})')
print('------')

View File

@ -52,6 +52,9 @@ async def text(ctx: commands.Context, name: str, *objects: typing.Union[discord.
overwrites = create_overwrites(ctx, *objects)
# Tell the type checker that our Guild is not None
assert ctx.guild is not None
await ctx.guild.create_text_channel(
name,
overwrites=overwrites,
@ -69,6 +72,8 @@ async def voice(ctx: commands.Context, name: str, *objects: typing.Union[discord
overwrites = create_overwrites(ctx, *objects)
assert ctx.guild is not None
await ctx.guild.create_voice_channel(
name,
overwrites=overwrites,
@ -86,6 +91,8 @@ async def emoji(ctx: commands.Context, emoji: discord.PartialEmoji, *roles: disc
# fetch the emoji asset and read it as bytes.
emoji_bytes = await emoji.read()
assert ctx.guild is not None
# the key parameter here is `roles`, which controls
# what roles are able to use the emoji.
await ctx.guild.create_custom_emoji(

View File

@ -6,6 +6,8 @@ import discord
class Bot(commands.Bot):
user: discord.ClientUser
def __init__(self):
intents = discord.Intents.default()
intents.message_content = True

View File

@ -6,6 +6,9 @@ import discord
class CounterBot(commands.Bot):
# Suppress error on the User attribute being None since it fills up later
user: discord.ClientUser
def __init__(self):
intents = discord.Intents.default()
intents.message_content = True

View File

@ -38,6 +38,9 @@ class DropdownView(discord.ui.View):
class Bot(commands.Bot):
# Suppress error on the User attribute being None since it fills up later
user: discord.ClientUser
def __init__(self):
intents = discord.Intents.default()
intents.message_content = True

View File

@ -70,6 +70,9 @@ class DynamicCounter(
class DynamicCounterBot(commands.Bot):
# Suppress error on the User attribute being None since it fills up later
user: discord.ClientUser
def __init__(self):
intents = discord.Intents.default()
super().__init__(command_prefix=commands.when_mentioned, intents=intents)

View File

@ -6,6 +6,9 @@ import discord
class EphemeralCounterBot(commands.Bot):
# Suppress error on the User attribute being None since it fills up later
user: discord.ClientUser
def __init__(self):
intents = discord.Intents.default()
intents.message_content = True

View File

@ -6,6 +6,9 @@ import discord
class Bot(commands.Bot):
# Suppress error on the User attribute being None since it fills up later
user: discord.ClientUser
def __init__(self):
intents = discord.Intents.default()
intents.message_content = True

View File

@ -7,6 +7,9 @@ from urllib.parse import quote_plus
class GoogleBot(commands.Bot):
# Suppress error on the User attribute being None since it fills up later
user: discord.ClientUser
def __init__(self):
intents = discord.Intents.default()
intents.message_content = True

View File

@ -64,6 +64,9 @@ class DynamicButton(discord.ui.DynamicItem[discord.ui.Button], template=r'button
class PersistentViewBot(commands.Bot):
# Suppress error on the User attribute being None since it fills up later
user: discord.ClientUser
def __init__(self):
intents = discord.Intents.default()
intents.message_content = True

View File

@ -121,6 +121,9 @@ class TicTacToe(discord.ui.View):
class TicTacToeBot(commands.Bot):
# Suppress error on the User attribute being None since it fills up later
user: discord.ClientUser
def __init__(self):
intents = discord.Intents.default()
intents.message_content = True