Skip to content

Conversation

@VincentRPS
Copy link

No description provided.

@Paillat-dev
Copy link
Member

Traceback
Traceback (most recent call last):
  File "/home/paillat/Documents/pycord-test/thing.py", line 20, in <module>
    bot.run(TOKEN)
  File "/home/paillat/Documents/pycord-test/discord/client.py", line 813, in run
    return future.result()
           ^^^^^^^^^^^^^^^
  File "/home/paillat/Documents/pycord-test/discord/client.py", line 792, in runner
    await self.start(*args, **kwargs)
  File "/home/paillat/Documents/pycord-test/discord/client.py", line 755, in start
    await self.login(token)
  File "/home/paillat/Documents/pycord-test/discord/client.py", line 609, in login
    data = await self.http.static_login(token.strip())
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/paillat/Documents/pycord-test/discord/http.py", line 445, in static_login
    data = await self.request(Route("GET", "/users/@me"))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/paillat/Documents/pycord-test/discord/http.py", line 313, in request
    async with self.__session.request(
               ^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/paillat/Documents/pycord-test/.venv/lib/python3.12/site-packages/aiohttp/client.py", line 1425, in __aenter__
    self._resp: _RetType = await self._coro
                           ^^^^^^^^^^^^^^^^
  File "/home/paillat/Documents/pycord-test/.venv/lib/python3.12/site-packages/aiohttp/client.py", line 622, in _request
    raise err_exc_cls(url)
aiohttp.client_exceptions.InvalidUrlClientError: /users/@me

@plun1331 plun1331 requested a review from Copilot May 10, 2025 07:27
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR refactors the webhook adapters and HTTP client to port over the v3 rate limit code. Key changes include:

  • Adding a configurable discord_api_url parameter and storing it in both synchronous and asynchronous webhook adapters.
  • Refactoring the Route class initializer and merging logic to explicitly accept key parameters and construct URLs.
  • Replacing per-bucket lock usage with an executor-based rate limit handling mechanism in the HTTP client.

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
discord/webhook/sync.py Updated WebhookAdapter init and request to use the new discord_api_url via merge.
discord/webhook/async_.py Updated AsyncWebhookAdapter init and request to use the new discord_api_url via merge.
discord/http.py Refactored Route initialization and added the Executor for rate limit handling.

Signed-off-by: plun1331 <plun1331@gmail.com>
@plun1331
Copy link
Member

@Paillat-dev if you've made sure this works you can merge
ideally test ratelimiting too (make a bunch of channels really fast or something)

@VincentRPS
Copy link
Author

@Paillat-dev if you've made sure this works you can merge

ideally test ratelimiting too (make a bunch of channels really fast or something)

Personally, I've tested that it works with the requests bots make at startup and when responding to interactions/webhooks. I haven't tested it actually getting into a rate limit, so ideally try getting into both a bucket (single route) rate limit and global rate limit if possible.

@Paillat-dev
Copy link
Member

Paillat-dev commented May 10, 2025

WARNING:discord.client:PyNaCl is not installed, voice will NOT be supported
INFO:discord.client:logging in using static token
INFO:discord.gateway:Shard ID None has sent the IDENTIFY payload.
INFO:discord.gateway:Shard ID None has connected to Gateway: ["gateway-prd-us-east1-d-lrqk",{"micros":98166,"calls":["id_created",{"micros":444,"calls":[]},"session_lookup_time",{"micros":329,"calls":[]},"session_lookup_finished",{"micros":17,"calls":[]},"discord-sessions-prd-2-78",{"micros":97083,"calls":["start_session",{"micros":48752,"calls":["discord-api-rpc-57c84bd4f5-5qzx6",{"micros":40234,"calls":["get_user",{"micros":5598},"get_guilds",{"micros":3101},"send_scheduled_deletion_message",{"micros":9},"guild_join_requests",{"micros":920},"authorized_ip_coro",{"micros":7},"pending_payments",{"micros":1357},"apex_user_experiments",{"micros":3},"user_activities",{"micros":2}]}]},"starting_guild_connect",{"micros":33,"calls":[]},"presence_started",{"micros":14666,"calls":[]},"guilds_started",{"micros":59,"calls":[]},"lobbies_started",{"micros":2,"calls":[]},"guilds_connect",{"micros":1,"calls":[]},"presence_connect",{"micros":33553,"calls":[]},"connect_finished",{"micros":33558,"calls":[]},"build_ready",{"micros":11,"calls":[]},"clean_ready",{"micros":0,"calls":[]},"optimize_ready",{"micros":1,"calls":[]},"split_ready",{"micros":0,"calls":[]}]}]}] (Session ID: f93aab6bfef36c47a794312d629f740f).
Ignoring exception in on_connect
Traceback (most recent call last):
  File "/home/paillat/Documents/pycord-test/discord/client.py", line 443, in _run_event
    await coro(*args, **kwargs)
  File "/home/paillat/Documents/pycord-test/discord/bot.py", line 1226, in on_connect
    await self.sync_commands()
  File "/home/paillat/Documents/pycord-test/discord/bot.py", line 754, in sync_commands
    registered_commands = await self.register_commands(
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/paillat/Documents/pycord-test/discord/bot.py", line 618, in register_commands
    registered = await register("bulk", data, _log=False)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/paillat/Documents/pycord-test/discord/http.py", line 335, in request
    reset_after=data['retry_after'],
                ~~~~^^^^^^^^^^^^^^^
TypeError: list indices must be integers or slices, not str

Same error when creating text channels

Traceback (most recent call last):
  File "/home/paillat/Documents/pycord-test/discord/commands/core.py", line 138, in wrapped
    ret = await coro(arg)
          ^^^^^^^^^^^^^^^
  File "/home/paillat/Documents/pycord-test/discord/commands/core.py", line 1082, in _invoke
    await self.callback(ctx, **kwargs)
  File "/home/paillat/Documents/pycord-test/thing.py", line 35, in test_ratelimit
    channel = await ctx.guild.create_text_channel(f"test-{i}")
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/paillat/Documents/pycord-test/discord/guild.py", line 1223, in create_text_channel
    data = await self._create_channel(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/paillat/Documents/pycord-test/discord/http.py", line 335, in request
    reset_after=data['retry_after'],
                ~~~~^^^^^^^^^^^^^^^
KeyError: 'retry_after'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/paillat/Documents/pycord-test/discord/bot.py", line 1149, in invoke_application_command
    await ctx.command.invoke(ctx)
  File "/home/paillat/Documents/pycord-test/discord/commands/core.py", line 435, in invoke
    await injected(ctx)
  File "/home/paillat/Documents/pycord-test/discord/commands/core.py", line 146, in wrapped
    raise ApplicationCommandInvokeError(exc) from exc
discord.errors.ApplicationCommandInvokeError: Application Command raised an exception: KeyError: 'retry_after'

@Paillat-dev
Copy link
Member

{'type': 0, 'content': 'test-msg-4', 'mentions': [], 'mention_roles': [], 'attachments': [], 'embeds': [], 'timestamp': '2025-05-10T11:13:20.523000+00:00', 'edited_timestamp': None, 'flags': 0, 'components': [], 'id': '1370720359774425159', 'channel_id': '1349514823192412293', 'author': {'id': '1290770170922664006', 'username': 'Death Star', 'avatar': None, 'discriminator': '2219', 'public_flags': 0, 'flags': 0, 'bot': True, 'banner': None, 'accent_color': None, 'global_name': None, 'avatar_decoration_data': None, 'collectibles': None, 'banner_color': None, 'clan': None, 'primary_guild': None}, 'pinned': False, 'mention_everyone': False, 'tts': False}

this is the kind of data i get that is missing retry_after

@Paillat-dev
Copy link
Member

Btw I successfully got 24h ratelimited for channel creation:

WARNING:discord.http:We are being rate limited. Retrying in 83479.41 seconds. Handled under the bucket "https://discord.com/api/v10/guilds/1021872219888033903/channels"

Copy link
Member

@Paillat-dev Paillat-dev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've got the following once when starting, but I'm not sure whether it's related to this pr

ERROR:discord.client:Attempting a reconnect in 1.31s
Traceback (most recent call last):
  File "/home/paillat/Documents/pycord-test/discord/client.py", line 645, in connect
    self.ws = await asyncio.wait_for(coro, timeout=60.0)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/paillat/.local/share/uv/python/cpython-3.12.9-linux-x86_64-gnu/lib/python3.12/asyncio/tasks.py", line 520, in wait_for
    return await fut
           ^^^^^^^^^
  File "/home/paillat/Documents/pycord-test/discord/gateway.py", line 339, in from_client
    socket = await client.http.ws_connect(gateway)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/paillat/Documents/pycord-test/discord/http.py", line 249, in ws_connect
    return await self.__session.ws_connect(url, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/paillat/Documents/pycord-test/.venv/lib/python3.12/site-packages/aiohttp/client.py", line 1021, in _ws_connect
    raise WSServerHandshakeError(
aiohttp.client_exceptions.WSServerHandshakeError: 520, message='Invalid response status', url='wss://gateway.discord.gg/?encoding=json&v=10&compress=zlib-stream'

self._executors: list[Executor] = []

user_agent = (
"DiscordBot (https://pycord.dev, {0}) Python/{1[0]}.{1[1]} aiohttp/{2}"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be cool if we could make it so a user can change this in some way or another

@VincentRPS
Copy link
Author

I've got the following once when starting, but I'm not sure whether it's related to this pr

ERROR:discord.client:Attempting a reconnect in 1.31s
Traceback (most recent call last):
  File "/home/paillat/Documents/pycord-test/discord/client.py", line 645, in connect
    self.ws = await asyncio.wait_for(coro, timeout=60.0)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/paillat/.local/share/uv/python/cpython-3.12.9-linux-x86_64-gnu/lib/python3.12/asyncio/tasks.py", line 520, in wait_for
    return await fut
           ^^^^^^^^^
  File "/home/paillat/Documents/pycord-test/discord/gateway.py", line 339, in from_client
    socket = await client.http.ws_connect(gateway)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/paillat/Documents/pycord-test/discord/http.py", line 249, in ws_connect
    return await self.__session.ws_connect(url, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/paillat/Documents/pycord-test/.venv/lib/python3.12/site-packages/aiohttp/client.py", line 1021, in _ws_connect
    raise WSServerHandshakeError(
aiohttp.client_exceptions.WSServerHandshakeError: 520, message='Invalid response status', url='wss://gateway.discord.gg/?encoding=json&v=10&compress=zlib-stream'

doesn't seem like it should be, @plun1331 what do you think? Seems like some Gateway error

Copy link
Member

@Paillat-dev Paillat-dev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to work for me, I''m just not getting warnings when ratelimited, only debug log messages, idk if that's expected or not.

@Paillat-dev Paillat-dev requested a review from Copilot May 13, 2025 20:10
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR refactors the rate limit handling code to port over the v3 rate limit code and update the webhook adapters’ API URL usage.

  • Updated the synchronous and asynchronous webhook adapters to accept a customizable API URL and use a new merge method on Route.
  • Refactored the Route class and introduced the Executor class in HTTP handling to manage rate limits more dynamically.

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
discord/webhook/sync.py Added discord_api_url parameter and switched to merge for URL.
discord/webhook/async_.py Similar changes as sync.py for asynchronous webhook adapter.
discord/http.py Revised Route initialization, introduced Executor for rate limits, and updated HTTPClient.request logic.
Comments suppressed due to low confidence (1)

discord/http.py:259

  • [nitpick] The variable 'bucket' now holds a merged URL rather than a rate limit bucket identifier. Renaming it to 'merged_url' (or similar) might better reflect its purpose and avoid confusion.
bucket = route.merge(self.api_url)

Comment on lines +141 to +143
or route.guild_id == self.guild_id
or route.webhook_id == self.webhook_id
or route.webhook_token == self.webhook_token
Copy link

Copilot AI May 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The equality implementation in Route uses a logical OR to compare individual attributes, which may incorrectly consider two distinct routes as equal if any one attribute matches. Consider using a logical AND for a stricter comparison if full equality is desired.

Suggested change
or route.guild_id == self.guild_id
or route.webhook_id == self.webhook_id
or route.webhook_token == self.webhook_token
and route.guild_id == self.guild_id
and route.webhook_id == self.webhook_id
and route.webhook_token == self.webhook_token

Copilot uses AI. Check for mistakes.
Copy link
Member

@Paillat-dev Paillat-dev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, maybe the per route rate limits should also take into account the http method (creating channels is being rate limited but deleting not, however deleting channels is also blocked).

@Paillat-dev Paillat-dev self-requested a review May 13, 2025 21:09
@VincentRPS VincentRPS merged commit 32d932d into master May 13, 2025
@VincentRPS VincentRPS deleted the cool-api branch May 13, 2025 21:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants