initial commit

This commit is contained in:
Lionarius
2023-04-01 01:53:57 +03:00
commit 8fccaa81ca
7 changed files with 206 additions and 0 deletions

10
.gitignore vendored Normal file
View File

@@ -0,0 +1,10 @@
# Intellij Idea
.idea
mixer_discord_bot.iml
# Rust
Cargo.lock
target/
# Database
data.db

11
Cargo.toml Normal file
View File

@@ -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 = "*"

13
src/bot/commands/mod.rs Normal file
View File

@@ -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<()>;
}

38
src/bot/commands/ping.rs Normal file
View File

@@ -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(())
}
}

41
src/bot/handler.rs Normal file
View File

@@ -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::<MixerBotContainer>().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::<MixerBotContainer>().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()
}
_ => {}
}
}
}

76
src/bot/mod.rs Normal file
View File

@@ -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<String, Box<dyn MixerCommand>>,
}
struct ShardManagerContainer;
struct MixerBotContainer;
impl TypeMapKey for ShardManagerContainer {
type Value = Arc<Mutex<ShardManager>>;
}
impl TypeMapKey for MixerBotContainer {
type Value = Arc<RwLock<MixerBot>>;
}
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::<ShardManagerContainer>(client.shard_manager.clone());
data.insert::<MixerBotContainer>(Arc::new(RwLock::new(self)));
bot = data.get::<MixerBotContainer>().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<dyn MixerCommand>) -> &mut Self {
self.commands.insert(command.name(), command);
self
}
pub async fn shutdown(&self, data: Arc<RwLock<TypeMap>>, cache_and_http: Arc<CacheAndHttp>) {
println!("{:#?}", cache_and_http.http);
println!("Bot has been shutdown.");
}
}

17
src/main.rs Normal file
View File

@@ -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(())
}