commit 8fccaa81ca48f88cbdb1ba2b365aa48001134b92 Author: Lionarius Date: Sat Apr 1 01:53:57 2023 +0300 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5b1c8d6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# Intellij Idea +.idea +mixer_discord_bot.iml + +# Rust +Cargo.lock +target/ + +# Database +data.db diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..169f1eb --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "mixer_discord_bot" +version = "0.1.0" +edition = "2021" + +[dependencies] +tokio = { version = "*", features = ["full"] } +serenity = { version = "*", default-features = false, features = ["client", "gateway", "rustls_backend", "model"] } +sqlx = { version = "*", features = ["runtime-tokio-rustls", "sqlite"] } +sea-orm = { version = "*", features = ["sqlx-sqlite", "runtime-tokio-rustls", "macros"] } +ctrlc = "*" \ No newline at end of file diff --git a/src/bot/commands/mod.rs b/src/bot/commands/mod.rs new file mode 100644 index 0000000..ac65e8e --- /dev/null +++ b/src/bot/commands/mod.rs @@ -0,0 +1,13 @@ +pub mod ping; + +use serenity::builder::CreateApplicationCommand; +use serenity::client::Context; +use serenity::model::application::interaction::application_command::ApplicationCommandInteraction; +use serenity::async_trait; + +#[async_trait] +pub trait MixerCommand: Sync + Send { + fn name(&self) -> String; + fn create(&self, command: &mut CreateApplicationCommand); + async fn execute(&self, ctx: &Context, interaction: ApplicationCommandInteraction) -> serenity::Result<()>; +} \ No newline at end of file diff --git a/src/bot/commands/ping.rs b/src/bot/commands/ping.rs new file mode 100644 index 0000000..715ab48 --- /dev/null +++ b/src/bot/commands/ping.rs @@ -0,0 +1,38 @@ +use serenity::builder::CreateApplicationCommand; +use serenity::client::Context; +use serenity::model::application::interaction::{ + application_command::ApplicationCommandInteraction, + InteractionResponseType +}; +use serenity::async_trait; +use crate::bot::commands::MixerCommand; + +#[derive(Clone)] +pub struct Ping; + +#[async_trait] +impl MixerCommand for Ping { + fn name(&self) -> String { + "ping".to_string() + } + + fn create(&self, command: &mut CreateApplicationCommand) { + command.name(self.name()).description("Hello world!"); + } + + async fn execute(&self, ctx: &Context, interaction: ApplicationCommandInteraction) -> serenity::Result<()> { + let content = "Pong!"; + interaction.create_interaction_response(&ctx.http, |response| { + response.kind(InteractionResponseType::ChannelMessageWithSource) + .interaction_response_data(|message| { + message.content(content) + }) + }).await?; + + println!("{:#?}", interaction.get_interaction_response(&ctx.http).await?); + + println!("Interacted"); + + Ok(()) + } +} \ No newline at end of file diff --git a/src/bot/handler.rs b/src/bot/handler.rs new file mode 100644 index 0000000..5a19099 --- /dev/null +++ b/src/bot/handler.rs @@ -0,0 +1,41 @@ +use serenity::client::{Context, EventHandler}; +use serenity::model::gateway::Ready; +use serenity::async_trait; +use serenity::model::application::command::Command; +use serenity::model::application::interaction::Interaction; +use crate::bot::MixerBotContainer; + +pub struct Handler; + +#[async_trait] +impl EventHandler for Handler { + async fn ready(&self, ctx: Context, data_about_bot: Ready) { + println!("{} is connected!", data_about_bot.user.name); + + let data = ctx.data.read().await; + let bot_commands = &data.get::().unwrap().read().await.commands; + Command::set_global_application_commands(&ctx.http, |commands| { + for cmd in bot_commands.values() { + commands.create_application_command(|command| { + cmd.create(command); + command + }); + println!("Registered command \"{}\"", cmd.name()) + } + commands + }).await.unwrap(); + } + + async fn interaction_create(&self, ctx: Context, interaction: Interaction) { + let data = ctx.data.read().await; + let bot_commands = &data.get::().unwrap().read().await.commands; + + match interaction { + Interaction::ApplicationCommand(command) => + if let Some(mixer_command) = bot_commands.get(&command.data.name) { + mixer_command.execute(&ctx, command).await.unwrap() + } + _ => {} + } + } +} \ No newline at end of file diff --git a/src/bot/mod.rs b/src/bot/mod.rs new file mode 100644 index 0000000..74ba4b5 --- /dev/null +++ b/src/bot/mod.rs @@ -0,0 +1,76 @@ +pub mod commands; +mod handler; + +use std::collections::HashMap; +use std::sync::Arc; +use serenity::{CacheAndHttp, Client}; +use serenity::client::bridge::gateway::ShardManager; +use serenity::prelude::{GatewayIntents, TypeMap, TypeMapKey}; +use tokio::sync::{Mutex, RwLock}; +use crate::bot::commands::MixerCommand; +use crate::bot::handler::Handler; + +pub struct MixerBot { + token: String, + commands: HashMap>, +} + +struct ShardManagerContainer; +struct MixerBotContainer; + + +impl TypeMapKey for ShardManagerContainer { + type Value = Arc>; +} + +impl TypeMapKey for MixerBotContainer { + type Value = Arc>; +} + + +impl MixerBot { + pub fn new(token: String) -> Self { + Self { + token, + commands: HashMap::new(), + } + } + + pub async fn start(self) -> serenity::Result<()> { + let mut client = Client::builder(&self.token, GatewayIntents::empty()).event_handler(Handler).await?; + + let bot; + { + let mut data = client.data.write().await; + data.insert::(client.shard_manager.clone()); + data.insert::(Arc::new(RwLock::new(self))); + bot = data.get::().unwrap().clone(); + } + + let shard_manager = client.shard_manager.clone(); + let cache_and_http = client.cache_and_http.clone(); + let data = client.data.clone(); + tokio::spawn(async move { + tokio::signal::ctrl_c().await.expect("Could not register ctrl+c handler"); + + bot.write().await.shutdown(data, cache_and_http).await; + + shard_manager.lock().await.shutdown_all().await; + }); + + + client.start().await?; + + Ok(()) + } + + pub fn add_command(&mut self, command: Box) -> &mut Self { + self.commands.insert(command.name(), command); + self + } + + pub async fn shutdown(&self, data: Arc>, cache_and_http: Arc) { + println!("{:#?}", cache_and_http.http); + println!("Bot has been shutdown."); + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..737dfa1 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,17 @@ +mod bot; + +use crate::bot::commands::ping::Ping; +use crate::bot::MixerBot; + +#[tokio::main] +async fn main() -> serenity::Result<()> { + let mut bot = MixerBot::new( + "NTE2MzMyMzM2NzQ5NzQwMDUz.GiLPzQ.j5gIUGqx6vF6CFhJv8yizksDi-dOBqCvxR32EE".to_string() + ); + + bot.add_command(Box::new(Ping)); + + bot.start().await?; + + Ok(()) +}