http-proxy icon indicating copy to clipboard operation
http-proxy copied to clipboard

Use with discord.py bot

Open lexicalunit opened this issue 2 years ago • 9 comments

I'm on a M1 mac so I wasn't able to get it to work via docker. But I cloned this repo then:

cargo build --release
DISCORD_TOKEN="my token" PORT=3000 ./target/release/twilight-http-proxy
2022-06-22T15:52:23.122150Z  INFO twilight_http_proxy: Listening on http://0.0.0.0:3000

Note also that the README.md is wrong and the built binary is twilight-http-proxy rather than twilight_http_proxy.

Now I start up my bot using proxy="http://localhost:3000" and I immediately have issues. In the proxy log I see:

2022-06-22T15:53:40.514419Z ERROR twilight_http_proxy: Unsupported HTTP method in request, CONNECT

And in my bot's log we can see that it's trying to GET /users/@me:

[...]
  File "mybot/env/lib/python3.10/site-packages/discord/http.py", line 561, in static_login
    data = await self.request(Route('GET', '/users/@me'))
  File "mybot/env/lib/python3.10/site-packages/discord/http.py", line 444, in request
    async with self.__session.request(method, url, **kwargs) as response:
  File "mybot/env/lib/python3.10/site-packages/aiohttp/client.py", line 1117, in __aenter__
    self._resp = await self._coro
  File "mybot/env/lib/python3.10/site-packages/aiohttp/client.py", line 520, in _request
    conn = await self._connector.connect(
  File "mybot/env/lib/python3.10/site-packages/aiohttp/connector.py", line 535, in connect
    proto = await self._create_connection(req, traces, timeout)
  File "mybot/env/lib/python3.10/site-packages/aiohttp/connector.py", line 890, in _create_connection
    _, proto = await self._create_proxy_connection(req, traces, timeout)
  File "mybot/env/lib/python3.10/site-packages/aiohttp/connector.py", line 1124, in _create_proxy_connection
    raise ClientHttpProxyError(
aiohttp.client_exceptions.ClientHttpProxyError: 501, message='Not Implemented', url=URL('http://localhost:3000')

lexicalunit avatar Jun 22 '22 15:06 lexicalunit

As is stated in the README, the proxy replaces the Discord API instead of serving as a real HTTP CONNECT proxy. You're using it as if it were a HTTP CONNECT proxy, which is unsupported.

Gelbpunkt avatar Jun 22 '22 16:06 Gelbpunkt

Aw, that's too bad. I was hoping that https://fusebit.io/blog/discord-rate-limiting/ knew what it was talking about, but apparently it doesn't. Thanks for the heads up. I might as well ask: Do you know of any real http proxies for Discord bots?

lexicalunit avatar Jun 22 '22 16:06 lexicalunit

I don't know of any proper proxies, but it is probably our own fault for the name. You can get it working with discord.py by monkeypatching discord.http.Route.BASE to being in your example http://localhost:3000 and I think that should work.

Erk- avatar Jun 22 '22 16:06 Erk-

It is practically impossible to use a real HTTP CONNECT proxy for this from what I know about the CONNECT protocol. You see, the client only specifies the host and port to open a connection with, and all the traffic itself is then tunneled. The issue is that if SSL is being used, which it would in this case, the proxy won't be able to tell which exact URLs are being requested. It will know that discord.com port 443 is being connected to, but not that you're requesting /api/users/@me - because that's encrypted.

Gelbpunkt avatar Jun 22 '22 16:06 Gelbpunkt

I don't know of any proper proxies, but it is probably our own fault for the name. You can get it working with discord.py by monkeypatching discord.http.Route.BASE to being in your example http://localhost:3000 and I think that should work.

This is the bare minimum - if you want to hand full control to the proxy, you could use something like this patch as a base to get rid off local rate limit handling and leave everything to the proxy.

Gelbpunkt avatar Jun 22 '22 16:06 Gelbpunkt

Thank you so much, y'all have been super helpful ❤️

🤔 This seems to work ok:

def patch_discord_proxy(PROXY_URL: str):
    """Patch discord.http to use our Discord API proxy."""
    from discord import http

    http.Route.BASE = f"{PROXY_URL}/api/v10"
    http._set_api_version = lambda v: f"{PROXY_URL}/api/v{v}"

@Gelbpunkt I see you did similar, but also ripped out a lot of discord.py's own rate limiting. That makes sense, for sure. But for the most part discord.py's rate limiting works fine for me. It's just the global rate limiting that discord.py is awful about handling. Rather than proactively prevent the bot from hitting the global rate limit, it just reacts to the rate limit being hit by shutting the bot down until the 1 hour CloudFlare ban wears off.

IIUC twilight's http-proxy will actually stop the requests from going out, queue them up, and then resume them later to avoid the 50 requests/s CloudFlare limit. Is that right?

Maybe I can just use my quick and dirty patch + twilight-http-proxy to get that nice proactive intelligent global rate limiting feature, and just leave all the discord.py rate limiting stuff in there (which hopefully won't hurt anything 🤞🏻)?

lexicalunit avatar Jun 22 '22 17:06 lexicalunit

IIUC twilight's http-proxy will actually stop the requests from going out, queue them up, and then resume them later to avoid the 50 requests/s CloudFlare limit. Is that right?

It will hold your connection and send the request to Discord once it won't be ratelimited. Therefore you might run into timeouts if the client is configured with a low timeout.

Maybe I can just use my quick and dirty patch + twilight-http-proxy to get that nice proactive intelligent global rate limiting feature, and just leave all the discord.py rate limiting stuff in there (which hopefully won't hurt anything 🤞🏻)?

You can and it will certainly work. I just don't see a reason to have overhead in Python code, based on limited knowledge, instead of just leaving everything to the proxy, because it does these computations already. That said, what you are doing will work fine, so no need to worry about that.

Gelbpunkt avatar Jun 27 '22 21:06 Gelbpunkt

Hrm, something didn't work. I am currently getting the dreaded HTTPException: 429 Too Many Requests (error code: 0): You are being blocked from accessing our API temporarily due to exceeding our rate limits frequently. Please read our docs at https://discord.com/developers/docs/topics/rate-limits to prevent this moving forward. error in my bot with every request it tries to make. All Discord API requests are being routed through twilight-http-proxy.

Did I misconfigure something?

I see no logs for twilight-http-proxy besides:

INFO twilight_http_proxy: Listening on http://[0.0.0.0](OMIT):3000

lexicalunit avatar Jun 28 '22 03:06 lexicalunit

No it probably is correct. The issue here is that we do not preemptively handle the global rate limit see https://github.com/twilight-rs/twilight/issues/650

Erk- avatar Jun 28 '22 05:06 Erk-

Closing this since it looks like this issue is resolved/related to https://github.com/twilight-rs/twilight/issues/650.

itohatweb avatar Apr 24 '23 07:04 itohatweb