Add rate limits to commands

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/9/head
trivernis 4 years ago
parent cbdb2e3265
commit 8b8fc8f814
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

@ -15,6 +15,7 @@ use crate::handler::Handler;
use crate::utils::context_data::{DatabaseContainer, Store, StoreData}; use crate::utils::context_data::{DatabaseContainer, Store, StoreData};
use crate::utils::error::{BotError, BotResult}; use crate::utils::error::{BotError, BotResult};
use bot_serenityutils::menu::EventDrivenMessageContainer; use bot_serenityutils::menu::EventDrivenMessageContainer;
use serenity::framework::standard::buckets::LimitedFor;
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::Mutex; use tokio::sync::Mutex;
@ -24,7 +25,7 @@ pub async fn get_client() -> BotResult<Client> {
let client = Client::builder(token) let client = Client::builder(token)
.event_handler(Handler) .event_handler(Handler)
.framework(get_framework()) .framework(get_framework().await)
.register_songbird() .register_songbird()
.await?; .await?;
{ {
@ -37,7 +38,7 @@ pub async fn get_client() -> BotResult<Client> {
Ok(client) Ok(client)
} }
pub fn get_framework() -> StandardFramework { pub async fn get_framework() -> StandardFramework {
let mut owners = HashSet::new(); let mut owners = HashSet::new();
if let Some(owner) = dotenv::var("BOT_OWNER").ok().and_then(|o| o.parse().ok()) { if let Some(owner) = dotenv::var("BOT_OWNER").ok().and_then(|o| o.parse().ok()) {
owners.insert(UserId(owner)); owners.insert(UserId(owner));
@ -61,6 +62,22 @@ pub fn get_framework() -> StandardFramework {
.before(before_hook) .before(before_hook)
.on_dispatch_error(dispatch_error) .on_dispatch_error(dispatch_error)
.help(&HELP) .help(&HELP)
.bucket("music_api", |b| {
b.delay(1)
.time_span(60)
.limit(30)
.limit_for(LimitedFor::User)
})
.await
.bucket("sauce_api", |b| {
b.delay(1)
.time_span(60)
.limit(10)
.limit_for(LimitedFor::User)
})
.await
.bucket("general", |b| b.time_span(10).limit(5))
.await
} }
#[hook] #[hook]

@ -11,6 +11,7 @@ use crate::utils::context_data::Store;
#[example("unbreaking")] #[example("unbreaking")]
#[min_args(1)] #[min_args(1)]
#[aliases("ench")] #[aliases("ench")]
#[bucket("general")]
pub(crate) async fn enchantment(ctx: &Context, msg: &Message, args: Args) -> CommandResult { pub(crate) async fn enchantment(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
let data = ctx.data.read().await; let data = ctx.data.read().await;
let store = data.get::<Store>().expect("Failed to get store"); let store = data.get::<Store>().expect("Failed to get store");

@ -11,6 +11,7 @@ use crate::utils::context_data::Store;
#[example("bread")] #[example("bread")]
#[min_args(1)] #[min_args(1)]
#[aliases("i")] #[aliases("i")]
#[bucket("general")]
pub(crate) async fn item(ctx: &Context, msg: &Message, args: Args) -> CommandResult { pub(crate) async fn item(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
let data = ctx.data.read().await; let data = ctx.data.read().await;
let store = data.get::<Store>().expect("Failed to get store"); let store = data.get::<Store>().expect("Failed to get store");

@ -5,6 +5,7 @@ use serenity::model::channel::Message;
#[command] #[command]
#[description("Displays information about the bot")] #[description("Displays information about the bot")]
#[bucket("general")]
async fn about(ctx: &Context, msg: &Message) -> CommandResult { async fn about(ctx: &Context, msg: &Message) -> CommandResult {
msg.channel_id msg.channel_id
.send_message(ctx, |m| { .send_message(ctx, |m| {

@ -21,6 +21,7 @@ static PEKOS: &[&str] = &[
#[usage("(<content>)")] #[usage("(<content>)")]
#[example("Hello")] #[example("Hello")]
#[aliases("peko")] #[aliases("peko")]
#[bucket("general")]
async fn pekofy(ctx: &Context, msg: &Message, args: Args) -> CommandResult { async fn pekofy(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
let mut reference_message = msg.id; let mut reference_message = msg.id;
let mut content = args.message().to_string(); let mut content = args.message().to_string();

@ -6,6 +6,7 @@ use serenity::model::channel::Message;
#[command] #[command]
#[description("Simple ping test command")] #[description("Simple ping test command")]
#[usage("")] #[usage("")]
#[bucket("general")]
async fn ping(ctx: &Context, msg: &Message) -> CommandResult { async fn ping(ctx: &Context, msg: &Message) -> CommandResult {
msg.reply(ctx, "Pong!").await?; msg.reply(ctx, "Pong!").await?;

@ -13,6 +13,7 @@ static QALC_HELP: &[&str] = &["help", "--help", "-h", "h"];
#[min_args(1)] #[min_args(1)]
#[usage("<expression>")] #[usage("<expression>")]
#[example("1 * 1 + 1 / sqrt(2)")] #[example("1 * 1 + 1 / sqrt(2)")]
#[bucket("general")]
async fn qalc(ctx: &Context, msg: &Message, args: Args) -> CommandResult { async fn qalc(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
let expression = args.message(); let expression = args.message();
lazy_static::lazy_static! { lazy_static::lazy_static! {

@ -14,6 +14,7 @@ use crate::utils::get_previous_message_or_reply;
#[description("Searches for the source of a previously posted image or an image replied to.")] #[description("Searches for the source of a previously posted image or an image replied to.")]
#[usage("")] #[usage("")]
#[aliases("source")] #[aliases("source")]
#[bucket("sauce_api")]
async fn sauce(ctx: &Context, msg: &Message) -> CommandResult { async fn sauce(ctx: &Context, msg: &Message) -> CommandResult {
log::debug!("Got sauce command"); log::debug!("Got sauce command");
let source_msg = get_previous_message_or_reply(ctx, msg).await?; let source_msg = get_previous_message_or_reply(ctx, msg).await?;

@ -15,6 +15,7 @@ const VERSION: &'static str = env!("CARGO_PKG_VERSION");
#[command] #[command]
#[description("Shows some statistics about the bot")] #[description("Shows some statistics about the bot")]
#[usage("")] #[usage("")]
#[bucket("general")]
async fn stats(ctx: &Context, msg: &Message) -> CommandResult { async fn stats(ctx: &Context, msg: &Message) -> CommandResult {
log::debug!("Reading system stats"); log::debug!("Reading system stats");
let mut system = sysinfo::System::new_all(); let mut system = sysinfo::System::new_all();

@ -10,6 +10,7 @@ use serenity::model::channel::Message;
#[min_args(1)] #[min_args(1)]
#[max_args(3)] #[max_args(3)]
#[usage("<%H:%M/now> (<from-timezone>) (<to-timezone>)")] #[usage("<%H:%M/now> (<from-timezone>) (<to-timezone>)")]
#[bucket("general")]
async fn time(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { async fn time(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
let when = args.single::<String>().unwrap_or("now".to_string()); let when = args.single::<String>().unwrap_or("now".to_string());
let first_timezone = args.single::<String>().ok(); let first_timezone = args.single::<String>().ok();

@ -8,6 +8,7 @@ use serenity::model::channel::Message;
#[min_args(1)] #[min_args(1)]
#[usage("<query...>")] #[usage("<query...>")]
#[example("Europe Berlin")] #[example("Europe Berlin")]
#[bucket("general")]
async fn timezones(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { async fn timezones(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
let query = args let query = args
.iter::<String>() .iter::<String>()

@ -11,6 +11,7 @@ use crate::commands::music::{get_queue_for_guild, is_dj};
#[description("Clears the queue")] #[description("Clears the queue")]
#[usage("")] #[usage("")]
#[aliases("cl")] #[aliases("cl")]
#[bucket("general")]
async fn clear_queue(ctx: &Context, msg: &Message) -> CommandResult { async fn clear_queue(ctx: &Context, msg: &Message) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap(); let guild = msg.guild(&ctx.cache).await.unwrap();

@ -14,6 +14,7 @@ use crate::messages::music::NowPlayingMessage;
#[description("Displays the currently playing song")] #[description("Displays the currently playing song")]
#[usage("")] #[usage("")]
#[aliases("nowplaying", "np")] #[aliases("nowplaying", "np")]
#[bucket("general")]
async fn current(ctx: &Context, msg: &Message) -> CommandResult { async fn current(ctx: &Context, msg: &Message) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap(); let guild = msg.guild(&ctx.cache).await.unwrap();

@ -10,6 +10,7 @@ use crate::commands::music::{get_channel_for_author, join_channel};
#[only_in(guilds)] #[only_in(guilds)]
#[description("Joins a voice channel")] #[description("Joins a voice channel")]
#[usage("")] #[usage("")]
#[bucket("general")]
async fn join(ctx: &Context, msg: &Message) -> CommandResult { async fn join(ctx: &Context, msg: &Message) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap(); let guild = msg.guild(&ctx.cache).await.unwrap();
let channel_id = get_channel_for_author(&msg.author.id, &guild)?; let channel_id = get_channel_for_author(&msg.author.id, &guild)?;

@ -11,6 +11,7 @@ use crate::commands::music::{get_queue_for_guild, get_voice_manager, is_dj};
#[description("Leaves a voice channel")] #[description("Leaves a voice channel")]
#[usage("")] #[usage("")]
#[aliases("stop")] #[aliases("stop")]
#[bucket("general")]
async fn leave(ctx: &Context, msg: &Message) -> CommandResult { async fn leave(ctx: &Context, msg: &Message) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap(); let guild = msg.guild(&ctx.cache).await.unwrap();
log::debug!("Leave request received for guild {}", guild.id); log::debug!("Leave request received for guild {}", guild.id);

@ -11,6 +11,7 @@ use crate::providers::music::lyrics::get_lyrics;
#[only_in(guilds)] #[only_in(guilds)]
#[description("Shows the lyrics for the currently playing song")] #[description("Shows the lyrics for the currently playing song")]
#[usage("")] #[usage("")]
#[bucket("general")]
async fn lyrics(ctx: &Context, msg: &Message) -> CommandResult { async fn lyrics(ctx: &Context, msg: &Message) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap(); let guild = msg.guild(&ctx.cache).await.unwrap();
log::debug!("Fetching lyrics for song playing in {}", guild.id); log::debug!("Fetching lyrics for song playing in {}", guild.id);

@ -10,6 +10,7 @@ use crate::commands::music::{get_queue_for_guild, is_dj};
#[only_in(guilds)] #[only_in(guilds)]
#[description("Pauses playback")] #[description("Pauses playback")]
#[usage("")] #[usage("")]
#[bucket("general")]
async fn pause(ctx: &Context, msg: &Message) -> CommandResult { async fn pause(ctx: &Context, msg: &Message) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap(); let guild = msg.guild(&ctx.cache).await.unwrap();
log::debug!("Pausing playback for guild {}", guild.id); log::debug!("Pausing playback for guild {}", guild.id);

@ -16,6 +16,7 @@ use crate::providers::settings::{get_setting, Setting};
#[usage("(<spotify_url,youtube_url,query>)")] #[usage("(<spotify_url,youtube_url,query>)")]
#[min_args(1)] #[min_args(1)]
#[aliases("p")] #[aliases("p")]
#[bucket("music_api")]
async fn play(ctx: &Context, msg: &Message, args: Args) -> CommandResult { async fn play(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
let query = args.message(); let query = args.message();

@ -15,6 +15,7 @@ use crate::commands::music::{
#[usage("<song-url>")] #[usage("<song-url>")]
#[min_args(1)] #[min_args(1)]
#[aliases("pn", "play-next")] #[aliases("pn", "play-next")]
#[bucket("music_api")]
async fn play_next(ctx: &Context, msg: &Message, args: Args) -> CommandResult { async fn play_next(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
let query = args.message(); let query = args.message();

@ -10,6 +10,7 @@ use crate::utils::context_data::get_database_from_context;
#[only_in(guilds)] #[only_in(guilds)]
#[description("Displays a list of all saved playlists")] #[description("Displays a list of all saved playlists")]
#[usage("")] #[usage("")]
#[bucket("general")]
async fn playlists(ctx: &Context, msg: &Message) -> CommandResult { async fn playlists(ctx: &Context, msg: &Message) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap(); let guild = msg.guild(&ctx.cache).await.unwrap();
log::debug!("Displaying playlists for guild {}", guild.id); log::debug!("Displaying playlists for guild {}", guild.id);

@ -13,6 +13,7 @@ use crate::commands::music::get_queue_for_guild;
#[description("Shows the song queue")] #[description("Shows the song queue")]
#[usage("")] #[usage("")]
#[aliases("q")] #[aliases("q")]
#[bucket("general")]
async fn queue(ctx: &Context, msg: &Message) -> CommandResult { async fn queue(ctx: &Context, msg: &Message) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap(); let guild = msg.guild(&ctx.cache).await.unwrap();
log::trace!("Displaying queue for guild {}", guild.id); log::trace!("Displaying queue for guild {}", guild.id);

@ -13,6 +13,7 @@ use crate::utils::context_data::get_database_from_context;
#[example("anime https://www.youtube.com/playlist?list=PLqaM77H_o5hykROCe3uluvZEaPo6bZj-C")] #[example("anime https://www.youtube.com/playlist?list=PLqaM77H_o5hykROCe3uluvZEaPo6bZj-C")]
#[min_args(2)] #[min_args(2)]
#[aliases("add-playlist", "save-playlist")] #[aliases("add-playlist", "save-playlist")]
#[bucket("general")]
async fn save_playlist(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { async fn save_playlist(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap(); let guild = msg.guild(&ctx.cache).await.unwrap();

@ -11,6 +11,7 @@ use crate::commands::music::{get_queue_for_guild, is_dj};
#[description("Shuffles the queue")] #[description("Shuffles the queue")]
#[usage("")] #[usage("")]
#[aliases("sh")] #[aliases("sh")]
#[bucket("general")]
async fn shuffle(ctx: &Context, msg: &Message) -> CommandResult { async fn shuffle(ctx: &Context, msg: &Message) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap(); let guild = msg.guild(&ctx.cache).await.unwrap();

@ -11,6 +11,7 @@ use crate::commands::music::{get_queue_for_guild, is_dj};
#[description("Skips to the next song")] #[description("Skips to the next song")]
#[usage("")] #[usage("")]
#[aliases("next")] #[aliases("next")]
#[bucket("general")]
async fn skip(ctx: &Context, msg: &Message) -> CommandResult { async fn skip(ctx: &Context, msg: &Message) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap(); let guild = msg.guild(&ctx.cache).await.unwrap();
if !is_dj(ctx, guild.id, &msg.author).await? { if !is_dj(ctx, guild.id, &msg.author).await? {

@ -14,6 +14,7 @@ use crate::utils::context_data::get_database_from_context;
#[min_args(0)] #[min_args(0)]
#[max_args(1)] #[max_args(1)]
#[required_permissions("MANAGE_GUILD")] #[required_permissions("MANAGE_GUILD")]
#[bucket("general")]
async fn get(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { async fn get(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
let database = get_database_from_context(ctx).await; let database = get_database_from_context(ctx).await;
let guild = msg.guild(&ctx.cache).await.unwrap(); let guild = msg.guild(&ctx.cache).await.unwrap();

@ -16,6 +16,7 @@ use crate::utils::context_data::get_database_from_context;
#[min_args(1)] #[min_args(1)]
#[max_args(2)] #[max_args(2)]
#[required_permissions("MANAGE_GUILD")] #[required_permissions("MANAGE_GUILD")]
#[bucket("general")]
async fn set(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { async fn set(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
let key = args.single::<String>().unwrap().to_lowercase(); let key = args.single::<String>().unwrap().to_lowercase();
let all_settings: Vec<String> = ALL_SETTINGS.iter().map(|s| s.to_string()).collect(); let all_settings: Vec<String> = ALL_SETTINGS.iter().map(|s| s.to_string()).collect();

Loading…
Cancel
Save