enhanced ratelimiter

This commit is contained in:
ModulatingForce 2023-12-19 00:34:21 -05:00
parent 3028b1abfe
commit b1c2c2b099
3 changed files with 112 additions and 59 deletions

54
Cargo.lock generated
View file

@ -93,9 +93,9 @@ dependencies = [
[[package]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.9.3" version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
dependencies = [ dependencies = [
"core-foundation-sys", "core-foundation-sys",
"libc", "libc",
@ -103,9 +103,9 @@ dependencies = [
[[package]] [[package]]
name = "core-foundation-sys" name = "core-foundation-sys"
version = "0.8.4" version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
[[package]] [[package]]
name = "dotenv" name = "dotenv"
@ -235,15 +235,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.150" version = "0.2.151"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.4.11" version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"
[[package]] [[package]]
name = "lock_api" name = "lock_api"
@ -278,9 +278,9 @@ dependencies = [
[[package]] [[package]]
name = "mio" name = "mio"
version = "0.8.9" version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
dependencies = [ dependencies = [
"libc", "libc",
"wasi", "wasi",
@ -335,15 +335,15 @@ dependencies = [
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.18.0" version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]] [[package]]
name = "openssl" name = "openssl"
version = "0.10.60" version = "0.10.61"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79a4c6c3a2b158f7f8f2a2fc5a969fa3a068df6fc9dbb4a43845436e3af7c800" checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45"
dependencies = [ dependencies = [
"bitflags 2.4.1", "bitflags 2.4.1",
"cfg-if", "cfg-if",
@ -373,9 +373,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]] [[package]]
name = "openssl-sys" name = "openssl-sys"
version = "0.9.96" version = "0.9.97"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f" checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
@ -495,15 +495,15 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.25" version = "0.38.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316"
dependencies = [ dependencies = [
"bitflags 2.4.1", "bitflags 2.4.1",
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
"windows-sys 0.48.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -580,9 +580,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.39" version = "2.0.41"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -604,18 +604,18 @@ dependencies = [
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.50" version = "1.0.51"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "1.0.50" version = "1.0.51"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -624,9 +624,9 @@ dependencies = [
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.34.0" version = "1.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c"
dependencies = [ dependencies = [
"backtrace", "backtrace",
"bytes", "bytes",

View file

@ -6,11 +6,17 @@ use twitch_irc::SecureTCPTransport;
use twitch_irc::TwitchIRCClient; use twitch_irc::TwitchIRCClient;
use twitch_irc::message::ServerMessage; use twitch_irc::message::ServerMessage;
use std::env; use std::env;
use std::time::Instant; // use std::time::Instant;
use rand::Rng; use rand::Rng;
use dotenv::dotenv; use dotenv::dotenv;
// mod helpers; // mod helpers;
use std::collections::HashMap;
mod ratelimiter;
use ratelimiter::RateLimiter;
#[tokio::main] #[tokio::main]
pub async fn main() { pub async fn main() {
@ -47,7 +53,7 @@ pub async fn main() {
// client.join("modulatingforcebot".to_owned()).unwrap(); // client.join("modulatingforcebot".to_owned()).unwrap();
// client.say("modulatingforcebot".to_owned(), "Connected!".to_owned()).await.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.join(chnl.to_owned()).unwrap();
// client.say(chnl.to_owned(), "Connected!".to_owned()).await.unwrap(); // client.say(chnl.to_owned(), "Connected!".to_owned()).await.unwrap();
client.say(chnl.to_owned(), "annytfLurk".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 // 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(); // ratelimiters are a hashmap of channel and a corresponding rate limiter
let mut ratelimitcounter = 0; let mut ratelimiters:HashMap<String,RateLimiter> = 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 { let join_handle = tokio::spawn(async move {
while let Some(message) = incoming_messages.recv().await { while let Some(message) = incoming_messages.recv().await {
@ -74,34 +85,29 @@ pub async fn main() {
} }
ServerMessage::Privmsg(msg) => { ServerMessage::Privmsg(msg) => {
println!("(#{}) {}: {}", msg.channel_login, msg.sender.name, msg.message_text); println!("(#{}) {}: {}", msg.channel_login, msg.sender.name, msg.message_text);
if !ratelimittimer {
println!("(#{}) > {}", msg.channel_login, "started rate limit timer");
// ratelimitstart = Instant::now(); let contextratelimiter = ratelimiters.get_mut(&msg.channel_login).expect("ERROR: Issue with Rate limiters");
// ratelimittimer = true;
( ratelimittimer , ratelimitstart ) = ( true , Instant::now() ); match contextratelimiter.check_limiter() {
} else if ratelimittimer && ratelimitstart.elapsed().as_secs() < 30 && ratelimitcounter >= 20 { ratelimiter::LimiterResp::Allow => {
// skip iteration if rate limit is being reached let maxblanks = rand::thread_rng().gen_range(1..=5);
println!("(#{}) > {}", msg.channel_login, "rate limit reached"); let mut outmsg = "GotTrolled ".to_owned();
continue;
} else if ratelimitstart.elapsed().as_secs() >= 30 { for _i in 1..maxblanks {
println!("(#{}) > {}", msg.channel_login, "rate limit timer reset"); let blankspace: &str = "󠀀";
// ratelimittimer = false; outmsg.push_str(blankspace);
// ratelimitcounter = 0; }
( ratelimittimer , ratelimitcounter ) = ( false , 0 ) ;
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) => { ServerMessage::Whisper(msg) => {
println!("(w) {}: {}", msg.sender.name, msg.message_text); println!("(w) {}: {}", msg.sender.name, msg.message_text);

47
src/ratelimiter.rs Normal file
View file

@ -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;
}
}