Merge pull request 'Enh RateLimiters with Async Sleep' (#25) from chat_async_sleep into main
All checks were successful
ci/woodpecker/push/cargo-checks Pipeline was successful

Reviewed-on: #25
This commit is contained in:
modulatingforce 2024-03-18 22:35:07 -04:00
commit 53c5780322
3 changed files with 50 additions and 6 deletions

View file

@ -19,6 +19,8 @@ use crate::core::botinstance::ChType;
use crate::core::botlog;
pub use ChType::Channel;
use tokio::time::{sleep, Duration};
#[derive(Clone)]
pub struct Chat {
pub ratelimiters: Arc<Mutex<HashMap<ChType, RateLimiter>>>, // used to limit messages sent per channel
@ -59,6 +61,11 @@ impl Chat {
.get_mut(&Channel(String::from(&msg.channel_login)))
.expect("ERROR: Issue with Rate limiters");
// Continue to check the limiter and sleep if required if the minimum is not reached
while let ratelimiter::LimiterResp::Sleep(sleeptime) = contextratelimiter.check_limiter() {
sleep(Duration::from_secs_f64(sleeptime)).await;
}
match contextratelimiter.check_limiter() {
ratelimiter::LimiterResp::Allow => {
let maxblanks = rand::thread_rng().gen_range(1..=20);
@ -73,8 +80,8 @@ impl Chat {
contextratelimiter.increment_counter();
let logstr = format!(
"(#{}) > {} ; Ratelimiers : {:?}",
msg.channel_login, "rate limit counter increase", self.ratelimiters
"(#{}) > {} ; contextratelimiter : {:?}",
msg.channel_login, "rate limit counter increase", contextratelimiter
);
botlog::trace(
@ -86,7 +93,11 @@ impl Chat {
ratelimiter::LimiterResp::Skip => {
// (); // do nothing otherwise
}
ratelimiter::LimiterResp::Sleep(_) => {
panic!("ISSUE : sleep was already awaited - Should not happen?");
}
}
Log::flush();
}

View file

@ -1,18 +1,23 @@
const TIME_THRESHOLD_S: u64 = 30;
const TIME_MIN_S_F64: f64 = 1.0;
const MSG_THRESHOLD: u32 = 20;
use std::time::Instant;
use crate::core::botlog;
#[derive(Debug, Clone)]
pub struct RateLimiter {
timer: Instant,
msgcounter: u32,
lastmsgtimer : Instant,
}
#[derive(Debug)]
pub enum LimiterResp {
Allow, // when it's evaluated to be within limits
Skip, // as outside of rate limits
// Enqueue, // [FUTURE]
Sleep(f64), // Sleep for x seconds
}
impl Default for RateLimiter {
@ -26,23 +31,50 @@ impl RateLimiter {
Self {
timer: Instant::now(),
msgcounter: 0,
lastmsgtimer: Instant::now(),
}
}
pub fn check_limiter(&mut self) -> LimiterResp {
if self.timer.elapsed().as_secs() >= TIME_THRESHOLD_S {
let logstr = format!(
">> RateLimiter > {:?}",self
);
botlog::trace(
logstr.as_str(),
Some("Rate Limiter Inner".to_string()),
None,
);
let rsp = if self.timer.elapsed().as_secs() >= TIME_THRESHOLD_S {
self.timer = Instant::now();
self.msgcounter = 0;
LimiterResp::Allow
} else if self.msgcounter < MSG_THRESHOLD {
} else if self.msgcounter < MSG_THRESHOLD &&
self.lastmsgtimer.elapsed().as_secs_f64() >= TIME_MIN_S_F64 {
LimiterResp::Allow
} else {
// when elapsed() < TIME_THRESHOLD_S && msgcounter >= MSG_THRESHOLD
LimiterResp::Skip
}
// LimiterResp::Skip
LimiterResp::Sleep(TIME_MIN_S_F64 - self.lastmsgtimer.elapsed().as_secs_f64())
};
botlog::trace(
&format!("Limiter Response : {:?} ; Elapsed (as_sec_f64) : {}",
rsp, self.lastmsgtimer.elapsed().as_secs_f64()),
Some("Rate Limiter Inner".to_string()),
None,
);
rsp
}
pub fn increment_counter(&mut self) {
self.msgcounter += 1;
self.lastmsgtimer = Instant::now();
}
}

View file

@ -66,6 +66,7 @@ async fn good_girl(bot: BotAR, msg: PrivmsgMessage) {
if msg.sender.name.to_lowercase() == "ModulatingForce".to_lowercase()
|| msg.sender.name.to_lowercase() == "mzNToRi".to_lowercase()
// if msg.sender.name.to_lowercase() == "mzNToRi".to_lowercase()
{
botlog::debug(
"Good Girl Detected > Pausechamp",