From a4a9353e1c8f902b7d7b3cf74e3e5b129c214330 Mon Sep 17 00:00:00 2001 From: seth Date: Mon, 10 Jul 2023 00:18:36 -0400 Subject: start using poise --- src/main.rs | 285 ++++++++++++++++++------------------------------------------ 1 file changed, 84 insertions(+), 201 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 51f0f3c..d4914e9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,232 +1,115 @@ -use once_cell::sync::Lazy; -use serenity::async_trait; -use serenity::framework::standard::macros::{command, group}; -use serenity::framework::standard::{CommandResult, StandardFramework}; -use serenity::model::application::command::Command; -use serenity::model::prelude::*; -use serenity::prelude::*; -use utils::parse_snowflake_from_env; +use std::time::Duration; +use std::{env, error}; +use crate::commands::*; +use crate::consts::*; use crate::pinboard::PinBoard; -use crate::utils::parse_snowflakes_from_env; +use log::*; +use poise::serenity_prelude as serentiy; +use poise::serenity_prelude::*; mod api; mod commands; mod consts; +mod handler; mod pinboard; mod utils; -const TEAWIE_GUILD: GuildId = GuildId(1055663552679137310); -const BOT: UserId = UserId(1056467120986271764); +type Error = Box; +type Context<'a> = poise::Context<'a, Data, Error>; -fn is_guild_allowed(gid: GuildId) -> bool { - // Had to be global state because Serenity doesn't allow you to store - // extra state in frameworks - static ALLOWED_GUILDS: Lazy> = Lazy::new(|| { - parse_snowflakes_from_env("ALLOWED_GUILDS", GuildId) - .unwrap_or_else(|| vec![TEAWIE_GUILD, GuildId(1091969030694375444)]) - }); - - ALLOWED_GUILDS.contains(&gid) +#[derive(Clone)] +pub struct Data { + bot: serentiy::UserId, + pin_board: Option, } -#[group] -#[commands(bing, ask, random_lore, random_teawie, teawiespam)] -struct General; - -struct Handler { - bot: UserId, - pin_board: Option, +impl Default for Data { + fn default() -> Self { + Self::new() + } } -impl Handler { +impl Data { pub fn new() -> Self { - let bot = parse_snowflake_from_env("BOT", UserId).unwrap_or(BOT); + let bot = utils::parse_snowflake_from_env("BOT", UserId).unwrap_or(consts::BOT); let pin_board = PinBoard::new(); Self { bot, pin_board } } - fn should_echo(&self, msg: &Message) -> bool { - // Don't echo to anything we posted ourselves, and don't echo at all unless on certain - // servers - if msg.author.id == self.bot || !is_guild_allowed(msg.guild_id.unwrap_or_default()) { - return false; - } - - let content = &msg.content; - - content == "🗿" - || consts::TEAMOJIS.contains(&content.as_str()) - || content.to_ascii_lowercase() == "moyai" - || content - .to_ascii_lowercase() - .contains("twitter's recommendation algorithm") - } } -#[async_trait] -impl EventHandler for Handler { - /* - * echo some messages when they're sent - */ - async fn message(&self, ctx: Context, msg: Message) { - if self.should_echo(&msg) { - let send = msg.reply(&ctx, &msg.content); - if let Err(why) = send.await { - println!("error when replying to {:?}: {:?}", msg.content, why); - } +async fn on_error(error: poise::FrameworkError<'_, Data, Error>) { + match error { + poise::FrameworkError::Setup { error, .. } => panic!("failed to start bot: {error:?}"), + poise::FrameworkError::Command { error, ctx } => { + error!("error in command {}: {:?}", ctx.command().name, error); } - } - - async fn channel_pins_update(&self, ctx: Context, pin: ChannelPinsUpdateEvent) { - let Some(pin_board) = &self.pin_board else { - return; - }; - - println!( - "audit log: {:#?}", - pin.guild_id - .unwrap() - .audit_logs( - &ctx.http, - Some(Action::Message(MessageAction::Pin).num()), - None, - None, - Some(1), - ) - .await - ); - pin_board.handle_pin(&ctx, &pin).await; - } - - async fn interaction_create(&self, ctx: Context, interaction: Interaction) { - if let Interaction::ApplicationCommand(command) = interaction { - println!("Received command interaction: {command:#?}"); - let content = match command.data.name.as_str() { - "ask" => commands::ask::run(&command.data.options), - "bottom" => commands::bottom::run(&command.data.options), - "convertto" => commands::convert::run(&command.data.options), - "copypasta" => { - commands::copypasta::run(&command.data.options, command.channel_id, &ctx.http) - .await - } - "random_lore" => commands::random_lore::run(&command.data.options), - "random_shiggy" => commands::random_shiggy::run(&command.data.options).await, - "random_teawie" => commands::random_teawie::run(&command.data.options).await, - _ => "not implemented :(".to_string(), - }; - - if let Err(why) = command - .create_interaction_response(&ctx.http, |response| { - response - .kind(InteractionResponseType::ChannelMessageWithSource) - .interaction_response_data(|message| message.content(content)) - }) - .await - { - println!("cannot respond to slash command: {why}"); + error => { + if let Err(e) = poise::builtins::on_error(error).await { + error!("error while handling an error: {}", e); } } } - - async fn ready(&self, ctx: Context, ready: Ready) { - println!("connected as {:?}", ready.user.name); - - let guild_commands = - GuildId::set_application_commands(&TEAWIE_GUILD, &ctx.http, |commands| { - commands.create_application_command(commands::copypasta::register) - }) - .await; - - println!("registered guild commands: {guild_commands:#?}"); - - let commands = Command::set_global_application_commands(&ctx.http, |commands| { - commands - .create_application_command(commands::ask::register) - .create_application_command(commands::bottom::register) - .create_application_command(commands::convert::register) - .create_application_command(commands::random_lore::register) - .create_application_command(commands::random_shiggy::register) - .create_application_command(commands::random_teawie::register) - }) - .await; - - println!("registered global commands: {commands:#?}"); - } } #[tokio::main] async fn main() { - let framework = StandardFramework::new() - .configure(|c| c.prefix("!")) - .group(&GENERAL_GROUP); - - let token = std::env::var("TOKEN").expect("couldn't find token in environment."); - - let intents = GatewayIntents::all(); - let handler = Handler::new(); - - let mut client = Client::builder(token, intents) - .event_handler(handler) - .framework(framework) - .await - .expect("error creating client"); - - if let Err(why) = client.start().await { - println!("an error occurred: {:?}", why); - } -} - -#[command] -async fn bing(ctx: &Context, msg: &Message) -> CommandResult { - msg.channel_id - .send_message(&ctx.http, |m| m.content("bong")) - .await?; - - Ok(()) -} - -#[command] -async fn ask(ctx: &Context, msg: &Message) -> CommandResult { - let resp = utils::get_random_response(); - msg.channel_id - .send_message(&ctx.http, |m| m.content(resp)) - .await?; - - Ok(()) -} - -#[command] -async fn random_lore(ctx: &Context, msg: &Message) -> CommandResult { - let resp = utils::get_random_lore(); - msg.channel_id - .send_message(&ctx.http, |m| m.content(resp)) - .await?; - - Ok(()) -} - -#[command] -async fn random_teawie(ctx: &Context, msg: &Message) -> CommandResult { - let resp = api::guzzle::get_random_teawie().await; - msg.channel_id - .send_message(&ctx.http, |m| m.content(resp)) - .await?; - - Ok(()) -} - -#[command] -async fn teawiespam(ctx: &Context, msg: &Message) -> CommandResult { - if !is_guild_allowed(msg.guild_id.unwrap_or_default()) { - return Ok(()); - } - - let resp = "<:teawiesmile:1056438046440042546>".repeat(50); - - msg.channel_id - .send_message(&ctx.http, |m| m.content(resp)) - .await?; + env_logger::init(); + dotenvy::dotenv().unwrap(); + + let guild_commands = vec![copypasta::copypasta(), teawiespam::teawiespam()]; + + let options = poise::FrameworkOptions { + commands: vec![ + ask::ask(), + bing::bing(), + bottom::bottom(), + convert::convert(), + random_lore::random_lore(), + random_shiggy::random_shiggy(), + random_teawie::random_teawie(), + copypasta::copypasta(), + teawiespam::teawiespam(), + ], + event_handler: |ctx, event, _, data| { + Box::pin(async move { + // yes this is dumb. no i don't care. + let handler = handler::Handler::new(data.clone()); + event.clone().dispatch(ctx.clone(), &handler).await; + Ok(()) + }) + }, + prefix_options: poise::PrefixFrameworkOptions { + prefix: Some("!".into()), + edit_tracker: Some(poise::EditTracker::for_timespan(Duration::from_secs(3600))), + ..Default::default() + }, + on_error: |error| Box::pin(on_error(error)), + command_check: Some(|ctx| { + Box::pin(async move { + Ok(ctx.author().id != ctx.framework().bot_id && ctx.author().id != consts::BOT) + }) + }), + ..Default::default() + }; + + let framework = poise::Framework::builder() + .options(options) + .token(env::var("TOKEN").expect("couldn't find token in environment.")) + .intents(serentiy::GatewayIntents::all()) + .setup(|ctx, _ready, framework| { + Box::pin(async move { + info!("logged in as {}", _ready.user.name); + + poise::builtins::register_globally(ctx, &framework.options().commands).await?; + info!("registered global commands!"); + poise::builtins::register_in_guild(ctx, &guild_commands, TEAWIE_GUILD).await?; + info!("registered guild commands!"); + + Ok(Data::new()) + }) + }); - Ok(()) + framework.run().await.unwrap() } -- cgit v1.2.3