yorokobot/src/discord/event_handler.rs

195 lines
7 KiB
Rust

use log::{debug, info, warn};
use std::{collections::HashSet, sync::Arc};
use tokio::sync::Mutex;
use super::commands::{
BotCommand, CreateTagCommand, ListTagCommand, SourceCodeCommand, TagNotifyCommand,
};
use crate::{
database::{models::Guild, Client as DatabaseClient},
discord::commands::{commons::CommandExecutionError, DeleteTagCommand, SubscribeCommand},
};
use serenity::{
async_trait,
model::gateway::Ready,
model::prelude::{
interaction::{
application_command::ApplicationCommandInteraction, Interaction,
InteractionResponseType,
},
ResumedEvent, UserId,
},
prelude::{Context, EventHandler},
};
async fn answer_with_error(ctx: &Context, interaction: &ApplicationCommandInteraction) {
const ERROR_MSG: &str = "Internal error while executing your command.";
let result = match interaction.get_interaction_response(&ctx).await {
Ok(mut m) => {
m.edit(&ctx, |msg| msg.suppress_embeds(true).content(ERROR_MSG))
.await
}
Err(_) => {
interaction
.create_interaction_response(&ctx, |msg| {
msg.kind(InteractionResponseType::ChannelMessageWithSource)
.interaction_response_data(|c| c.content(ERROR_MSG))
})
.await
}
};
if let Err(e) = result {
warn!("Could not reply to user with error message: {e:#?}");
}
}
pub struct Handler {
pub database: Arc<DatabaseClient>,
pub users_with_running_selector: Arc<Mutex<HashSet<UserId>>>,
}
impl Handler {
async fn reset_selector_list(&self) {
self.users_with_running_selector.lock().await.clear();
debug!("List of user with running selector reset");
}
}
#[async_trait]
impl EventHandler for Handler {
async fn ready(&self, ctx: Context, ready: Ready) {
// Unregister all application commands before registering them again
if let Ok(commands) = ctx.http.get_global_application_commands().await {
for command in commands {
match ctx
.http
.delete_global_application_command(*command.id.as_u64())
.await
{
Ok(_) => debug!("Successfully unregistered {} command.", command.name),
Err(_) => debug!("Failed to unregister {} command", command.name),
}
}
}
info!("Successfully connected as {}", ready.user.name);
CreateTagCommand::register(&ctx).await;
SourceCodeCommand::register(&ctx).await;
ListTagCommand::register(&ctx).await;
DeleteTagCommand::register(&ctx).await;
TagNotifyCommand::register(&ctx).await;
SubscribeCommand::register(&ctx).await;
}
async fn resume(&self, _: Context, _: ResumedEvent) {
self.reset_selector_list().await;
info!("Successfully reconnected.")
}
async fn interaction_create(&self, ctx: Context, interaction: Interaction) {
let mut failed_command = false;
if let Interaction::ApplicationCommand(command) = interaction {
info!("Received command {}", command.data.name);
if let Some(guild_id) = command.guild_id {
if let Ok(None) = self
.database
.get_by_id::<Guild>(&guild_id.to_string())
.await
{
let new_guild = Guild {
id: guild_id.to_string(),
tags: vec![],
};
match self.database.insert_one(new_guild).await {
Ok(()) => info!("Unregistered guild: Adding it to the database"),
Err(()) => {
warn!("Error adding a new guild in the database");
failed_command = true;
}
};
}
}
if failed_command {
answer_with_error(&ctx, &command).await;
return;
}
let command_result = match command.data.name.as_str() {
"create_tag" => {
CreateTagCommand::new(command.clone())
.run(ctx.clone(), self.database.clone())
.await
}
"about" => {
SourceCodeCommand::new(command.clone())
.run(ctx.clone(), self.database.clone())
.await
}
"list_tags" => {
ListTagCommand::new(command.clone())
.run(ctx.clone(), self.database.clone())
.await
}
"delete_tag" => {
DeleteTagCommand::new(command.clone())
.run(ctx.clone(), self.database.clone())
.await
}
"notify" => {
let mut users_selector = self.users_with_running_selector.lock().await;
if !users_selector.contains(&command.user.id) {
users_selector.insert(command.user.id);
drop(users_selector);
TagNotifyCommand::new(command.clone())
.run(ctx.clone(), self.database.clone())
.await
} else {
drop(users_selector);
Err(CommandExecutionError::SelectorError(
"User has already a selector running".to_string(),
))
}
}
"subscribe" => {
let mut users_selector = self.users_with_running_selector.lock().await;
if !users_selector.contains(&command.user.id) {
users_selector.insert(command.user.id);
drop(users_selector);
SubscribeCommand::new(command.clone())
.run(ctx.clone(), self.database.clone())
.await
} else {
drop(users_selector);
Err(CommandExecutionError::SelectorError(
"User has already a selector running".to_string(),
))
}
}
_ => Err(CommandExecutionError::UnknownCommand(
"Received an unknown command from Discord".to_string(),
)),
};
if let Err(e) = command_result {
warn!("Error while executing command: {e:#?}");
answer_with_error(&ctx, &command).await;
} else {
let mut users_lock = self.users_with_running_selector.lock().await;
if users_lock.contains(&command.user.id) {
users_lock.remove(&command.user.id);
}
}
}
}
}