The world's most clever kitty cat
at main 102 lines 3.0 kB view raw
1use std::{ 2 boxed::Box, 3 sync::{Arc, atomic::Ordering}, 4}; 5 6use log::warn; 7use twilight_model::{ 8 channel::message::{AllowedMentions, MessageFlags, MessageType}, 9 gateway::payload::incoming::MessageCreate, 10 id::{ 11 Id, 12 marker::{ChannelMarker, MessageMarker}, 13 }, 14}; 15 16use crate::{BotContext, prelude::*, status::update_status}; 17 18async fn learn_message(msg: &str, ctx: Arc<BotContext>) -> Result { 19 let mut brain = ctx.brain_handle.write().await; 20 let learned_new_word = brain.ingest(msg); 21 ctx.pending_save.store(true, Ordering::Relaxed); 22 23 if learned_new_word { 24 update_status(&brain, &ctx.shard_sender).context("Failed to update status")?; 25 } 26 27 Ok(()) 28} 29 30async fn reply_message( 31 msg: &str, 32 msg_id: Id<MessageMarker>, 33 channel_id: Id<ChannelMarker>, 34 is_self: bool, 35 ctx: &Arc<BotContext>, 36) -> Result { 37 let (typ_tx, typ_rx) = tokio::sync::oneshot::channel(); 38 let (done_tx, done_rx) = tokio::sync::oneshot::channel(); 39 40 let ctx_typ = ctx.clone(); 41 let typ_id = channel_id; 42 tokio::spawn(async move { 43 if typ_rx.await.ok().is_some_and(|start| start) 44 && let Err(why) = ctx_typ.http.create_typing_trigger(typ_id).await 45 { 46 warn!("Failed to set typing indicator:\n{why:?}"); 47 } 48 done_tx.send(()).ok(); 49 }); 50 51 let brain = ctx.brain_handle.read().await; 52 if let Some(reply_text) = brain.respond(msg, is_self, false, Some(typ_tx)) { 53 drop(brain); 54 done_rx.await.ok(); 55 let allowed_mentions = AllowedMentions::default(); 56 let my_msg = ctx 57 .http 58 .create_message(channel_id) 59 .content(&reply_text) 60 .allowed_mentions(Some(&allowed_mentions)); 61 62 let my_msg = if !is_self { 63 my_msg.reply(msg_id).fail_if_not_exists(false) 64 } else { 65 my_msg 66 }; 67 68 my_msg.await.context("Failed to send message")?; 69 } 70 71 Ok(()) 72} 73 74pub async fn handle_discord_message(msg: Box<MessageCreate>, ctx: Arc<BotContext>) -> Result { 75 let is_self = msg.author.id == ctx.self_id; 76 let is_normal_message = matches!(msg.kind, MessageType::Regular | MessageType::Reply); 77 let is_ephemeral = msg 78 .flags 79 .is_some_and(|flags| flags.contains(MessageFlags::EPHEMERAL)); 80 let is_dm = msg.guild_id.is_none(); 81 82 // Should we consider this message at all? 83 if !is_normal_message || is_ephemeral || is_dm { 84 return Ok(()); 85 } 86 87 // Should Reply to Message? 88 if ctx.reply_channels.contains(&msg.channel_id) { 89 reply_message(&msg.content, msg.id, msg.channel_id, is_self, &ctx) 90 .await 91 .context("Bingus failed to reply to a message")?; 92 } 93 94 // Should we learn from this message? (We don't want to learn from ourselves) 95 if !is_self { 96 learn_message(&msg.content, ctx) 97 .await 98 .context("Bingus failed to learn from a message")?; 99 } 100 101 Ok(()) 102}