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

View file

@ -1,5 +1,6 @@
pub mod bulk_create_tag;
pub mod create_tag;
pub mod delete_tag;
pub mod list_tags;
pub mod source_code;
mod bulk_create_tag;
pub mod commands;
mod create_tag;
mod delete_tag;
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 serenity::{
builder::{CreateApplicationCommand, CreateInteractionResponseData},
async_trait,
builder::CreateInteractionResponseData,
model::{
application::interaction::application_command::ApplicationCommandInteraction,
prelude::{
@ -12,61 +13,74 @@ use serenity::{
use crate::database::{models::Tag, Client as DatabaseClient};
pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand {
command
.name("create_tag")
.description("Add a new tag")
.create_option(|option| {
option
.name("tag")
.description("The tag to create")
.kind(CommandOptionType::String)
.required(true)
})
use super::commands::{BotCommand, BotCommandOption};
struct CreateTagCommand {
context: ApplicationCommandInteraction,
}
pub async fn run<'a, 'b>(
response: &'a mut CreateInteractionResponseData<'b>,
command: &ApplicationCommandInteraction,
database: &DatabaseClient,
) -> &'a mut CreateInteractionResponseData<'b> {
let arg = command
.data
.options
.get(0)
.expect("Missing option")
.resolved
.as_ref()
.expect("Could not deserialize option");
let guild_id = command
.guild_id
.expect("Could not fetch guild id")
.to_string();
if let CommandDataOptionValue::String(tag_name) = arg {
let matching_tags = database
.get_all::<Tag>(Some(doc! {"name": tag_name, "guild_id": guild_id.as_str()}))
.await;
if !matching_tags.is_empty() {
response.content("This tag already exist for this server");
} else {
match database
.insert_one(Tag {
id: None,
name: tag_name.to_string(),
guild_id: guild_id.clone(),
is_nsfw: false,
subscribers: vec![],
})
.await
{
Ok(_) => response.content("Tag successfully created."),
Err(_) => response.content("Error creating the tag"),
};
}
#[async_trait]
impl BotCommand for CreateTagCommand {
fn name() -> String {
String::from("create_tag")
}
response
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
.options
.get(0)
.expect("Missing option")
.resolved
.as_ref()
.expect("Could not deserialize option");
let guild_id = self
.context
.guild_id
.expect("Could not fetch guild id")
.to_string();
if let CommandDataOptionValue::String(tag_name) = arg {
let matching_tags = database
.get_all::<Tag>(Some(doc! {"name": tag_name, "guild_id": guild_id.as_str()}))
.await;
if !matching_tags.is_empty() {
response.content("This tag already exist for this server");
} else {
match database
.insert_one(Tag {
id: None,
name: tag_name.to_string(),
guild_id: guild_id.clone(),
is_nsfw: false,
subscribers: vec![],
})
.await
{
Ok(_) => response.content("Tag successfully created."),
Err(_) => response.content("Error creating the tag"),
};
}
}
}
}

View file

@ -1,23 +1,17 @@
use std::sync::{Arc, Mutex};
use crate::database::Client as DatabaseClient;
use serenity::{
async_trait,
model::gateway::Ready,
model::{
application::command::Command,
prelude::{
interaction::{Interaction, InteractionResponseType},
ResumedEvent,
},
},
model::prelude::{interaction::Interaction, ResumedEvent},
prelude::{Context, EventHandler},
};
use super::commands::*;
const MAX_ARGS_NUMBER: u32 = 25;
pub struct Handler {
pub database: DatabaseClient,
pub database: Arc<Mutex<DatabaseClient>>,
}
#[async_trait]
@ -25,21 +19,7 @@ impl EventHandler for Handler {
async fn ready(&self, ctx: Context, ready: Ready) {
println!("Successfully connected as {}", ready.user.name);
match Command::set_global_application_commands(&ctx.http, |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}"),
};
// TODO: Register commands
}
async fn resume(&self, _: Context, _: ResumedEvent) {
@ -49,21 +29,6 @@ impl EventHandler for Handler {
async fn interaction_create(&self, ctx: Context, interaction: Interaction) {
if let Interaction::ApplicationCommand(command) = interaction {
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}");
}
}
}
}