-
-
Notifications
You must be signed in to change notification settings - Fork 964
Request limiter - prevent hitting Telegram's API limits #397
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
69f44cd
fe988b7
ac29f07
0dee13b
fc49483
d7bf693
b2e1c34
899b79b
331e149
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,6 +46,13 @@ class Request | |
*/ | ||
private static $input; | ||
|
||
/** | ||
* Request limiter | ||
* | ||
* @var boolean | ||
*/ | ||
private static $limiter_enabled; | ||
|
||
/** | ||
* Available actions to send | ||
* | ||
|
@@ -318,6 +325,8 @@ public static function send($action, array $data = []) | |
|
||
self::ensureNonEmptyData($data); | ||
|
||
self::limitTelegramRequests($action, $data); | ||
|
||
$response = json_decode(self::execute($action, $data), true); | ||
|
||
if (null === $response) { | ||
|
@@ -976,4 +985,79 @@ public static function getWebhookInfo() | |
// Must send some arbitrary data for this to work for now... | ||
return self::send('getWebhookInfo', ['info']); | ||
} | ||
|
||
/** | ||
* Enable request limiter | ||
* | ||
* @param boolean $value | ||
*/ | ||
public static function setLimiter($value = true) | ||
{ | ||
if (DB::isDbConnected()) { | ||
self::$limiter_enabled = $value; | ||
} | ||
} | ||
|
||
/** | ||
* This functions delays API requests to prevent reaching Telegram API limits | ||
* Can be disabled while in execution by 'Request::setLimiter(false)' | ||
* | ||
* @link https://core.telegram.org/bots/faq#my-bot-is-hitting-limits-how-do-i-avoid-this | ||
* | ||
* @param string $action | ||
* @param array $data | ||
* | ||
* @throws \Longman\TelegramBot\Exception\TelegramException | ||
*/ | ||
private static function limitTelegramRequests($action, array $data = []) | ||
{ | ||
if (self::$limiter_enabled) { | ||
$limited_methods = [ | ||
'sendMessage', | ||
'forwardMessage', | ||
'sendPhoto', | ||
'sendAudio', | ||
'sendDocument', | ||
'sendSticker', | ||
'sendVideo', | ||
'sendVoice', | ||
'sendLocation', | ||
'sendVenue', | ||
'sendContact', | ||
'editMessageText', | ||
'editMessageCaption', | ||
'editMessageReplyMarkup', | ||
]; | ||
|
||
$chat_id = isset($data['chat_id']) ? $data['chat_id'] : null; | ||
$inline_message_id = isset($data['inline_message_id']) ? $data['inline_message_id'] : null; | ||
|
||
if (($chat_id || $inline_message_id) && in_array($action, $limited_methods)) { | ||
$timeout = 60; | ||
|
||
while (true) { | ||
if ($timeout <= 0) { | ||
throw new TelegramException('Timed out while waiting for a request spot!'); | ||
} | ||
|
||
$requests = DB::getTelegramRequestCount($chat_id, $inline_message_id); | ||
|
||
if ( | ||
$requests['LIMIT_PER_SEC'] == 0 && // No more than one message per second inside a particular chat | ||
( | ||
(($chat_id > 0 || $inline_message_id) && $requests['LIMIT_PER_SEC_ALL'] < 30) || // No more than 30 messages per second globally | ||
($chat_id < 0 && $requests['LIMIT_PER_MINUTE'] < 20) // No more than 20 messages per minute for group chats | ||
) | ||
) { | ||
break; | ||
} | ||
|
||
$timeout--; | ||
sleep(1); | ||
} | ||
|
||
DB::insertTelegramRequest($action, $data); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I noticed that entries once added, never get deleted from the DB once handled. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But if I remove them next requests will think they are ok to go. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, I'm just thinking long term here. As this is a temporary table, it just gets bigger and bigger, in some cases pretty quick, right? Maybe something like: if (mt_rand(0, 999) === 42) {
do_cleanup();
} Or maybe I'm going too far here and this isn't really a problem... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess, in my case I'm cleaning the table every 5 minutes, set as a task in Mysql. I have no 'good' idea how to implement cleanup inside this code without adding potential delays or performance losses. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, then let's leave this as is for the moment and passively think of something 😉 |
||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could also do
$timeout--
here instead of$timeout = $timeout - 1;
further down.