use poise::serenity_prelude::{self as serenity, Interaction, MessageComponentInteraction, MessageId, ChannelId}; use regex::Regex; use serde_json::json; use crate::models::*; use crate::{Data, Error}; use tracing::info; /** * Handles specific events, including ReactionAdd, which is needed for the reaction role handler to * function properly */ pub async fn event_handler( ctx: &serenity::Context, event: &poise::Event<'_>, data: &Data, ) -> Result<(), Error> { { let pool = data.pg.lock().unwrap().clone(); match event { poise::Event::ReactionAdd { add_reaction } => { let current_user = ctx.http.get_current_user().await?; if add_reaction.user_id.unwrap() == current_user.id { return Ok(()); } // This fetches the role and lets us query extra data including role ID let rrole = sqlx::query_as!( ReactionRole, "SELECT * FROM reaction_roles WHERE message_id=$1 AND reaction=$2", add_reaction.message_id.0.to_string(), add_reaction.emoji.to_string() ) .fetch_one(&pool) .await?; let member = ctx .http .get_member( rrole.guild_id.parse::()?, add_reaction.user_id.unwrap().0, ) .await?; // Honestly, not really needed. let member_roles = member.roles; let role_id = serenity::RoleId(rrole.role_id.parse::()?); if member_roles.contains(&role_id) { ctx.http .remove_member_role( member.guild_id.0, member.user.id.0, role_id.0, Some("Reaction Role Menu"), ) .await?; } else { ctx.http .add_member_role( rrole.guild_id.parse::()?, add_reaction.user_id.unwrap().0, rrole.role_id.parse::()?, Some("Reaction Role"), ) .await?; } if let Ok(dm_chan) = add_reaction .user_id .unwrap() .create_dm_channel(&ctx.http) .await { dm_chan.say(ctx, format!("Toggled the role!")).await?; } else { info!("Could not DM user, but we did the role anyways"); } add_reaction.delete(&ctx.http).await?; }, poise::Event::Message { new_message } => { let current_user = ctx.http.get_current_user().await?; if new_message.author.id.0 != current_user.id.0 { // This message does *not* belong to the bot let filters = sqlx::query_as::<_, MessageFilter>( "SELECT * FROM message_filter WHERE guild_id=$1") .bind(new_message.guild_id.unwrap().0.to_string()) .fetch_all(&pool) .await?; let content = new_message.content.clone(); for f in filters { let r = Regex::new(&f.pattern)?; if r.is_match(&content) { match f.action { FilterAction::Review => { if let Ok(c) = sqlx::query_as!(ConfigChannel, "SELECT * FROM channels WHERE purpose='review' AND guild_id=$1", new_message.guild_id.unwrap().0.to_string()) .fetch_one(&pool) .await { let chan = serenity::ChannelId(c.channel_id.parse::()?); chan.send_message(&ctx, |m| { m.content(format!("Message was flagged for review (using rule {})", f.id)); m.components(|c| { c.create_action_row(|r| { r.create_button(|b| { b.style(serenity::ButtonStyle::Primary); b.label("Approve"); b.custom_id("approve"); b }); r.create_button(|b| { b.style(serenity::ButtonStyle::Danger); b.label("Deny"); b.custom_id("deny"); b }); r }); c }); m.embed(|e| { e.author(|a| { a.name(new_message.clone().author.name); a.icon_url(new_message.clone().author.face()) }); e.description(content.clone()); e.footer(|f| { f.text(format!("{}:{}", new_message.channel_id.0, new_message.id.0.to_string())) }); e.field("Link", new_message.link().clone(), true) }) }).await?; } else { let guild = new_message.guild(&ctx).unwrap(); let owner = guild.owner_id; let dm_chan = owner.create_dm_channel(&ctx.http).await?; dm_chan.say(&ctx.http, format!("Hi! I tried to flag a message for review in {}, but you haven't set a review channel yet! Set one now by running `filter channel` in the server.", guild.name.clone())).await?; } }, FilterAction::Delete => { new_message.delete(&ctx.http).await?; } } } } } }, poise::Event::InteractionCreate { interaction } => { match interaction { Interaction::MessageComponent(mci) => { let data = &mci.data; let mci2 = mci.clone(); match data.custom_id.as_str() { "approve" => { let msg_report = mci2.message.clone(); let footer = msg_report.embeds[0].clone().footer.unwrap().text; let mut footer = footer.split(':'); let orig_channel_id = footer.next().unwrap(); let orig_msg_id = footer.next().unwrap(); let orig_msg_id = MessageId(orig_msg_id.parse::()?); let orig_channel_id = ChannelId(orig_channel_id.parse::()?); let orig_msg = ctx.http.get_message(orig_channel_id.0, orig_msg_id.0).await?; disable_buttons(&ctx, mci2).await?; }, "deny" => { let msg_report = mci2.message.clone(); let footer = msg_report.embeds[0].clone().footer.unwrap().text; let mut footer = footer.split(':'); let orig_channel_id = footer.next().unwrap(); let orig_msg_id = footer.next().unwrap(); let orig_msg_id = MessageId(orig_msg_id.parse::()?); let orig_channel_id = ChannelId(orig_channel_id.parse::()?); let orig_msg = ctx.http.get_message(orig_channel_id.0, orig_msg_id.0).await?; orig_msg.delete(&ctx).await?; disable_buttons(&ctx, mci2).await?; }, _ => (), }; }, _ => () }; }, _ => (), } } Ok(()) } async fn disable_buttons(ctx: &serenity::Context, mut mci: MessageComponentInteraction) -> Result<(), Error> { mci.message.edit(&ctx, |m| { m.components(|c| { c.create_action_row(|r| { r.create_button(|b| { b.style(serenity::ButtonStyle::Primary); b.label("Approve"); b.custom_id("approve"); b.disabled(true); b }); r.create_button(|b| { b.style(serenity::ButtonStyle::Danger); b.label("Deny"); b.custom_id("deny"); b.disabled(true); b }); r }); c }); m }).await?; ctx.http.create_interaction_response(mci.id.0, &mci.token, &json!({ "type": 7})).await?; Ok(()) }