Responding to chat pings
This commit is contained in:
parent
427afa921d
commit
2559ef4452
188
src/chatbot.rs
188
src/chatbot.rs
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue