From 2bccd50b7a11dda0ae239ce13efcdac118bfee92 Mon Sep 17 00:00:00 2001 From: Fuwn Date: Thu, 24 Jun 2021 15:06:43 -0700 Subject: feat(dos-bot): :star: --- src/commands.rs | 40 +++++++++++++++ src/config.rs | 32 ++++++++++++ src/main.rs | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 228 insertions(+) create mode 100644 src/commands.rs create mode 100644 src/config.rs create mode 100644 src/main.rs (limited to 'src') diff --git a/src/commands.rs b/src/commands.rs new file mode 100644 index 0000000..086b6c3 --- /dev/null +++ b/src/commands.rs @@ -0,0 +1,40 @@ +// Copyright (C) 2021-2021 Fuwn +// SPDX-License-Identifier: GPL-3.0-only + +use serenity::{ + framework::standard::{macros::command, CommandResult}, + model::prelude::*, + prelude::*, +}; + +#[command] +pub async fn ping(ctx: &Context, msg: &Message) -> CommandResult { + msg.channel_id.say(&ctx.http, "Pong!").await?; + + Ok(()) +} + +#[command] +pub async fn help(ctx: &Context, msg: &Message) -> CommandResult { + msg + .channel_id + .say( + &ctx.http, + r#"``` + commands + ======== + +help - you are here +ping - pong! + + information + =========== + +- https://github.com/fuwn/dos-bot +- https://discord.io/assembly +```"#, + ) + .await?; + + Ok(()) +} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..afdba2d --- /dev/null +++ b/src/config.rs @@ -0,0 +1,32 @@ +// Copyright (C) 2021-2021 Fuwn +// SPDX-License-Identifier: GPL-3.0-only + +use config::ConfigError; +use serde_derive::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct RolesRole { + pub message: u64, + pub role: u64, +} +#[derive(Serialize, Deserialize, Debug)] +pub struct Config { + pub token: String, + pub roles: Vec, +} +impl Config { + #[allow(future_prelude_collision)] + fn new() -> Result { + let mut c = config::Config::default(); + + c.merge(config::File::with_name(".dos-bot/config.json")) + .expect("unable to access configuration file"); + + c.try_into() + } + + /// # Panics + /// if the configuration file is unable to be accessed. + #[must_use] + pub fn get() -> Self { Self::new().unwrap() } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..333f9f9 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,156 @@ +// Copyright (C) 2021-2021 Fuwn +// SPDX-License-Identifier: GPL-3.0-only + +#![feature( + type_ascription, + hash_set_entry, + type_name_of_val, + decl_macro, + proc_macro_hygiene +)] +#![deny( + warnings, + nonstandard_style, + unused, + future_incompatible, + rust_2018_idioms, + unsafe_code +)] +#![deny(clippy::all, clippy::nursery, clippy::pedantic)] +#![recursion_limit = "128"] +#![doc( + html_logo_url = "https://cdn.discordapp.com/icons/854071194453671976/bc8c80e4bcfa66ecd55da8ceaa80\ + f5a8.webp?size=128", + html_favicon_url = "https://cdn.discordapp.com/icons/854071194453671976/bc8c80e4bcfa66ecd55da8cea\ + a80f5a8.webp?size=128" +)] + +pub mod commands; +pub mod config; + +#[macro_use] +extern crate log; + +use std::{collections::HashSet, sync::Arc}; + +#[allow(clippy::wildcard_imports)] +use commands::*; +use serenity::{ + async_trait, + client::bridge::gateway::{GatewayIntents, ShardManager}, + framework::{standard::macros::group, StandardFramework}, + http::Http, + model::{ + channel::Reaction, + gateway::{Activity, Ready}, + }, + prelude::*, +}; + +use crate::config::Config; + +pub struct ShardManagerContainer; +impl TypeMapKey for ShardManagerContainer { + type Value = Arc>; +} + +struct Handler; +#[async_trait] +impl EventHandler for Handler { + async fn reaction_add(&self, ctx: Context, reaction: Reaction) { + if let Some(guild) = reaction.guild_id { + if let Some(c) = config::Config::get() + .roles + .iter() + .find(|x| x.message == reaction.message_id.0) + { + guild + .member(&ctx, reaction.user_id.expect("unable to locate user id")) + .await + .expect("unable to locate member") + .add_role(&ctx, c.role) + .await + .expect("unable to add role to member"); + } + } + } + + async fn reaction_remove(&self, ctx: Context, reaction: Reaction) { + if let Some(guild) = reaction.guild_id { + if let Some(c) = config::Config::get() + .roles + .iter() + .find(|x| x.message == reaction.message_id.0) + { + guild + .member(&ctx, reaction.user_id.expect("unable to locate user id")) + .await + .expect("unable to locate member") + .remove_role(&ctx, c.role) + .await + .expect("unable to add role to member"); + } + } + } + + async fn ready(&self, ctx: Context, ready: Ready) { + info!("connected to discord gateway as {}", ready.user.name); + + ctx + .set_activity(Activity::watching(">help - discord.io/assembly")) + .await; + } +} + +#[group] +#[commands(ping, help)] +struct General; + +#[tokio::main] +async fn main() { + dotenv::dotenv().ok(); + + std::env::set_var("RUST_LOG", "dos_bot=info"); + pretty_env_logger::init(); + + let http = Http::new_with_token(Config::get().token.as_str()); + + let (owners, _bot_id) = match http.get_current_application_info().await { + Ok(info) => { + let mut owners = HashSet::new(); + owners.insert(info.owner.id); + + (owners, info.id) + } + Err(why) => panic!("could not access application info: {:?}", why), + }; + + let framework = StandardFramework::new() + .configure(|c| c.owners(owners).prefix(">")) + .group(&GENERAL_GROUP); + + let mut client = Client::builder(Config::get().token.as_str()) + .framework(framework) + .event_handler(Handler) + .intents(GatewayIntents::all()) + .await + .expect("error creating dos-bot"); + + { + let mut data = client.data.write().await; + data.insert::(client.shard_manager.clone()); + } + + let shard_manager = client.shard_manager.clone(); + + tokio::spawn(async move { + tokio::signal::ctrl_c() + .await + .expect("could not register ctrl+c handler"); + shard_manager.lock().await.shutdown_all().await; + }); + + if let Err(why) = client.start().await { + error!("client error: {:?}", why); + } +} -- cgit v1.2.3