Merge pull request #121 from teloxide/throttle_fix

Fix `Throttle` adaptor
This commit is contained in:
Hirrolot 2021-09-30 04:59:06 +06:00 committed by GitHub
commit 156cff7ce5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 63 additions and 4 deletions

View file

@ -11,9 +11,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `EditedMessageIsTooLong` error [#109][pr109]
- `UntilDate` enum and use it for `{Restricted, Banned}::until_date` ([#116][pr116])
- `Limits::messages_per_min_channel` ([#121][pr121])
[pr109]: https://github.com/teloxide/teloxide-core/pull/109
[pr116]: https://github.com/teloxide/teloxide-core/pull/116
[pr121]: https://github.com/teloxide/teloxide-core/pull/121
### Changed
@ -26,8 +28,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Type of `BanChatMember::until_date`: `u64` -> `chrono::DateTime<Utc>` ([#116][pr116])
- Type of `Poll::correct_option_id`: `i32` -> `u8` ([#119][pr119])
- Type of `Poll::open_period`: `i32` -> `u16` ([#119][pr119])
- `Throttle` adaptor not honouring chat/min limits ([#121][pr121])
[pr119]: https://github.com/teloxide/teloxide-core/pull/119
## 0.3.3 - 2021-08-03
### Fixed

View file

@ -203,11 +203,15 @@ pub struct Limits {
/// Allowed messages in one chat per minute.
pub messages_per_min_chat: u32,
/// Allowed messages in one channel per minute.
pub messages_per_min_channel: u32,
/// Allowed messages per second.
pub messages_per_sec_overall: u32,
}
/// Defaults are taken from [telegram documentation][tgdoc].
/// Defaults are taken from [telegram documentation][tgdoc] (except for
/// `messages_per_min_channel`).
///
/// [tgdoc]: https://core.telegram.org/bots/faq#my-bot-is-hitting-limits-how-do-i-avoid-this
impl Default for Limits {
@ -216,6 +220,7 @@ impl Default for Limits {
messages_per_sec_chat: 1,
messages_per_sec_overall: 30,
messages_per_min_chat: 20,
messages_per_min_channel: 10,
}
}
}
@ -465,9 +470,17 @@ async fn worker(
while let Some(entry) = queue_removing.next() {
let chat = &entry.value().0;
let requests_sent_count = requests_sent.per_sec.get(chat).copied().unwrap_or(0);
let limits_not_exceeded = requests_sent_count < limits.messages_per_sec_chat
&& requests_sent_count < limits.messages_per_min_chat;
let requests_sent_per_sec_count = requests_sent.per_sec.get(chat).copied().unwrap_or(0);
let requests_sent_per_min_count = requests_sent.per_min.get(chat).copied().unwrap_or(0);
let messages_per_min_limit = if chat.is_channel() {
limits.messages_per_min_channel
} else {
limits.messages_per_min_chat
};
let limits_not_exceeded = requests_sent_per_sec_count < limits.messages_per_sec_chat
&& requests_sent_per_min_count < messages_per_min_limit;
if limits_not_exceeded {
*requests_sent.per_sec.entry(*chat).or_insert(0) += 1;
@ -627,6 +640,15 @@ enum ChatIdHash {
ChannelUsernameHash(u64),
}
impl ChatIdHash {
fn is_channel(&self) -> bool {
match self {
&Self::Id(id) => ChatId::Id(id).is_channel(),
Self::ChannelUsernameHash(_) => true,
}
}
}
impl From<&ChatId> for ChatIdHash {
fn from(value: &ChatId) -> Self {
match value {

View file

@ -15,6 +15,39 @@ pub enum ChatId {
ChannelUsername(String),
}
impl ChatId {
pub(crate) fn is_channel(&self) -> bool {
matches!(self.unmark(), None | Some(UnmarkedChatId::Channel(_)))
}
pub(crate) fn unmark(&self) -> Option<UnmarkedChatId> {
use UnmarkedChatId::*;
const MAX_CHANNEL_ID: i64 = -(10i64.pow(12));
const MIN_CHANNEL_ID: i64 = MAX_CHANNEL_ID - (i32::MAX as i64);
const MAX_USER_ID: i64 = i32::MAX as _;
const MIN_CHAT_ID: i64 = -MAX_USER_ID;
let res = match self {
&Self::Id(id @ MIN_CHAT_ID..=-1) => Chat(-id as _),
&Self::Id(id @ MIN_CHANNEL_ID..=MAX_CHANNEL_ID) => Channel((MAX_CHANNEL_ID - id) as _),
&Self::Id(id) => {
debug_assert!(0 < id && id < MAX_USER_ID, "malformed chat id");
User(id as _)
}
Self::ChannelUsername(_) => return None,
};
Some(res)
}
}
pub(crate) enum UnmarkedChatId {
User(u32),
Chat(u32),
Channel(u32),
}
#[cfg(test)]
mod tests {
use super::*;