Prepare architecture for custom Runtime

This commit is contained in:
Victor Mignot 2023-01-29 18:27:07 +01:00
parent b34da70c8b
commit de1eb03b13
No known key found for this signature in database
GPG key ID: FFE4EF056FB5E0D0
5 changed files with 138 additions and 103 deletions

View file

@ -1,3 +1,5 @@
use std::sync::{Arc, Mutex};
use serenity::{prelude::GatewayIntents, Client as SerenityClient}; use serenity::{prelude::GatewayIntents, Client as SerenityClient};
use crate::{database::Client as DatabaseClient, environment::get_env_variable}; use crate::{database::Client as DatabaseClient, environment::get_env_variable};
@ -6,18 +8,19 @@ use crate::discord::event_handler::Handler;
pub struct Client { pub struct Client {
serenity_client: SerenityClient, serenity_client: SerenityClient,
database_client: Arc<Mutex<DatabaseClient>>,
} }
impl Client { impl Client {
pub async fn new() -> Self { pub async fn new() -> Self {
let mut database_client = DatabaseClient::new(); let database_client = Arc::new(Mutex::new(DatabaseClient::new()));
database_client.connect().await; database_client.clone().lock().unwrap().connect();
let discord_token = get_env_variable("DISCORD_TOKEN"); let discord_token = get_env_variable("DISCORD_TOKEN");
let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT; let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT;
let event_handler = Handler { let event_handler = Handler {
database: database_client, database: database_client.clone(),
}; };
let serenity_client = match SerenityClient::builder(discord_token, intents) let serenity_client = match SerenityClient::builder(discord_token, intents)
@ -28,7 +31,10 @@ impl Client {
Err(e) => panic!("Failed to instantiate Discord Client: {e}"), Err(e) => panic!("Failed to instantiate Discord Client: {e}"),
}; };
Client { serenity_client } Client {
serenity_client,
database_client,
}
} }
pub async fn start(&mut self) { pub async fn start(&mut self) {

View file

@ -1,5 +1,6 @@
pub mod bulk_create_tag; mod bulk_create_tag;
pub mod create_tag; pub mod commands;
pub mod delete_tag; mod create_tag;
pub mod list_tags; mod delete_tag;
pub mod source_code; mod list_tags;
mod source_code;

View file

@ -0,0 +1,49 @@
use serenity::{
async_trait,
builder::CreateInteractionResponseData,
model::prelude::{
command::{Command, CommandOptionType},
interaction::application_command::ApplicationCommandInteraction,
},
prelude::Context,
};
use crate::database::Client as DatabaseClient;
pub struct BotCommandOption {
pub name: String,
pub description: String,
pub kind: CommandOptionType,
pub required: bool,
}
#[async_trait]
pub trait BotCommand {
fn new(context: ApplicationCommandInteraction) -> Self;
fn name() -> String;
fn description() -> String;
fn options_list() -> Vec<BotCommandOption>;
async fn run(&self, response: &mut CreateInteractionResponseData, database: &DatabaseClient);
async fn register(context: &Context) {
match Command::create_global_application_command(context, |command| {
let mut new_command = command.name(Self::name()).description(Self::description());
for opt in Self::options_list() {
new_command = new_command.create_option(|option| {
option
.name(opt.name)
.description(opt.description)
.kind(opt.kind)
.required(opt.required)
});
}
new_command
})
.await
{
Ok(_) => println!("Successfully registered the {} command", Self::name()),
Err(_) => panic!("Failed to register the {} command", Self::name()),
};
}
}

View file

@ -1,7 +1,8 @@
use mongodb::bson::doc; use mongodb::bson::doc;
use serenity::{ use serenity::{
builder::{CreateApplicationCommand, CreateInteractionResponseData}, async_trait,
builder::CreateInteractionResponseData,
model::{ model::{
application::interaction::application_command::ApplicationCommandInteraction, application::interaction::application_command::ApplicationCommandInteraction,
prelude::{ prelude::{
@ -12,25 +13,38 @@ use serenity::{
use crate::database::{models::Tag, Client as DatabaseClient}; use crate::database::{models::Tag, Client as DatabaseClient};
pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { use super::commands::{BotCommand, BotCommandOption};
command
.name("create_tag") struct CreateTagCommand {
.description("Add a new tag") context: ApplicationCommandInteraction,
.create_option(|option| {
option
.name("tag")
.description("The tag to create")
.kind(CommandOptionType::String)
.required(true)
})
} }
pub async fn run<'a, 'b>( #[async_trait]
response: &'a mut CreateInteractionResponseData<'b>, impl BotCommand for CreateTagCommand {
command: &ApplicationCommandInteraction, fn name() -> String {
database: &DatabaseClient, String::from("create_tag")
) -> &'a mut CreateInteractionResponseData<'b> { }
let arg = command
fn description() -> String {
String::from("Create a new tag")
}
fn options_list() -> Vec<BotCommandOption> {
vec![BotCommandOption {
name: String::from("tag"),
description: String::from("The tag to create"),
kind: CommandOptionType::String,
required: true,
}]
}
fn new(context: ApplicationCommandInteraction) -> Self {
CreateTagCommand { context }
}
async fn run(&self, response: &mut CreateInteractionResponseData, database: &DatabaseClient) {
let arg = self
.context
.data .data
.options .options
.get(0) .get(0)
@ -39,7 +53,8 @@ pub async fn run<'a, 'b>(
.as_ref() .as_ref()
.expect("Could not deserialize option"); .expect("Could not deserialize option");
let guild_id = command let guild_id = self
.context
.guild_id .guild_id
.expect("Could not fetch guild id") .expect("Could not fetch guild id")
.to_string(); .to_string();
@ -67,6 +82,5 @@ pub async fn run<'a, 'b>(
}; };
} }
} }
}
response
} }

View file

@ -1,23 +1,17 @@
use std::sync::{Arc, Mutex};
use crate::database::Client as DatabaseClient; use crate::database::Client as DatabaseClient;
use serenity::{ use serenity::{
async_trait, async_trait,
model::gateway::Ready, model::gateway::Ready,
model::{ model::prelude::{interaction::Interaction, ResumedEvent},
application::command::Command,
prelude::{
interaction::{Interaction, InteractionResponseType},
ResumedEvent,
},
},
prelude::{Context, EventHandler}, prelude::{Context, EventHandler},
}; };
use super::commands::*;
const MAX_ARGS_NUMBER: u32 = 25; const MAX_ARGS_NUMBER: u32 = 25;
pub struct Handler { pub struct Handler {
pub database: DatabaseClient, pub database: Arc<Mutex<DatabaseClient>>,
} }
#[async_trait] #[async_trait]
@ -25,21 +19,7 @@ impl EventHandler for Handler {
async fn ready(&self, ctx: Context, ready: Ready) { async fn ready(&self, ctx: Context, ready: Ready) {
println!("Successfully connected as {}", ready.user.name); println!("Successfully connected as {}", ready.user.name);
match Command::set_global_application_commands(&ctx.http, |commands| { // TODO: Register commands
commands
.create_application_command(|command| source_code::register(command))
.create_application_command(|command| create_tag::register(command))
.create_application_command(|command| {
bulk_create_tag::register(command, MAX_ARGS_NUMBER)
})
.create_application_command(|command| delete_tag::register(command))
.create_application_command(|command| list_tags::register(command))
})
.await
{
Ok(_) => println!("Successfully registered application commands"),
Err(e) => println!("Failed to register application commands: {e}"),
};
} }
async fn resume(&self, _: Context, _: ResumedEvent) { async fn resume(&self, _: Context, _: ResumedEvent) {
@ -49,21 +29,6 @@ impl EventHandler for Handler {
async fn interaction_create(&self, ctx: Context, interaction: Interaction) { async fn interaction_create(&self, ctx: Context, interaction: Interaction) {
if let Interaction::ApplicationCommand(command) = interaction { if let Interaction::ApplicationCommand(command) = interaction {
println!("Received command {}", command.data.name); println!("Received command {}", command.data.name);
if let Err(e) = command
.create_interaction_response(&ctx.http, |response| {
response
.kind(InteractionResponseType::ChannelMessageWithSource)
.interaction_response_data(|message| match command.data.name.as_str() {
"source_code" => source_code::run(message),
"create_tag" => create_tag::run(message, &command, &self.database),
_ => message.content("Not yet implemented"),
})
})
.await
{
println!("Failed to answer to command: {e}");
}
} }
} }
} }