Responding to chat pings

This commit is contained in:
Daniel Flanagan 2024-04-12 11:59:46 -05:00
parent 427afa921d
commit 2559ef4452

View file

@ -6,20 +6,21 @@
use std::{any::Any, future}; use std::{any::Any, future};
use discord::{ use discord::{
model::{Event, Message}, model::{ChannelId, Event, Message, ReadyEvent},
Discord, Discord as DDiscord,
}; };
use tokio::sync::Mutex;
use crate::prelude::*; const COMMAND_PREFIX: &str = "!";
pub async fn start(conf: Arc<Config>) -> Result<()> { struct Discord {
if conf.discord.is_none() { discord: DDiscord,
warn!("Chatbot starting without Discord token. Nothing will happen."); connection: Option<Arc<Mutex<discord::Connection>>>,
// wait forever
future::pending::<()>().await;
} }
let discord = Discord::from_bot_token( impl Discord {
pub fn try_new(conf: Arc<Config>) -> Result<Self> {
let discord = DDiscord::from_bot_token(
conf.discord conf.discord
.as_ref() .as_ref()
.unwrap() .unwrap()
@ -27,12 +28,31 @@ pub async fn start(conf: Arc<Config>) -> Result<()> {
.clone() .clone()
.expose_secret(), .expose_secret(),
)?; )?;
Ok(Self {
discord,
connection: None,
})
}
let (mut conn, ready_ev) = discord.connect()?; pub async fn connect(&mut self) -> Result<ReadyEvent> {
info!("Discord connection ready: {ready_ev:?}"); if self.connection.is_some() {
return Err(anyhow::anyhow!("already connected"));
}
let (connection, ready_ev) = self.discord.connect()?;
self.connection = Some(Arc::new(Mutex::new(connection)));
Ok(ready_ev)
}
pub async fn handle_commands(self: Arc<Self>) -> Result<()> {
if let Some(conn) = self.connection.clone() {
loop {
// TODO: I'm suspicious this will just always be locked here and
// I'm doing something stupid.
let mut conn = conn.lock().await;
while let Ok(event) = conn.recv_event() { while let Ok(event) = conn.recv_event() {
let rself = self.clone();
tokio::spawn(async move { tokio::spawn(async move {
match handle_event(event).await { match rself.handle_event(event).await {
Ok(_) => {} Ok(_) => {}
Err(err) => { Err(err) => {
error!("Failed to handle Discord event: {err}") error!("Failed to handle Discord event: {err}")
@ -40,16 +60,18 @@ pub async fn start(conf: Arc<Config>) -> Result<()> {
} }
}); });
} }
}
}
Ok(()) Ok(())
} }
pub async fn handle_event(event: Event) -> Result<()> { pub async fn handle_event(&self, event: Event) -> Result<()> {
match event { match event {
Event::Ready(_) => return Ok(ignore_event(event)), Event::Ready(_) => return Ok(self.ignore_event(event)),
Event::Resumed { trace: _ } => return Ok(ignore_event(event)), Event::Resumed { trace: _ } => return Ok(self.ignore_event(event)),
Event::UserUpdate(_) => return Ok(ignore_event(event)), Event::UserUpdate(_) => return Ok(self.ignore_event(event)),
Event::UserNoteUpdate(_, _) => return Ok(ignore_event(event)), Event::UserNoteUpdate(_, _) => return Ok(self.ignore_event(event)),
Event::UserSettingsUpdate { Event::UserSettingsUpdate {
detect_platform_accounts: _, detect_platform_accounts: _,
developer_mode: _, developer_mode: _,
@ -65,39 +87,39 @@ pub async fn handle_event(event: Event) -> Result<()> {
theme: _, theme: _,
convert_emoticons: _, convert_emoticons: _,
friend_source_flags: _, friend_source_flags: _,
} => return Ok(ignore_event(event)), } => return Ok(self.ignore_event(event)),
Event::UserServerSettingsUpdate(_) => return Ok(ignore_event(event)), Event::UserServerSettingsUpdate(_) => return Ok(self.ignore_event(event)),
Event::VoiceStateUpdate(_, _) => return Ok(ignore_event(event)), Event::VoiceStateUpdate(_, _) => return Ok(self.ignore_event(event)),
Event::VoiceServerUpdate { Event::VoiceServerUpdate {
server_id: _, server_id: _,
channel_id: _, channel_id: _,
endpoint: _, endpoint: _,
token: _, token: _,
} => return Ok(ignore_event(event)), } => return Ok(self.ignore_event(event)),
Event::CallCreate(_) => return Ok(ignore_event(event)), Event::CallCreate(_) => return Ok(self.ignore_event(event)),
Event::CallUpdate { Event::CallUpdate {
channel_id: _, channel_id: _,
message_id: _, message_id: _,
region: _, region: _,
ringing: _, ringing: _,
} => return Ok(ignore_event(event)), } => return Ok(self.ignore_event(event)),
Event::CallDelete(_) => return Ok(ignore_event(event)), Event::CallDelete(_) => return Ok(self.ignore_event(event)),
Event::ChannelRecipientAdd(_, _) => return Ok(ignore_event(event)), Event::ChannelRecipientAdd(_, _) => return Ok(self.ignore_event(event)),
Event::ChannelRecipientRemove(_, _) => return Ok(ignore_event(event)), Event::ChannelRecipientRemove(_, _) => return Ok(self.ignore_event(event)),
Event::TypingStart { Event::TypingStart {
channel_id: _, channel_id: _,
user_id: _, user_id: _,
timestamp: _, timestamp: _,
} => return Ok(ignore_event(event)), } => return Ok(self.ignore_event(event)),
Event::PresenceUpdate { Event::PresenceUpdate {
presence: _, presence: _,
server_id: _, server_id: _,
roles: _, roles: _,
} => return Ok(ignore_event(event)), } => return Ok(self.ignore_event(event)),
Event::PresencesReplace(_) => return Ok(ignore_event(event)), Event::PresencesReplace(_) => return Ok(self.ignore_event(event)),
Event::RelationshipAdd(_) => return Ok(ignore_event(event)), Event::RelationshipAdd(_) => return Ok(self.ignore_event(event)),
Event::RelationshipRemove(_, _) => return Ok(ignore_event(event)), Event::RelationshipRemove(_, _) => return Ok(self.ignore_event(event)),
Event::MessageCreate(msg) => return Ok(handle_message(msg)), Event::MessageCreate(msg) => return Ok(self.handle_message(msg)?),
Event::MessageUpdate { Event::MessageUpdate {
id: _, id: _,
channel_id: _, channel_id: _,
@ -114,72 +136,112 @@ pub async fn handle_event(event: Event) -> Result<()> {
mention_roles: _, mention_roles: _,
attachments: _, attachments: _,
embeds: _, embeds: _,
} => return Ok(ignore_event(event)), } => return Ok(self.ignore_event(event)),
Event::MessageAck { Event::MessageAck {
channel_id: _, channel_id: _,
message_id: _, message_id: _,
} => return Ok(ignore_event(event)), } => return Ok(self.ignore_event(event)),
Event::MessageDelete { Event::MessageDelete {
channel_id: _, channel_id: _,
message_id: _, message_id: _,
} => return Ok(ignore_event(event)), } => return Ok(self.ignore_event(event)),
Event::MessageDeleteBulk { Event::MessageDeleteBulk {
channel_id: _, channel_id: _,
ids: _, ids: _,
} => return Ok(ignore_event(event)), } => return Ok(self.ignore_event(event)),
Event::ServerCreate(_) => return Ok(ignore_event(event)), Event::ServerCreate(_) => return Ok(self.ignore_event(event)),
Event::ServerUpdate(_) => return Ok(ignore_event(event)), Event::ServerUpdate(_) => return Ok(self.ignore_event(event)),
Event::ServerDelete(_) => return Ok(ignore_event(event)), Event::ServerDelete(_) => return Ok(self.ignore_event(event)),
Event::ServerMemberAdd(_, _) => return Ok(ignore_event(event)), Event::ServerMemberAdd(_, _) => return Ok(self.ignore_event(event)),
Event::ServerMemberUpdate { Event::ServerMemberUpdate {
server_id: _, server_id: _,
roles: _, roles: _,
user: _, user: _,
nick: _, nick: _,
} => return Ok(ignore_event(event)), } => return Ok(self.ignore_event(event)),
Event::ServerMemberRemove(_, _) => return Ok(ignore_event(event)), Event::ServerMemberRemove(_, _) => return Ok(self.ignore_event(event)),
Event::ServerMembersChunk(_, _) => return Ok(ignore_event(event)), Event::ServerMembersChunk(_, _) => return Ok(self.ignore_event(event)),
Event::ServerSync { Event::ServerSync {
server_id: _, server_id: _,
large: _, large: _,
members: _, members: _,
presences: _, presences: _,
} => return Ok(ignore_event(event)), } => return Ok(self.ignore_event(event)),
Event::ServerRoleCreate(_, _) => return Ok(ignore_event(event)), Event::ServerRoleCreate(_, _) => return Ok(self.ignore_event(event)),
Event::ServerRoleUpdate(_, _) => return Ok(ignore_event(event)), Event::ServerRoleUpdate(_, _) => return Ok(self.ignore_event(event)),
Event::ServerRoleDelete(_, _) => return Ok(ignore_event(event)), Event::ServerRoleDelete(_, _) => return Ok(self.ignore_event(event)),
Event::ServerBanAdd(_, _) => return Ok(ignore_event(event)), Event::ServerBanAdd(_, _) => return Ok(self.ignore_event(event)),
Event::ServerBanRemove(_, _) => return Ok(ignore_event(event)), Event::ServerBanRemove(_, _) => return Ok(self.ignore_event(event)),
Event::ServerIntegrationsUpdate(_) => return Ok(ignore_event(event)), Event::ServerIntegrationsUpdate(_) => return Ok(self.ignore_event(event)),
Event::ServerEmojisUpdate(_, _) => return Ok(ignore_event(event)), Event::ServerEmojisUpdate(_, _) => return Ok(self.ignore_event(event)),
Event::ChannelCreate(_) => return Ok(ignore_event(event)), Event::ChannelCreate(_) => return Ok(self.ignore_event(event)),
Event::ChannelUpdate(_) => return Ok(ignore_event(event)), Event::ChannelUpdate(_) => return Ok(self.ignore_event(event)),
Event::ChannelDelete(_) => return Ok(ignore_event(event)), Event::ChannelDelete(_) => return Ok(self.ignore_event(event)),
Event::ChannelPinsAck { Event::ChannelPinsAck {
channel_id: _, channel_id: _,
timestamp: _, timestamp: _,
} => return Ok(ignore_event(event)), } => return Ok(self.ignore_event(event)),
Event::ChannelPinsUpdate { Event::ChannelPinsUpdate {
channel_id: _, channel_id: _,
last_pin_timestamp: _, last_pin_timestamp: _,
} => return Ok(ignore_event(event)), } => return Ok(self.ignore_event(event)),
Event::ReactionAdd(_) => return Ok(ignore_event(event)), Event::ReactionAdd(_) => return Ok(self.ignore_event(event)),
Event::ReactionRemove(_) => return Ok(ignore_event(event)), Event::ReactionRemove(_) => return Ok(self.ignore_event(event)),
Event::Unknown(_, _) => return Ok(ignore_event(event)), Event::Unknown(_, _) => return Ok(self.ignore_event(event)),
_ => return Ok(ignore_event(event)), _ => return Ok(self.ignore_event(event)),
} }
} }
pub fn ignore_event(event: Event) -> () { pub fn ignore_event(&self, event: Event) -> () {
let event_type_id = event.type_id(); let event_type_id = event.type_id();
info!("Ignoring Discord event of type: {event_type_id:?}") info!("Ignoring Discord event of type: {event_type_id:?}")
} }
pub fn handle_message(msg: Message) -> () { pub fn handle_message(&self, msg: Message) -> Result<()> {
info!( info!(
"Recieved Discord message in channel {} with content: {}", "Recieved Discord message in channel {} with content: {}",
msg.channel_id, msg.content msg.channel_id, msg.content
); );
return (); let text = msg.content.clone();
let trimmed_text = text.trim();
if trimmed_text.starts_with(COMMAND_PREFIX) {
self.handle_text_command(trimmed_text, msg)?
}
Ok(())
}
fn send_message(&self, channel: ChannelId, text: &str) -> Result<Message> {
self.discord
.send_message(channel, text, "", false)
.map_err(|e| e.into())
}
fn handle_text_command(&self, trimmed_text: &str, msg: Message) -> Result<()> {
let trimmed_text = trimmed_text.trim_start_matches(COMMAND_PREFIX);
match trimmed_text {
s if s.starts_with("ping") => {
let _ = self.send_message(msg.channel_id, "`pong`")?;
}
_ => {}
}
Ok(())
}
}
use crate::prelude::*;
pub async fn start(conf: Arc<Config>) -> Result<()> {
if conf.discord.is_none() {
warn!("Chatbot starting without Discord token. Nothing will happen.");
// wait forever
future::pending::<()>().await;
}
let mut discord = Discord::try_new(conf.clone())?;
let ready_ev = discord.connect().await;
info!("Discord connection ready: {ready_ev:?}");
let adiscord = Arc::new(discord);
adiscord.handle_commands().await
} }