diff --git a/src/command/about_role.rs b/src/command/about_role.rs deleted file mode 100644 index 9ab5666..0000000 --- a/src/command/about_role.rs +++ /dev/null @@ -1,33 +0,0 @@ -use super::*; - -#[command] -// Limits the usage of this command to roles named: -#[allowed_roles("mods", "ultimate neko")] -async fn about_role(ctx: &Context, msg: &Message, args: Args) -> CommandResult { - let potential_role_name = args.rest(); - - if let Some(guild) = msg.guild(&ctx.cache).await { - // `role_by_name()` allows us to attempt attaining a reference to a role - // via its name. - if let Some(role) = guild.role_by_name(&potential_role_name) { - if let Err(why) = msg - .channel_id - .say(&ctx.http, &format!("Role-ID: {}", role.id)) - .await - { - println!("Error sending message: {:?}", why); - } - - return Ok(()); - } - } - - msg.channel_id - .say( - &ctx.http, - format!("Could not find role named: {:?}", potential_role_name), - ) - .await?; - - Ok(()) -} \ No newline at end of file diff --git a/src/command/am_i_admin.rs b/src/command/am_i_admin.rs deleted file mode 100644 index 2eb21a4..0000000 --- a/src/command/am_i_admin.rs +++ /dev/null @@ -1,25 +0,0 @@ -use super::*; - -// We could also use -// #[required_permissions(ADMINISTRATOR)] -// but that would not let us reply when it fails. -#[command] -async fn am_i_admin(ctx: &Context, msg: &Message, _args: Args) -> CommandResult { - if let Some(member) = &msg.member { - for role in &member.roles { - if role - .to_role_cached(&ctx.cache) - .await - .map_or(false, |r| r.has_permission(Permissions::ADMINISTRATOR)) - { - msg.channel_id.say(&ctx.http, "Yes, you are.").await?; - - return Ok(()); - } - } - } - - msg.channel_id.say(&ctx.http, "No, you are not.").await?; - - Ok(()) -} diff --git a/src/command/bashget.rs b/src/command/bashget.rs deleted file mode 100644 index 19c06d4..0000000 --- a/src/command/bashget.rs +++ /dev/null @@ -1,31 +0,0 @@ -use super::*; - -#[command] -async fn bashget(_ctx: &Context, _msg: &Message, _args: Args) -> CommandResult { - if path::Path::new(&_args.rest()).exists() { - _msg.channel_id - .send_message(&_ctx.http, |m| { - // m.content("test"); - // m.tts(true); - - m.embed(|e| { - e.title("Bash command"); - e.description(&_args.rest()); - e.attachment(format!("attachment://{}", &_args.rest())); - - e - }); - - m - }) - .await - .unwrap(); - let paths = vec!["/workspace/dinobot/heh.txt"]; - - _msg.channel_id - .send_files(&_ctx.http, paths, |m| m.content("l")) - .await - .unwrap(); - } - Ok(()) -} diff --git a/src/command/commands.rs b/src/command/commands.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/command/config.rs b/src/command/config.rs deleted file mode 100644 index b090e06..0000000 --- a/src/command/config.rs +++ /dev/null @@ -1,118 +0,0 @@ -use serenity::model::id::ChannelId; - -use super::*; - -// pub struct QuestionChannels { -// pub id: ChannelId, -// } - -impl Db { - pub async fn set_watch_channels( - &self, - data_name: &str, - id: ChannelId, - _ctx: &Context, - ) -> Result<()> { - let id = id.0 as i64; - - let q = format!("insert into server_config({}) values({})", data_name, id); - if sqlx::query(&q).execute(&self.sqlitedb).await.is_ok() { - // questions_thread::responder(ctx).await; - } - Ok(()) - } - // pub async fn get_question_channels(&self) -> Result> { - // let q = sqlx::query!("select question_channels from server_config") - // .fetch_all(&self.sqlitedb) - // .await? - // .into_iter() - // .map(|x| QuestionChannels { - // id: ChannelId(x.question_channels as u64), - // }) - // .collect(); - - // Ok(q) - // } -} - -// A command can have sub-commands, just like in command lines tools. -// Imagine `cargo help` and `cargo help run`. -#[command("config")] -// #[sub_commands( -// questions_channel, -// introduction_channel, -// subscriber_role, -// getting_started, -// default_role -// )] -#[required_permissions(ADMINISTRATOR)] -async fn config(ctx: &Context, msg: &Message, _args: Args) -> CommandResult { - if _args.is_empty() { - msg.reply(&ctx.http, format!("{} no argument provided", _args.rest())) - .await?; - } else { - let mut arg = Args::new(_args.rest(), &[Delimiter::Single(' ')]); - let config = arg.single_quoted::()?; - let value = arg.single_quoted::()?; - let db = &ctx.get_db().await; - - match config.as_str() { - "question_channels" - | "feedback_channel" - | "getting_started_channel" - | "introduction_channel" - | "showcase_channel" => { - db.set_watch_channels(config.as_str(), value.parse::().unwrap(), ctx) - .await? - } - _ => { - msg.reply( - &ctx.http, - format!("Invalid config parameter: {}", _args.rest()), - ) - .await?; - } - } - } - - Ok(()) -} - -// // This will only be called if preceded by the `upper`-command. -// #[command] -// // #[aliases("sub-command", "secret")] -// #[description("Set the question channels to watch for")] -// async fn questions_channel(ctx: &Context, msg: &Message, _args: Args) -> CommandResult { -// impl Db {} -// let db = &ctx.get_db().await; -// Ok(()) -// } -// #[command] -// // #[aliases("sub-command", "secret")] -// #[description("Set the introduction channel to watch for")] -// async fn introduction_channel(ctx: &Context, msg: &Message, _args: Args) -> CommandResult { -// msg.reply(&ctx.http, "This is a sub function!").await?; - -// Ok(()) -// } -// #[command] -// // #[aliases("sub-command", "secret")] -// #[description("Set the getting started channel to handle")] -// async fn getting_started(ctx: &Context, msg: &Message, _args: Args) -> CommandResult { -// msg.reply(&ctx.http, "This is a sub function!").await?; - -// Ok(()) -// } -// #[command] -// // #[aliases("sub-command", "secret")] -// #[description("Set the subscriber role for users")] -// async fn subscriber_role(ctx: &Context, msg: &Message, _args: Args) -> CommandResult { -// msg.reply(&ctx.http, "This is a sub function!").await?; - -// Ok(()) -// } -// #[command] -// #[description("Set the default role for server members")] -// async fn default_role(ctx: &Context, msg: &Message, _args: Args) -> CommandResult { -// Ok(()) -// } diff --git a/src/command/emoji.rs b/src/command/emoji.rs deleted file mode 100644 index 47ea0f9..0000000 --- a/src/command/emoji.rs +++ /dev/null @@ -1,37 +0,0 @@ -use super::*; - -#[command] -// Adds multiple aliases -#[aliases("kitty", "neko")] -// Make this command use the "emoji" bucket. -#[bucket = "emoji"] -// Allow only administrators to call this: -#[required_permissions("ADMINISTRATOR")] -async fn cat(ctx: &Context, msg: &Message) -> CommandResult { - msg.channel_id.say(&ctx.http, ":cat:").await?; - - // We can return one ticket to the bucket undoing the ratelimit. - Err(RevertBucket.into()) -} - -#[command] -#[description = "Sends an emoji with a dog."] -#[bucket = "emoji"] -async fn dog(ctx: &Context, msg: &Message) -> CommandResult { - msg.channel_id.say(&ctx.http, ":dog:").await?; - - Ok(()) -} - -#[command] -async fn bird(ctx: &Context, msg: &Message, args: Args) -> CommandResult { - let say_content = if args.is_empty() { - ":bird: can find animals for you.".to_string() - } else { - format!(":bird: could not find animal named: `{}`.", args.rest()) - }; - - msg.channel_id.say(&ctx.http, say_content).await?; - - Ok(()) -} diff --git a/src/command/math.rs b/src/command/math.rs deleted file mode 100644 index a2b9647..0000000 --- a/src/command/math.rs +++ /dev/null @@ -1,15 +0,0 @@ -use super::*; - -#[command] -// Lets us also call `~math *` instead of just `~math multiply`. -#[aliases("*")] -async fn multiply(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { - let first = args.single::()?; - let second = args.single::()?; - - let res = first * second; - - msg.channel_id.say(&ctx.http, &res.to_string()).await?; - - Ok(()) -} diff --git a/src/command/mod.rs b/src/command/mod.rs index 5210593..e2f5ace 100644 --- a/src/command/mod.rs +++ b/src/command/mod.rs @@ -3,51 +3,33 @@ mod about; mod av; mod bash; // mod editlog; -mod emoji; mod invite; mod latency; -mod math; // pub mod note; -mod owner_check; -mod ping; -mod say; -mod some_long_command; -mod status; -// mod whois; mod exec; mod index_threads; +mod status; // Import commands use about::*; use av::*; use bash::*; -// use editlog::*; -use emoji::*; +use exec::*; +use index_threads::*; use invite::*; use latency::*; -use math::*; -// use note::*; -use index_threads::*; -use owner_check::*; -use say::*; use status::*; -// use whois::*; -use exec::*; use crate::utils::{parser::Parse, substr::*}; -// use thorne::*; use serenity::{ client::bridge::gateway::{ShardId, ShardManager}, framework::standard::{ - buckets::RevertBucket, help_commands, - macros::{check, command, group, help, hook}, - Args, CommandGroup, CommandOptions, CommandResult, Delimiter, DispatchError, HelpOptions, - Reason, + macros::{command, group, help, hook}, + Args, CommandGroup, CommandResult, Delimiter, DispatchError, HelpOptions, }, model::{channel::Message, id::UserId}, - utils::{content_safe, ContentSafeOptions}, }; use std::{ collections::{HashMap, HashSet}, @@ -56,7 +38,6 @@ use std::{ sync::Arc, }; -use anyhow::Result; use serenity::prelude::*; // use term_grid::{Cell, Direction, Filling, Grid, GridOptions}; use tokio::{process, sync::Mutex}; @@ -77,65 +58,9 @@ impl TypeMapKey for CommandCounter { } #[group] -#[commands( - about, - // editlog, - // am_i_admin, - say, - commands, - bash, - exec, - // bashget, - // ping, - latency, - // whois, - av, - status, - invite, - // some_long_command, - index_threads -)] +#[commands(about, bash, exec, latency, av, status, invite, index_threads)] struct General; -#[group] -// Sets multiple prefixes for a group. -// This requires us to call commands in this group -// via `~emoji` (or `~em`) instead of just `~`. -#[prefixes("emoji", "em")] -// Set a description to appear if a user wants to display a single group -// e.g. via help using the group-name or one of its prefixes. -#[description = "A group with commands providing an emoji as response."] -// Summary only appears when listing multiple groups. -#[summary = "Do emoji fun!"] -// Sets a command that will be executed if only a group-prefix was passed. -#[default_command(bird)] -#[commands(cat, dog)] -struct Emoji; - -// #[group] -// #[prefix = "note"] -// #[description = "A group of commands providing notes keeping functionality"] -// #[summary = "Note autoresponder"] -// #[commands(add, remove, link, list)] -// struct Note; - -#[group] -// Sets a single prefix for this group. -// So one has to call commands in this group -// via `~math` instead of just `~`. -#[prefix = "math"] -#[commands(multiply)] -struct Math; - -// #[group] -// #[owners_only] -// // Limit all commands to be guild-restricted. -// #[only_in(guilds)] -// // Summary only appears when listing multiple groups. -// #[summary = "Server admins only"] -// #[commands(slow_mode)] -// struct Owner; - // The framework provides two built-in help commands for you to use. // But you can also make your own customized help command that forwards // to the behaviour of either of them. diff --git a/src/command/note.rs b/src/command/note.rs deleted file mode 100644 index 68946c7..0000000 --- a/src/command/note.rs +++ /dev/null @@ -1,120 +0,0 @@ -use super::*; - -pub struct Note { - note_name: String, -} - -impl Note { - pub async fn from(note_name: &String) -> Self { - Self { - note_name: note_name.to_string(), - } - } - - pub async fn get_contents(&self) -> String { - let dbnode = Database::from("notes".to_string()).await; - fs::read_to_string(format!("{}/{}", dbnode, &self.note_name)) - .await - .unwrap() - } - - pub async fn save_contents(&self, contents: String) { - let dbnode = Database::from("notes".to_string()).await; - fs::write( - format!("{}/{}", &dbnode.to_string(), &self.note_name), - &contents, - ) - .await - .unwrap(); - } -} - -#[command] -#[required_permissions(ADMINISTRATOR)] -async fn add(_ctx: &Context, _msg: &Message, mut _args: Args) -> CommandResult { - let mut note_name = Args::new(_args.rest(), &[Delimiter::Single(' ')]); - let first_arg = note_name.single_quoted::().unwrap(); - - Note::from(&first_arg) - .await - .save_contents(note_name.rest().to_string()) - .await; - - _msg.reply(&_ctx.http, format!("Note(**{}**) written", &first_arg)) - .await - .unwrap(); - Ok(()) -} - -#[command] -#[aliases("rm")] -#[required_permissions(ADMINISTRATOR)] -async fn remove(_ctx: &Context, _msg: &Message, _args: Args) -> CommandResult { - let mut note_name = Args::new(_args.rest(), &[Delimiter::Single(' ')]); - let first_arg = note_name.single_quoted::().unwrap(); - - fs::remove_file(format!( - "{}/{}", - Database::from("notes".to_string()).await, - &first_arg - )) - .await - .unwrap(); - - _msg.reply(&_ctx.http, format!("Note(**{}**) removed", &first_arg)) - .await - .unwrap(); - - Ok(()) -} - -#[command] -#[required_permissions(ADMINISTRATOR)] -async fn link(_ctx: &Context, _msg: &Message, _args: Args) -> CommandResult { - let mut note_name = Args::new(_args.rest(), &[Delimiter::Single(' ')]); - let first_arg = note_name.single_quoted::().unwrap(); - let second_arg = note_name.single_quoted::().unwrap(); - let dbnode = Database::from("notes".to_string()).await; - - symlink( - format!("{}/{}", &dbnode, &first_arg), - format!("{}/{}", &dbnode, &second_arg), - ) - .await - .unwrap(); - - _msg.reply( - &_ctx.http, - format!("Note(**{}**) linked As(**{}**)", &first_arg, &second_arg), - ) - .await - .unwrap(); - - Ok(()) -} - -#[command] -// #[required_permissions(ADMINISTRATOR)] -async fn list(_ctx: &Context, _msg: &Message, _args: Args) -> CommandResult { - let dbnode = Database::from("notes".to_string()).await; - - let mut grid = Grid::new(GridOptions { - filling: Filling::Spaces(1), - direction: Direction::TopToBottom, - }); - - for elem in glob::glob(format!("{}/*", &dbnode).as_str()).unwrap() { - if let Ok(path) = elem { - grid.add(Cell::from(String::from(format!( - "⌘`{}`", - path.file_name().unwrap().to_str().unwrap() - )))); - } - } - - _msg.reply(&_ctx.http, grid.fit_into_columns(300)) - .await - .unwrap(); - - Ok(()) -} diff --git a/src/command/owner_check.rs b/src/command/owner_check.rs deleted file mode 100644 index e88f674..0000000 --- a/src/command/owner_check.rs +++ /dev/null @@ -1,33 +0,0 @@ -use super::*; -// A function which acts as a "check", to determine whether to call a command. -// -// In this case, this command checks to ensure you are the owner of the message -// in order for the command to be executed. If the check fails, the command is -// not called. -#[check] -#[name = "Owner"] -async fn owner_check( - _: &Context, - msg: &Message, - _: &mut Args, - _: &CommandOptions, -) -> Result<(), Reason> { - // Replace 7 with your ID to make this check pass. - // - // 1. If you want to pass a reason alongside failure you can do: - // `Reason::User("Lacked admin permission.".to_string())`, - // - // 2. If you want to mark it as something you want to log only: - // `Reason::Log("User lacked admin permission.".to_string())`, - // - // 3. If the check's failure origin is unknown you can mark it as such: - // `Reason::Unknown` - // - // 4. If you want log for your system and for the user, use: - // `Reason::UserAndLog { user, log }` - if msg.author.id != 7 { - return Err(Reason::User("Lacked owner permission".to_string())); - } - - Ok(()) -} diff --git a/src/command/ping.rs b/src/command/ping.rs deleted file mode 100644 index 735553f..0000000 --- a/src/command/ping.rs +++ /dev/null @@ -1,11 +0,0 @@ -use super::*; - -#[command] -// Limit command usage to guilds. -#[only_in(guilds)] -#[checks(Owner)] -async fn ping(ctx: &Context, msg: &Message) -> CommandResult { - msg.channel_id.say(&ctx.http, "Pong! : )").await?; - - Ok(()) -} diff --git a/src/command/say.rs b/src/command/say.rs deleted file mode 100644 index 9d290a8..0000000 --- a/src/command/say.rs +++ /dev/null @@ -1,34 +0,0 @@ -use super::*; - -// Repeats what the user passed as argument but ensures that user and role -// mentions are replaced with a safe textual alternative. -// In this example channel mentions are excluded via the `ContentSafeOptions`. -#[command] -#[required_permissions(ADMINISTRATOR)] -async fn say(_ctx: &Context, _msg: &Message, _args: Args) -> CommandResult { - // Firstly remove the command msg - _msg.channel_id.delete_message(&_ctx.http, _msg.id).await?; - - // Use contentsafe options - let settings = { - ContentSafeOptions::default() - .clean_channel(false) - .clean_role(true) - .clean_user(false) - }; - - let content = content_safe(&_ctx.cache, _args.rest(), &settings, &[]); - let ref_msg = &_msg.referenced_message; - - if ref_msg.is_some() { - ref_msg - .as_ref() - .map(|x| x.reply_ping(&_ctx.http, &content)) - .unwrap() - .await?; - } else { - _msg.channel_id.say(&_ctx.http, &content).await?; - } - - Ok(()) -} diff --git a/src/command/some_long_command.rs b/src/command/some_long_command.rs deleted file mode 100644 index f7fd23c..0000000 --- a/src/command/some_long_command.rs +++ /dev/null @@ -1,10 +0,0 @@ -use super::*; - -#[command] -async fn some_long_command(ctx: &Context, msg: &Message, args: Args) -> CommandResult { - msg.channel_id - .say(&ctx.http, &format!("Arguments: {:?}", args.rest())) - .await?; - - Ok(()) -} diff --git a/src/command/whois.rs b/src/command/whois.rs deleted file mode 100644 index f297751..0000000 --- a/src/command/whois.rs +++ /dev/null @@ -1,145 +0,0 @@ -use super::*; - -#[command] -#[aliases("whoami")] -// #[sub_commands(fetch)] -pub async fn whois(_ctx: &Context, _msg: &Message, mut _args: Args) -> CommandResult { - { - let typing = _ctx - .http - .start_typing(u64::try_from(_msg.channel_id).unwrap()) - .unwrap(); - - let dbnode = Database::from("userid".to_string()).await; - - let user = Parse::user(_ctx, _msg, &_args).await; - - let guid = &_msg.guild_id.unwrap(); - - let prev_usernames = dbnode.get_user_info(&user.to_string()).await; - - let user_data = &_ctx - .cache - .guild(guid) - .unwrap() - .member(&_ctx.http, user) - .await - .unwrap(); - - let user_date = &user_data.user.created_at().date().format("%a, %B %e, %Y"); - - let user_server_date = &user_data.joined_at.unwrap().date().format("%a, %B %e, %Y"); - - let user_time = &_msg.author.created_at().time().format("%H:%M:%S"); - - let user_avatar = &user_data.user.face(); - - let user_colors = { - let user = &user_data.colour(&_ctx.cache).await; - if user.is_some() { - user.unwrap().hex() - } else { - String::from("0000") - } - }; - - // let guild = &_msg.guild_id.unwrap(); - // let messages = &_ctx.cache.guild_channels(guild).await.unwrap(); - - // let mut countmsg = 1; - - // for (id, channel) in messages.iter() { - // countmsg = countmsg + 1; - // let chi = id.messages_iter(&_ctx.http).un; - // } - - // let hmm = &_msg.channel(&_ctx.cache).await.unwrap().guild().unwrap().messages(http, builder) - - _msg.channel_id - .send_message(&_ctx.http, |m| { - m.embed(|e| { - e.title("FreeBSD whois"); - // e.color() - e.thumbnail(&user_avatar); - e.url("https://www.freebsd.org/cgi/man.cgi?query=whois"); - - let intro = english_gen(1, 1); - - e.fields(vec![ - ( - "Intoduction.exe", - format!( - "{} [{}](https://www.dictionary.com/browse/{})", - vowel_gen(&intro), - &intro, - &intro - ), - true, - ), - ( - "Color.exe", - format!( - "[#{}](https://www.colorhexa.com/{}) (Heximal)", - &user_colors, &user_colors - ), - true, - ), - ]); - - e.field("‎", "‎", false); - - e.fields(vec![ - ("JoinedAt.exe", format!("{}", &user_server_date), true), - ( - "RegisteredAt.exe", - format!("{} {}", &user_date, &user_time), - true, - ), - ]); - - e.field( - "Usernames.exe:", - format!( - "{}{}{}", - "```\n", - prev_usernames.as_str().substring(0, 1016), - "```\n" - ), - false, - ); - - // e.image(&user_avatar); - - e - }); - - m - }) - .await - .unwrap(); - typing.stop(); - } - Ok(()) -} - -// #[command] -// #[required_permissions(ADMINISTRATOR)] -// pub async fn fetch(_ctx: &Context, _msg: &Message) -> CommandResult { -// { -// let typing = _ctx -// .http -// .start_typing(u64::try_from(_msg.channel_id).unwrap()) -// .unwrap(); -// let dbnode_userid = Database::from("userid".to_string()).await; -// let members = &_msg.guild(&_ctx.cache).await.unwrap().members; - -// for (_user_id, _member) in members { -// dbnode_userid -// .save_user_info(&_user_id, _member.user.tag()) -// .await; -// } - -// typing.stop(); -// } -// Ok(()) -// } diff --git a/src/db.rs b/src/db.rs deleted file mode 100644 index a33cf28..0000000 --- a/src/db.rs +++ /dev/null @@ -1,77 +0,0 @@ -use anyhow::{Context, Result}; -use serenity::{ - async_trait, client, - model::id::{RoleId, UserId}, - prelude::TypeMapKey, -}; -use std::sync::Arc; - -pub struct Db { - pub sqlitedb: sqlx::sqlite::SqlitePool, -} - -impl TypeMapKey for Db { - type Value = Arc; -} - -impl Db { - pub async fn new() -> Result { - let pool = sqlx::sqlite::SqlitePoolOptions::new() - .max_connections(5) - .connect_with( - sqlx::sqlite::SqliteConnectOptions::new() - .filename("bot.sqlite") - .create_if_missing(true), - ) - .await?; - Ok(Self { sqlitedb: pool }) - } - - pub async fn run_migrations(&self) -> Result<()> { - sqlx::migrate!("./migrations") - .run(&self.sqlitedb) - .await - .context("Failed to run database migrations")?; - Ok(()) - } -} - -#[async_trait] -pub trait ClientContextExt { - async fn get_db(&self) -> Arc; -} - -#[async_trait] -impl ClientContextExt for client::Context { - async fn get_db(&self) -> Arc { - self.data.read().await.get::().unwrap().clone() - } -} - -// pub struct User { -// userid: UserId, -// roles: Vec, -// } - -impl Db { - pub async fn set_user_roles(&self, user_id: UserId, roles: Vec) -> Result<()> { - let user_id = user_id.0 as i64; - let roles = serde_json::to_string(&roles)?; - sqlx::query!("insert into user_profile (user_id, roles) values (?1, ?2) on conflict(user_id) do update set roles=?2", user_id, roles).execute(&self.sqlitedb).await?; - Ok(()) - } - // pub async fn get_user_roles(&self, user_id: UserId) -> Result> { - // let user_id = user_id.0 as i64; - // let result = sqlx::query!("select * from user_profile where user_id=?", user_id) - // .fetch_optional(&self.sqlitedb) - // .await?; - // if let Some(x) = result { - // Ok(Some(User { - // userid: UserId(x.user_id as u64), - // roles: serde_json::from_str(&x.roles).context("Failed to get roles")?, - // })) - // } else { - // Ok(None) - // } - // } -}