From 5f8ceb94b20f9363eba69a9316176dbdd00f9eac Mon Sep 17 00:00:00 2001 From: natto1784 Date: Sun, 13 Feb 2022 20:28:59 +0530 Subject: [PATCH] renamed count commands and added tags commands --- Cargo.toml | 1 + flake.nix | 2 +- init.sql | 14 ++ src/commands/count.rs | 51 ++++--- src/commands/mod.rs | 3 +- src/commands/tags.rs | 325 ++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 9 +- 7 files changed, 381 insertions(+), 24 deletions(-) create mode 100644 src/commands/tags.rs diff --git a/Cargo.toml b/Cargo.toml index 3717f54..dea24b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,3 +17,4 @@ features = ["cache", "framework", "standard_framework", "rustls_backend", "unsta [dependencies.tokio] version = "1.0" features = ["macros", "signal", "rt-multi-thread"] + diff --git a/flake.nix b/flake.nix index 6ad8be8..1f2e1cd 100644 --- a/flake.nix +++ b/flake.nix @@ -30,7 +30,7 @@ nativeBuildInputs = with pkgs; [ rust-bin.nightly.latest.default ]; - cargoSha256 = "sha256-K+WHOEo6reNfcs7pOZZmHZfZl4pUqlykfTdqgSyVURU="; + cargoSha256 = "sha256-0Apd8a9IdQJ8Mj5Xvm1/wM0PSc7PgciIptmz/KGx8XM="; }; } ); diff --git a/init.sql b/init.sql index 9111751..60a0bc3 100644 --- a/init.sql +++ b/init.sql @@ -3,3 +3,17 @@ CREATE TABLE IF NOT EXISTS words ( name varchar not null, reg varchar not null, owner varchar ); + +ALTER SEQUENCE words_id_seq RESTART; +UPDATE words SET id = DEFAULT; + +CREATE TABLE IF NOT EXISTS tags ( + id serial primary key, + name varchar unique not null, + value varchar not null, + owner varchar ); + +ALTER SEQUENCE tags_id_seq RESTART; +UPDATE tags SET id = DEFAULT; + +CREATE EXTENSION IF NOT EXISTS fuzzystrmatch; diff --git a/src/commands/count.rs b/src/commands/count.rs index b44ebc7..c9b65cc 100644 --- a/src/commands/count.rs +++ b/src/commands/count.rs @@ -14,7 +14,8 @@ use serenity::{ use tokio_postgres::Row; #[command] -pub async fn kitna(ctx: &Context, msg: &Message, args: Args) -> CommandResult { +#[aliases("kitna")] +pub async fn count(ctx: &Context, msg: &Message, args: Args) -> CommandResult { let query: String = args.raw().collect::>().join(" "); if query == "" { msg.reply(ctx, "bruh kitna kya?").await?; @@ -29,14 +30,14 @@ pub async fn kitna(ctx: &Context, msg: &Message, args: Args) -> CommandResult { let id = msg.author.id.to_string(); let mut query_helper = db .query( - format!("select name from words where '{}' ~ reg", query).as_str(), + format!("SELECT name FROM words WHERE '{}' ~ reg", query).as_str(), &[], ) .await?; if query_helper.is_empty() { query_helper = db .query( - format!("select name from words where name='{}'", query).as_str(), + format!("SELECT name FROM words WHERE name='{}'", query).as_str(), &[], ) .await?; @@ -44,7 +45,7 @@ pub async fn kitna(ctx: &Context, msg: &Message, args: Args) -> CommandResult { msg.reply( ctx, format!( - "No entry for '{}' found. If you want to add it, run ',count add {}&'", + "No entry for '{}' found. If you want to add it, run ',cadd {}&'", query, query ), ) @@ -61,7 +62,7 @@ pub async fn kitna(ctx: &Context, msg: &Message, args: Args) -> CommandResult { let name: &str = row.get(0); let query_result: i32 = db .query_one( - format!("select count from user{} where name='{}'", id, name).as_str(), + format!("SELECT count FROM user{} WHERE name='{}'", id, name).as_str(), &[], ) .await? @@ -73,11 +74,11 @@ pub async fn kitna(ctx: &Context, msg: &Message, args: Args) -> CommandResult { } #[command] -pub async fn add(ctx: &Context, msg: &Message, args: Args) -> CommandResult { +pub async fn cadd(ctx: &Context, msg: &Message, args: Args) -> CommandResult { let query: String = args.raw().collect::>().join(" "); let queries = query.splitn(2, "&").collect::>(); if queries.len() != 2 { - msg.reply(ctx, "Please use the proper syntax: `,count add &`\nIf you don't know what regex is, just do: `,count add &`") + msg.reply(ctx, "Please use the proper syntax: `,cadd &`\nIf you don't know what regex is, just do: `,cadd &`") .await?; return Ok(()); } @@ -92,7 +93,7 @@ pub async fn add(ctx: &Context, msg: &Message, args: Args) -> CommandResult { .clone(); let check_existense = db .query( - format!("select name, reg from words where name='{}'", queries[0]).as_str(), + format!("SELECT name, reg FROM words WHERE name='{}'", queries[0]).as_str(), &[], ) .await?; @@ -107,7 +108,7 @@ pub async fn add(ctx: &Context, msg: &Message, args: Args) -> CommandResult { } db.execute( format!( - "insert into words(name, reg, owner) values('{}','(?i){}', '{}')", + "INSERT INTO words(name, reg, owner) VALUES('{}','(?i){}', '{}')", queries[0], queries[1], msg.author.id.to_string() @@ -121,7 +122,7 @@ pub async fn add(ctx: &Context, msg: &Message, args: Args) -> CommandResult { } #[command] -pub async fn rm(ctx: &Context, msg: &Message, args: Args) -> CommandResult { +pub async fn crm(ctx: &Context, msg: &Message, args: Args) -> CommandResult { let query: String = args.raw().collect::>().join(" "); if query == "" { msg.reply(ctx, "remove what?").await?; @@ -134,7 +135,7 @@ pub async fn rm(ctx: &Context, msg: &Message, args: Args) -> CommandResult { .clone(); let owner = db .query( - format!("select owner from words where name = '{}'", query).as_str(), + format!("SELECT owner FROM words WHERE name = '{}'", query).as_str(), &[], ) .await?; @@ -146,7 +147,7 @@ pub async fn rm(ctx: &Context, msg: &Message, args: Args) -> CommandResult { } } db.execute( - format!("delete from words where name='{}'", query,).as_str(), + format!("DELETE FROM words WHERE name='{}'", query,).as_str(), &[], ) .await?; @@ -155,13 +156,13 @@ pub async fn rm(ctx: &Context, msg: &Message, args: Args) -> CommandResult { } #[command] -pub async fn change(ctx: &Context, msg: &Message, args: Args) -> CommandResult { +pub async fn cedit(ctx: &Context, msg: &Message, args: Args) -> CommandResult { let query: String = args.raw().collect::>().join(" "); let queries = query.splitn(2, "&").collect::>(); if queries.len() != 2 { msg.reply( ctx, - "Please use the proper syntax\n,count change &", + "Please use the proper syntax\n,cedit &", ) .await?; return Ok(()); @@ -177,7 +178,7 @@ pub async fn change(ctx: &Context, msg: &Message, args: Args) -> CommandResult { .clone(); let owner = db .query( - format!("select owner from words where name = '{}'", queries[0]).as_str(), + format!("SELECT owner FROM words WHERE name = '{}'", queries[0]).as_str(), &[], ) .await?; @@ -190,7 +191,7 @@ pub async fn change(ctx: &Context, msg: &Message, args: Args) -> CommandResult { } db.execute( format!( - "update words set reg='(?i){}' where name='{}'", + "UPDATE words SET reg='(?i){}' WHERE name='{}'", queries[1], queries[0] ) .as_str(), @@ -237,18 +238,24 @@ macro_rules! make_terminal_components { .custom_id("next") .disabled($terminal == "last") }) + .create_button(|b| { + b.style(ButtonStyle::Danger) + .label("Delete") + .emoji(ReactionType::Unicode("\u{1F5D1}".to_string())) + .custom_id("delete") + }) }) }}; } #[command] -pub async fn ls(ctx: &Context, msg: &Message, _: Args) -> CommandResult { +pub async fn cls(ctx: &Context, msg: &Message, _: Args) -> CommandResult { let data_read = ctx.data.read().await; let db = data_read .get::() .expect("Expected Database in TypeMap.") .clone(); - let rows = db.query("select * from words", &[]).await?; + let rows = db.query("SELECT * FROM words", &[]).await?; if rows.is_empty() { msg.reply(ctx, "No words stored").await?; return Ok(()); @@ -273,11 +280,11 @@ pub async fn ls(ctx: &Context, msg: &Message, _: Args) -> CommandResult { match component.custom_id.as_ref() { "next" => { if cur != groups.len() { + cur += 1; let _ = interaction .create_interaction_response(&ctx, |r| { r.kind(InteractionResponseType::UpdateMessage) .interaction_response_data(|m| { - cur += 1; m.create_embed(|mut e| make_embed!(e, cur, groups[cur - 1])) .components(|c| { make_terminal_components!( @@ -296,11 +303,11 @@ pub async fn ls(ctx: &Context, msg: &Message, _: Args) -> CommandResult { } "prev" => { if cur != 1 { + cur -= 1; let _ = interaction .create_interaction_response(&ctx, |r| { r.kind(InteractionResponseType::UpdateMessage) .interaction_response_data(|m| { - cur -= 1; m.create_embed(|mut e| make_embed!(e, cur, groups[cur - 1])) .components(|c| { make_terminal_components!( @@ -313,6 +320,10 @@ pub async fn ls(ctx: &Context, msg: &Message, _: Args) -> CommandResult { .await; } } + "delete" => { + message.delete(ctx).await?; + msg.delete(ctx).await?; + } _ => {} } } diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 24dc273..754b8fe 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -1,3 +1,4 @@ -pub mod general; pub mod count; +pub mod general; pub mod minigames; +pub mod tags; diff --git a/src/commands/tags.rs b/src/commands/tags.rs new file mode 100644 index 0000000..677a0bb --- /dev/null +++ b/src/commands/tags.rs @@ -0,0 +1,325 @@ +use core::time::Duration; +use serenity::{ + collector::component_interaction_collector::ComponentInteractionCollectorBuilder, + framework::standard::{macros::command, Args, CommandResult}, + futures::StreamExt, + model::{ + channel::ReactionType, + interactions::{ButtonStyle, InteractionData}, + prelude::*, + }, + prelude::*, + utils::Colour, +}; +use tokio_postgres::Row; + +#[command] +#[aliases("t")] +pub async fn tag(ctx: &Context, msg: &Message, args: Args) -> CommandResult { + let query: String = args.raw().collect::>().join(""); + if query == "" { + msg.reply(ctx, "Mention the tag retard").await?; + return Ok(()); + } + let data_read = ctx.data.read().await; + let db = data_read + .get::() + .expect("Expected Database in TypeMap.") + .clone(); + + let query_helper = db + .query( + format!("SELECT name, value FROM tags WHERE name='{}'", query).as_str(), + &[], + ) + .await?; + if query_helper.is_empty() { + let leven = db + .query( + format!( + "SELECT name FROM tags WHERE levenshtein(name, '{}') < 2", + query + ) + .as_str(), + &[], + ) + .await?; + let l = if leven.is_empty() { + "".to_string() + } else { + let leven_name: String = leven[0].get(0); + format!("\nDid you mean `{}`?", leven_name) + }; + + msg.reply( + ctx, + format!( + "No entry for '{}' found. If you want to add it, run `,tadd {} `{}", + query, query, l + ), + ) + .await?; + return Ok(()); + } + let value: String = query_helper[0].get(1); + msg.reply(ctx, value).await?; + Ok(()) +} + +#[command] +pub async fn tadd(ctx: &Context, msg: &Message, args: Args) -> CommandResult { + let query: String = args.raw().collect::>().join(" "); + let queries = query.splitn(2, " ").collect::>(); + if queries.len() != 2 { + msg.reply(ctx, "Please use the proper syntax: `,tadd `") + .await?; + return Ok(()); + } + let data_read = ctx.data.read().await; + let db = data_read + .get::() + .expect("Expected Database in TypeMap.") + .clone(); + let check_existense = db + .query( + format!("SELECT name FROM tags WHERE name='{}'", queries[0]).as_str(), + &[], + ) + .await?; + if check_existense.len() != 0 { + msg.reply(ctx, format!("This tag already exists")).await?; + return Ok(()); + } + db.execute( + format!( + "INSERT INTO tags(name, value, owner) VALUES('{}','{}', '{}')", + queries[0], + format!( + "{}\n{}", + queries[1], + msg.attachments + .iter() + .map(|x| x.url.clone()) + .collect::>() + .join("\n") + ), + msg.author.id.to_string() + ) + .as_str(), + &[], + ) + .await?; + msg.reply(ctx, "Added").await?; + Ok(()) +} + +#[command] +pub async fn trm(ctx: &Context, msg: &Message, args: Args) -> CommandResult { + let query: String = args.raw().collect::>().join(" "); + if query == "" { + msg.reply(ctx, "remove what?").await?; + return Ok(()); + } + let data_read = ctx.data.read().await; + let db = data_read + .get::() + .expect("Expected Database in TypeMap.") + .clone(); + let owner = db + .query( + format!("SELECT owner FROM tags WHERE name = '{}'", query).as_str(), + &[], + ) + .await?; + if owner.len() == 1 { + let owner_id: String = owner[0].get(0); + if owner_id != msg.author.id.to_string() { + msg.reply(ctx, "You don't even own this tag").await?; + return Ok(()); + } + } + db.execute( + format!("DELETE FROM tags WHERE name='{}'", query,).as_str(), + &[], + ) + .await?; + msg.reply(ctx, "Deleted if it existed").await?; + Ok(()) +} + +#[command] +pub async fn tedit(ctx: &Context, msg: &Message, args: Args) -> CommandResult { + let query: String = args.raw().collect::>().join(" "); + let queries = query.splitn(2, " ").collect::>(); + if queries.len() != 2 { + msg.reply(ctx, "Please use the proper syntax\n`,tedit `") + .await?; + return Ok(()); + } + let data_read = ctx.data.read().await; + let db = data_read + .get::() + .expect("Expected Database in TypeMap.") + .clone(); + let owner = db + .query( + format!("SELECT owner FROM tags WHERE name = '{}'", queries[0]).as_str(), + &[], + ) + .await?; + if owner.len() == 1 { + let owner_id: String = owner[0].get(0); + if owner_id != msg.author.id.to_string() { + msg.reply(ctx, "You don't even own this tag").await?; + return Ok(()); + } + } + db.execute( + format!( + "UPDATE tags SET value='{}' WHERE name='{}'", + format!( + "{}\n{}", + queries[1], + msg.attachments + .iter() + .map(|x| x.url.clone()) + .collect::>() + .join("\n") + ), + queries[0] + ) + .as_str(), + &[], + ).await?; + msg.reply(ctx, "Changed the value if it existed").await?; + Ok(()) +} + +macro_rules! make_embed { + ($e: expr, $cur: expr, $group: expr) => {{ + $e = $e + .title(format!("List of tags: Page {}", $cur)) + .color(Colour::FABLED_PINK); + for row in $group { + let idx: i32 = row.get(0); + let name: String = row.get(1); + let owner_id: String = row.get(3); + $e = $e.field( + format!("{}. {}", idx, name), + format!(" by <@{}>", owner_id), + true, + ); + } + $e + }}; +} + +macro_rules! make_terminal_components { + ($c: expr, $terminal: expr ) => {{ + $c.create_action_row(|ar| { + ar.create_button(|b| { + b.style(ButtonStyle::Primary) + .label("Prev") + .emoji(ReactionType::Unicode("\u{2B05}".to_string())) + .custom_id("prev") + .disabled($terminal == "first") + }) + .create_button(|b| { + b.style(ButtonStyle::Primary) + .label("Next") + .emoji(ReactionType::Unicode("\u{27A1}".to_string())) + .custom_id("next") + .disabled($terminal == "last") + }) + .create_button(|b| { + b.style(ButtonStyle::Danger) + .label("Delete") + .emoji(ReactionType::Unicode("\u{1F5D1}".to_string())) + .custom_id("delete") + }) + }) + }}; +} + +#[command] +pub async fn tls(ctx: &Context, msg: &Message, _: Args) -> CommandResult { + let data_read = ctx.data.read().await; + let db = data_read + .get::() + .expect("Expected Database in TypeMap.") + .clone(); + let rows = db.query("SELECT * FROM tags", &[]).await?; + if rows.is_empty() { + msg.reply(ctx, "No tags stored").await?; + return Ok(()); + } + let groups: Vec<&[Row]> = rows.chunks(5).collect(); + let mut cur = 1; + + let message = msg + .channel_id + .send_message(ctx, |m| { + m.embed(|mut e| make_embed!(e, cur, groups[cur - 1])) + .components(|c| make_terminal_components!(c, "first")) + }) + .await?; + let mut collector = ComponentInteractionCollectorBuilder::new(&ctx) + .timeout(Duration::from_secs(90)) + .author_id(msg.author.id) + .message_id(message.id) + .await; + while let Some(interaction) = collector.next().await { + if let InteractionData::MessageComponent(component) = interaction.data.as_ref().unwrap() { + match component.custom_id.as_ref() { + "next" => { + if cur != groups.len() { + cur += 1; + let _ = interaction + .create_interaction_response(&ctx, |r| { + r.kind(InteractionResponseType::UpdateMessage) + .interaction_response_data(|m| { + m.create_embed(|mut e| make_embed!(e, cur, groups[cur - 1])) + .components(|c| { + make_terminal_components!( + c, + if cur == groups.len() { + "last" + } else { + "mid" + } + ) + }) + }) + }) + .await; + } + } + "prev" => { + if cur != 1 { + cur -= 1; + let _ = interaction + .create_interaction_response(&ctx, |r| { + r.kind(InteractionResponseType::UpdateMessage) + .interaction_response_data(|m| { + m.create_embed(|mut e| make_embed!(e, cur, groups[cur - 1])) + .components(|c| { + make_terminal_components!( + c, + if cur == 1 { "first" } else { "mid" } + ) + }) + }) + }) + .await; + } + } + "delete" => { + message.delete(ctx).await?; + msg.delete(ctx).await?; + } + _ => {} + } + } + } + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index ccab0b2..07cd4d4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ mod handler; use commands::count::*; use commands::general::*; use commands::minigames::*; +use commands::tags::*; use handler::Handler; use serenity::{ client::bridge::gateway::ShardManager, @@ -36,10 +37,13 @@ impl TypeMapKey for Database { struct General; #[group] -#[prefix = "count"] -#[commands(kitna, add, rm, change, ls)] +#[commands(count, cadd, crm, cedit, cls)] struct Count; +#[group] +#[commands(tag, tadd, trm, tedit, tls)] +pub struct Tags; + #[group] #[commands(challenge)] struct Minigames; @@ -82,6 +86,7 @@ async fn main() { .help(&MY_HELP) .group(&GENERAL_GROUP) .group(&COUNT_GROUP) + .group(&TAGS_GROUP) .group(&MINIGAMES_GROUP); let mut client = Client::builder(&token)