195 lines
7 KiB
Rust
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|