Terrance / SkPy

An unofficial Python library for interacting with the Skype HTTP API.
https://skpy.t.allofti.me
BSD 3-Clause "New" or "Revised" License
268 stars 66 forks source link

SkypeAuthException being returned on non Auth related exceptions #267

Closed manecosta closed 2 months ago

manecosta commented 3 months ago

Before we start...

Summary

https://github.com/Terrance/SkPy/blob/4adfb808fac0728fb97ad3f2fae6b535da069aac/skpy/conn.py#L248

From what I see, the __call__() function where this exception is thrown is used for all requests including (for the purpose of this bug report) for sending messages.

Recently it seems like Skype might've tighten the rate limits on sending messages and for my specific implementation this rate limit is being hit often now. This made the bug obvious. When that rate limit hits, it returns a 429 and it is handled in this place. This means that a rate limit when sending messages is surfaced as a SkypeAuthException, which is misleading.

Here's what I'm seeing when I print the resp.text in this part of the code when a rate limit for sending messages it hit:

{"errorCode":800,"message":"Command Rate limit exceeded Throttling command"}

resp.status_code is 429.

A possible solution to this is to change the raised exception to be something like SkypeRateLimitException. If there's any good reason for it to be SkypeAuthException on login, we could have a check for the url and return something different if it is login or not.

Code sample

N/A

Code output

N/A

Explain your code

N/A

SkPy version

Latest

Python version

N/A

Anything else?

No response

Terrance commented 3 months ago

for my specific implementation this rate limit is being hit often now

You didn't include any code, so it'd be useful to know which requests are triggering rate limits -- long-poll for events, sending messages, looking up users/chats?

That said, rate-limiting is probably common enough that it's worth having its own exception, and indeed it can just be raised there instead of SkypeAuthException.

manecosta commented 3 months ago

I didn't include any code because it's not a code specific bug, since it will affect any request that is rate limited, as far as I understand the code. I did say it was for sending messages by the way but it's easy to go unnoticed on a long post.

I'm on mobile so it's hard to provide a code snippet right now but it's literally just sending messages on a loop as quickly as possible (can be to the same chat), and it will throw that error after a bit over 100 messages sent.

Another piece of information that might be useful for other people who might end up in this thread is that from my experimenting, I believe the rate limits for sending messages follow a Token Bucket strategy. The size of the bucket is 100 and it replenishes a token every 4-5 seconds (the exact number is hard to be sure). I checked the response headers and it doesn't seem like it specifies anything, so experimentation was the only way to figure out the values.

This seems to have been introduced the past week or so. I wasn't hitting any rate limits before so maybe there were none or they were more relaxed. I also don't know if these limits are account/IP specific or for everyone, since there's a chance I'm being targeted for heavy use.

johe123qwe commented 3 months ago

I have this problem too, it has appeared in the last two weeks. Not sure if it is specific to the IP.

Terrance commented 3 months ago

@johe123qwe The problem reported here is that rate limiting is specifically reported as an auth error when it happens elsewhere.

SkPy does not handle or circumvent any rate-limiting imposed by Skype -- if Skype prevents starts preventing API calls then there is nothing SkPy can do about it. You'll most likely need to reduce your messaging to stay below Skype's new limits.

Terrance commented 3 months ago

I checked the response headers and it doesn't seem like it specifies anything

@manecosta I'd still like to see a full request/response with debug output, just in case there is anything of interest in there.

(That's really what I was looking for earlier -- having not sent any bulk requests through my account, I've yet to see any Skype rate-limiting first hand, and I don't particularly want to push my own account to trigger it.)

manecosta commented 3 months ago

Here's a simple snippet to trigger the rate limit:

from skpy import Skype

target = '<chat_id>'
client = Skype('<username>', '<password>')

try:
    for i in range(500):
        client.chats[target].sendMsg(f'Test {i}')
except Exception as e:
    print('Exception', e)

If I run it like this, what I get printed eventually is:

Exception ('Auth rate limit exceeded', <Response [429]>)

If I go into SkPy, into __call__ in conn.py and print the response before the SkypeAuthException is raised:

print(resp):

<Response [429]>

print(resp.status_code):

429

print(resp.json()):

{'errorCode': 800, 'message': 'Command Rate limit exceeded Throttling command'}

print(resp.headers):

{'Cache-Control': 'no-store, must-revalidate, no-cache', 'Pragma': 'no-cache', 'Content-Length': '76', 'Content-Type': 'application/json; charset=utf-8', 'StatusText': 'Command Rate limit exceeded Throttling command', 'ContextId': 'tcid=<redacted>,server=<redacted>', 'Date': 'Mon, 19 Aug 2024 11:09:09 GMT'}

It doesn't seem like there's any specifics about the rate limiting that could be bubbled up, so a generic SkypeRateLimitException is likely the best that can be done.