From b1c2c2b0997f32e3d9a64db608ba7c5955a3102c Mon Sep 17 00:00:00 2001 From: ModulatingForce <116608425+modulatingforce@users.noreply.github.com> Date: Tue, 19 Dec 2023 00:34:21 -0500 Subject: [PATCH] enhanced ratelimiter --- Cargo.lock | 54 +++++++++++++++++------------------ src/main.rs | 70 +++++++++++++++++++++++++--------------------- src/ratelimiter.rs | 47 +++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 59 deletions(-) create mode 100644 src/ratelimiter.rs diff --git a/Cargo.lock b/Cargo.lock index 6eb85d5..0708382 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -93,9 +93,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -103,9 +103,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "dotenv" @@ -235,15 +235,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.150" +version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" [[package]] name = "linux-raw-sys" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "lock_api" @@ -278,9 +278,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "wasi", @@ -335,15 +335,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openssl" -version = "0.10.60" +version = "0.10.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79a4c6c3a2b158f7f8f2a2fc5a969fa3a068df6fc9dbb4a43845436e3af7c800" +checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45" dependencies = [ "bitflags 2.4.1", "cfg-if", @@ -373,9 +373,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.96" +version = "0.9.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f" +checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b" dependencies = [ "cc", "libc", @@ -495,15 +495,15 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.38.25" +version = "0.38.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" dependencies = [ "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -580,9 +580,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.39" +version = "2.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" dependencies = [ "proc-macro2", "quote", @@ -604,18 +604,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.50" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.50" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" dependencies = [ "proc-macro2", "quote", @@ -624,9 +624,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.34.0" +version = "1.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" +checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" dependencies = [ "backtrace", "bytes", diff --git a/src/main.rs b/src/main.rs index 87f8d7a..a578fc6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,11 +6,17 @@ use twitch_irc::SecureTCPTransport; use twitch_irc::TwitchIRCClient; use twitch_irc::message::ServerMessage; use std::env; -use std::time::Instant; +// use std::time::Instant; use rand::Rng; use dotenv::dotenv; // mod helpers; +use std::collections::HashMap; + + +mod ratelimiter; +use ratelimiter::RateLimiter; + #[tokio::main] pub async fn main() { @@ -47,7 +53,7 @@ pub async fn main() { // client.join("modulatingforcebot".to_owned()).unwrap(); // client.say("modulatingforcebot".to_owned(), "Connected!".to_owned()).await.unwrap(); - for chnl in botchannels { + for chnl in &botchannels { client.join(chnl.to_owned()).unwrap(); // client.say(chnl.to_owned(), "Connected!".to_owned()).await.unwrap(); client.say(chnl.to_owned(), "annytfLurk".to_owned()).await.unwrap(); @@ -55,11 +61,16 @@ pub async fn main() { // Adding rate limit functionality to be under : https://dev.twitch.tv/docs/irc/#rate-limits - let mut ratelimittimer = false.to_owned(); - let mut ratelimitstart = Instant::now(); - let mut ratelimitcounter = 0; + // ratelimiters are a hashmap of channel and a corresponding rate limiter + let mut ratelimiters:HashMap = HashMap::new(); + for chnl in &botchannels { + let n = RateLimiter::new(); + ratelimiters.insert(chnl.to_owned(),n); + } + + println!("{:?}",ratelimiters); let join_handle = tokio::spawn(async move { while let Some(message) = incoming_messages.recv().await { @@ -74,34 +85,29 @@ pub async fn main() { } ServerMessage::Privmsg(msg) => { println!("(#{}) {}: {}", msg.channel_login, msg.sender.name, msg.message_text); - if !ratelimittimer { - println!("(#{}) > {}", msg.channel_login, "started rate limit timer"); - // ratelimitstart = Instant::now(); - // ratelimittimer = true; - ( ratelimittimer , ratelimitstart ) = ( true , Instant::now() ); - } else if ratelimittimer && ratelimitstart.elapsed().as_secs() < 30 && ratelimitcounter >= 20 { - // skip iteration if rate limit is being reached - println!("(#{}) > {}", msg.channel_login, "rate limit reached"); - continue; - } else if ratelimitstart.elapsed().as_secs() >= 30 { - println!("(#{}) > {}", msg.channel_login, "rate limit timer reset"); - // ratelimittimer = false; - // ratelimitcounter = 0; - ( ratelimittimer , ratelimitcounter ) = ( false , 0 ) ; + + + let contextratelimiter = ratelimiters.get_mut(&msg.channel_login).expect("ERROR: Issue with Rate limiters"); + + match contextratelimiter.check_limiter() { + ratelimiter::LimiterResp::Allow => { + let maxblanks = rand::thread_rng().gen_range(1..=5); + let mut outmsg = "GotTrolled ".to_owned(); + + for _i in 1..maxblanks { + let blankspace: &str = "󠀀"; + outmsg.push_str(blankspace); + } + + client.say_in_reply_to(&msg,outmsg).await.unwrap(); + println!("(#{}) > {}", msg.channel_login, "rate limit counter increase"); + contextratelimiter.increment_counter(); + println!("{:?}",ratelimiters); + }, + ratelimiter::LimiterResp::Skip => { + (); // do nothing otherwise + } } - - let maxblanks = rand::thread_rng().gen_range(1..=5); - let mut outmsg = "GotTrolled ".to_owned(); - - for _i in 1..maxblanks { - let blankspace: &str = "󠀀"; - outmsg.push_str(blankspace); - } - - client.say_in_reply_to(&msg,outmsg).await.unwrap(); - println!("(#{}) > {}", msg.channel_login, "rate limit counter increase"); - ratelimitcounter += 1; - // client.say(msg.channel_login, "GotTrolled".to_owned()).await.unwrap(); }, ServerMessage::Whisper(msg) => { println!("(w) {}: {}", msg.sender.name, msg.message_text); diff --git a/src/ratelimiter.rs b/src/ratelimiter.rs new file mode 100644 index 0000000..252b287 --- /dev/null +++ b/src/ratelimiter.rs @@ -0,0 +1,47 @@ + +use std::time::Instant; + +const TIME_THRESHOLD_S: u64 = 30; +const MSG_THRESHOLD: u32 = 20; + +#[derive(Debug)] +pub struct RateLimiter { + timer: Instant, + msgcounter: u32, +} + +pub enum LimiterResp { + Allow, // when it's evaluated to be within limits + Skip, // as outside of rate limits +} + + +impl RateLimiter { + pub fn new() -> Self { + Self { + timer: Instant::now(), + msgcounter: 0, + } + } + + pub fn check_limiter(&mut self) -> LimiterResp { + + if self.timer.elapsed().as_secs() >= TIME_THRESHOLD_S { +// # [x] elapsed >= TIME_THRESHOLD_S + self.timer = Instant::now(); + self.msgcounter = 0; + LimiterResp::Allow + } else if self.msgcounter < MSG_THRESHOLD { +// # [x] elapsed < TIME_THRESHOLD_S && msgcounter < MSG_THRESHOLD + LimiterResp::Allow + // } else if self.msgcounter >= MSG_THRESHOLD { + } else { +// # [x] elapsed < TIME_THRESHOLD_S && msgcounter >= MSG_THRESHOLD + LimiterResp::Skip + } + } + + pub fn increment_counter(&mut self) -> () { + self.msgcounter += 1; + } +} \ No newline at end of file