From 5a418065673566e29f1146425558d807dd3062c6 Mon Sep 17 00:00:00 2001 From: Aadi Desai <21363892+supleed2@users.noreply.github.com> Date: Wed, 6 Dec 2023 23:56:35 +0000 Subject: [PATCH] Add `mod extras` in `cmd`/`db` for `gaijin` table --- ...7cbd8c38bad944287be73548883d1e2f42723.json | 20 ++ ...3ec4b65165a76010969af49e44e0d41b8aca6.json | 16 ++ ...830b0c18da166eca63aaa758d3c896b3e5466.json | 15 ++ ...e08e3d4d671563c1546f3f63038601fe5142d.json | 32 +++ ...bb6588137401fb88cb7bf02aca73f89134d94.json | 34 +++ ...49b63c30d9b9217330dab776c0b10b766b9c2.json | 34 +++ ...6f98bd31fd2cf78f8bd36002a81e3ed7295a0.json | 35 +++ ...4cd05ab3dcdf4e04ea1491160b9395d0e96d5.json | 14 ++ ...0fac449e6552c94e0819e1684bd7f48a2f087.json | 15 ++ migrations/20231206175347_gaijin.sql | 5 + src/cmds/extras.rs | 203 ++++++++++++++++++ src/cmds/mod.rs | 3 + src/db/extras.rs | 110 ++++++++++ src/db/mod.rs | 3 + src/main.rs | 6 + 15 files changed, 545 insertions(+) create mode 100644 .sqlx/query-2ff8714555dfbbd54d0714eacc97cbd8c38bad944287be73548883d1e2f42723.json create mode 100644 .sqlx/query-46164355ac6198238b49399cb6f3ec4b65165a76010969af49e44e0d41b8aca6.json create mode 100644 .sqlx/query-67652562ba289eb253e4986fbcf830b0c18da166eca63aaa758d3c896b3e5466.json create mode 100644 .sqlx/query-74519af03ec8e4d280501ec5325e08e3d4d671563c1546f3f63038601fe5142d.json create mode 100644 .sqlx/query-7d460b23ef1ba963ed61247055fbb6588137401fb88cb7bf02aca73f89134d94.json create mode 100644 .sqlx/query-7d83a166b3c672b6f8b7de4655b49b63c30d9b9217330dab776c0b10b766b9c2.json create mode 100644 .sqlx/query-87c9aed68f10d778eea00d0c9e46f98bd31fd2cf78f8bd36002a81e3ed7295a0.json create mode 100644 .sqlx/query-c399a706e9f080ad5c3cac0e40a4cd05ab3dcdf4e04ea1491160b9395d0e96d5.json create mode 100644 .sqlx/query-dca361e9e41275709f459f8dd2e0fac449e6552c94e0819e1684bd7f48a2f087.json create mode 100644 migrations/20231206175347_gaijin.sql create mode 100644 src/cmds/extras.rs create mode 100644 src/db/extras.rs diff --git a/.sqlx/query-2ff8714555dfbbd54d0714eacc97cbd8c38bad944287be73548883d1e2f42723.json b/.sqlx/query-2ff8714555dfbbd54d0714eacc97cbd8c38bad944287be73548883d1e2f42723.json new file mode 100644 index 0000000..43062e1 --- /dev/null +++ b/.sqlx/query-2ff8714555dfbbd54d0714eacc97cbd8c38bad944287be73548883d1e2f42723.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "select count(*) as \"i64!\" from gaijin", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "i64!", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "2ff8714555dfbbd54d0714eacc97cbd8c38bad944287be73548883d1e2f42723" +} diff --git a/.sqlx/query-46164355ac6198238b49399cb6f3ec4b65165a76010969af49e44e0d41b8aca6.json b/.sqlx/query-46164355ac6198238b49399cb6f3ec4b65165a76010969af49e44e0d41b8aca6.json new file mode 100644 index 0000000..ece0ca9 --- /dev/null +++ b/.sqlx/query-46164355ac6198238b49399cb6f3ec4b65165a76010969af49e44e0d41b8aca6.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "insert into gaijin values ($1, $2, $3)", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8", + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "46164355ac6198238b49399cb6f3ec4b65165a76010969af49e44e0d41b8aca6" +} diff --git a/.sqlx/query-67652562ba289eb253e4986fbcf830b0c18da166eca63aaa758d3c896b3e5466.json b/.sqlx/query-67652562ba289eb253e4986fbcf830b0c18da166eca63aaa758d3c896b3e5466.json new file mode 100644 index 0000000..68836f2 --- /dev/null +++ b/.sqlx/query-67652562ba289eb253e4986fbcf830b0c18da166eca63aaa758d3c896b3e5466.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "update gaijin set name=$2 where discord_id=$1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8", + "Text" + ] + }, + "nullable": [] + }, + "hash": "67652562ba289eb253e4986fbcf830b0c18da166eca63aaa758d3c896b3e5466" +} diff --git a/.sqlx/query-74519af03ec8e4d280501ec5325e08e3d4d671563c1546f3f63038601fe5142d.json b/.sqlx/query-74519af03ec8e4d280501ec5325e08e3d4d671563c1546f3f63038601fe5142d.json new file mode 100644 index 0000000..34fda02 --- /dev/null +++ b/.sqlx/query-74519af03ec8e4d280501ec5325e08e3d4d671563c1546f3f63038601fe5142d.json @@ -0,0 +1,32 @@ +{ + "db_name": "PostgreSQL", + "query": "select * from gaijin", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "discord_id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "university", + "type_info": "Text" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + false, + false + ] + }, + "hash": "74519af03ec8e4d280501ec5325e08e3d4d671563c1546f3f63038601fe5142d" +} diff --git a/.sqlx/query-7d460b23ef1ba963ed61247055fbb6588137401fb88cb7bf02aca73f89134d94.json b/.sqlx/query-7d460b23ef1ba963ed61247055fbb6588137401fb88cb7bf02aca73f89134d94.json new file mode 100644 index 0000000..2a3a51b --- /dev/null +++ b/.sqlx/query-7d460b23ef1ba963ed61247055fbb6588137401fb88cb7bf02aca73f89134d94.json @@ -0,0 +1,34 @@ +{ + "db_name": "PostgreSQL", + "query": "select * from gaijin where lower(name)=lower($1)", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "discord_id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "university", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + false + ] + }, + "hash": "7d460b23ef1ba963ed61247055fbb6588137401fb88cb7bf02aca73f89134d94" +} diff --git a/.sqlx/query-7d83a166b3c672b6f8b7de4655b49b63c30d9b9217330dab776c0b10b766b9c2.json b/.sqlx/query-7d83a166b3c672b6f8b7de4655b49b63c30d9b9217330dab776c0b10b766b9c2.json new file mode 100644 index 0000000..31cd692 --- /dev/null +++ b/.sqlx/query-7d83a166b3c672b6f8b7de4655b49b63c30d9b9217330dab776c0b10b766b9c2.json @@ -0,0 +1,34 @@ +{ + "db_name": "PostgreSQL", + "query": "select * from gaijin where discord_id=$1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "discord_id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "university", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [ + false, + false, + false + ] + }, + "hash": "7d83a166b3c672b6f8b7de4655b49b63c30d9b9217330dab776c0b10b766b9c2" +} diff --git a/.sqlx/query-87c9aed68f10d778eea00d0c9e46f98bd31fd2cf78f8bd36002a81e3ed7295a0.json b/.sqlx/query-87c9aed68f10d778eea00d0c9e46f98bd31fd2cf78f8bd36002a81e3ed7295a0.json new file mode 100644 index 0000000..92da749 --- /dev/null +++ b/.sqlx/query-87c9aed68f10d778eea00d0c9e46f98bd31fd2cf78f8bd36002a81e3ed7295a0.json @@ -0,0 +1,35 @@ +{ + "db_name": "PostgreSQL", + "query": "select * from gaijin where similarity(name,$1) > 0.3 order by similarity(name,$1) desc limit $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "discord_id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "university", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text", + "Int8" + ] + }, + "nullable": [ + false, + false, + false + ] + }, + "hash": "87c9aed68f10d778eea00d0c9e46f98bd31fd2cf78f8bd36002a81e3ed7295a0" +} diff --git a/.sqlx/query-c399a706e9f080ad5c3cac0e40a4cd05ab3dcdf4e04ea1491160b9395d0e96d5.json b/.sqlx/query-c399a706e9f080ad5c3cac0e40a4cd05ab3dcdf4e04ea1491160b9395d0e96d5.json new file mode 100644 index 0000000..5a74651 --- /dev/null +++ b/.sqlx/query-c399a706e9f080ad5c3cac0e40a4cd05ab3dcdf4e04ea1491160b9395d0e96d5.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "delete from gaijin where discord_id=$1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [] + }, + "hash": "c399a706e9f080ad5c3cac0e40a4cd05ab3dcdf4e04ea1491160b9395d0e96d5" +} diff --git a/.sqlx/query-dca361e9e41275709f459f8dd2e0fac449e6552c94e0819e1684bd7f48a2f087.json b/.sqlx/query-dca361e9e41275709f459f8dd2e0fac449e6552c94e0819e1684bd7f48a2f087.json new file mode 100644 index 0000000..031614b --- /dev/null +++ b/.sqlx/query-dca361e9e41275709f459f8dd2e0fac449e6552c94e0819e1684bd7f48a2f087.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "update gaijin set university=$2 where discord_id=$1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8", + "Text" + ] + }, + "nullable": [] + }, + "hash": "dca361e9e41275709f459f8dd2e0fac449e6552c94e0819e1684bd7f48a2f087" +} diff --git a/migrations/20231206175347_gaijin.sql b/migrations/20231206175347_gaijin.sql new file mode 100644 index 0000000..0179f34 --- /dev/null +++ b/migrations/20231206175347_gaijin.sql @@ -0,0 +1,5 @@ +CREATE TABLE IF NOT EXISTS "gaijin" ( + "discord_id" BIGINT NOT NULL PRIMARY KEY, + "name" TEXT NOT NULL, + "university" TEXT NOT NULL +) \ No newline at end of file diff --git a/src/cmds/extras.rs b/src/cmds/extras.rs new file mode 100644 index 0000000..cbc538c --- /dev/null +++ b/src/cmds/extras.rs @@ -0,0 +1,203 @@ +use crate::{db, ACtx, Error, Gaijin}; +use poise::serenity_prelude as serenity; +use poise::Modal; + +/// Get the number of entries in the gaijin table +#[tracing::instrument(skip_all)] +#[poise::command(slash_command)] +pub(crate) async fn count_gaijin(ctx: ACtx<'_>) -> Result<(), Error> { + tracing::info!("{}", ctx.author().name); + let count = db::count_gaijin(&ctx.data().db).await?; + ctx.say(format!("There are {count} entries in the gaijin table")) + .await?; + Ok(()) +} + +/// Delete gaijin info by Discord ID +#[tracing::instrument(skip_all)] +#[poise::command(slash_command)] +pub(crate) async fn delete_gaijin( + ctx: ACtx<'_>, + mut id: serenity::Member, + remove_roles: Option, +) -> Result<(), Error> { + tracing::info!("{} {}", ctx.author().name, id.user.name); + if db::delete_gaijin_by_id(&ctx.data().db, id.user.id.into()).await? { + if remove_roles.unwrap_or(true) { + crate::verify::remove_role(ctx.serenity_context(), &mut id, ctx.data().gaijin).await?; + } + ctx.say(format!("Successfully deleted gaijin info for {id}")) + .await? + } else { + ctx.say(format!("Failed to delete gaijin info for {id}")) + .await? + }; + Ok(()) +} + +/// Print all gaijin in gaijin table +#[tracing::instrument(skip_all)] +#[poise::command(slash_command)] +pub(crate) async fn get_all_gaijin(ctx: ACtx<'_>) -> Result<(), Error> { + #[derive(Modal)] + struct Confirm { + #[name = "This will output the gaijin db as text"] + #[placeholder = "yes"] + confirm: String, + } + + tracing::info!("{}", ctx.author().name); + + if let Some(Confirm { confirm }) = Confirm::execute(ctx).await? { + if confirm.to_lowercase().contains("yes") { + let gaijin = db::get_all_gaijin(&ctx.data().db).await?; + match tokio::fs::write("gaijin.rs", format!("{gaijin:#?}")).await { + Ok(()) => { + ctx.say("Sending gaijin db data in followup message") + .await?; + ctx.channel_id() + .send_files(&ctx.http(), vec!["gaijin.rs"], |cm| { + cm.content("File: gaijin db") + }) + .await?; + } + Err(e) => { + tracing::error!("{e}"); + ctx.say("Failed to create gaijin db file").await?; + } + } + let _ = tokio::fs::remove_file("gaijin.rs").await; + Ok(()) + } else { + ctx.say("Skipping gaijin db output").await?; + Ok(()) + } + } else { + ctx.say("Timed out").await?; + Ok(()) + } +} + +/// Unreachable, used to create get_gaijin command folder +#[allow(clippy::unused_async)] +#[poise::command(slash_command, subcommands("get_gaijin_by_id", "get_gaijin_by_name"))] +pub(crate) async fn get_gaijin(_ctx: ACtx<'_>) -> Result<(), Error> { + unreachable!() +} + +/// Get gaijin info by Discord ID +#[tracing::instrument(skip_all)] +#[poise::command(slash_command, rename = "id")] +pub(crate) async fn get_gaijin_by_id(ctx: ACtx<'_>, id: serenity::Member) -> Result<(), Error> { + tracing::info!("{} {}", ctx.author().name, id.user.name); + match db::get_gaijin_by_id(&ctx.data().db, id.user.id.into()).await? { + Some(m) => { + ctx.say(format!("Gaijin info for {id}:\n```rust\n{m:#?}\n```")) + .await? + } + None => ctx.say(format!("No gaijin entry found for {id}")).await?, + }; + Ok(()) +} + +/// Get gaijin info by Name +#[tracing::instrument(skip_all)] +#[poise::command(slash_command, rename = "name")] +pub(crate) async fn get_gaijin_by_name(ctx: ACtx<'_>, name: String) -> Result<(), Error> { + tracing::info!("{} {name}", ctx.author().name); + if let Some(g) = db::get_gaijin_by_name(&ctx.data().db, &name).await? { + ctx.say(format!( + "Gaijin info for name {name}:\n```rust\n{g:#?}\n```" + )) + .await?; + } else { + let gaijin = db::get_gaijin_by_name_fuzzy(&ctx.data().db, &name, 3).await?; + if gaijin.is_empty() { + ctx.say(format!("No entry found for name {name}")).await?; + } else { + ctx.say(format!( + "Possible matches for {name}: {}", + gaijin + .iter() + .map(|g| format!(" <@{}>", g.discord_id)) + .collect::() + )) + .await?; + } + } + Ok(()) +} + +/// Add a gaijin to the gaijin table +#[tracing::instrument(skip_all)] +#[poise::command(slash_command)] +pub(crate) async fn add_gaijin( + ctx: ACtx<'_>, + mut id: serenity::Member, + name: String, + university: String, +) -> Result<(), Error> { + tracing::info!( + "{} {}, {name}, {university}", + ctx.author().name, + id.user.name, + ); + db::insert_gaijin( + &ctx.data().db, + Gaijin { + discord_id: id.user.id.into(), + name, + university, + }, + ) + .await?; + crate::verify::apply_role(ctx.serenity_context(), &mut id, ctx.data().gaijin).await?; + ctx.say(format!("Gaijin added: {id}")).await?; + Ok(()) +} + +/// Unreachable, used to create edit_gaijin command folder +#[allow(clippy::unused_async)] +#[poise::command( + slash_command, + subcommands("edit_gaijin_name", "edit_gaijin_university") +)] +pub(crate) async fn edit_gaijin(_ctx: ACtx<'_>) -> Result<(), Error> { + unreachable!() +} + +/// Edit gaijin Name +#[tracing::instrument(skip_all)] +#[poise::command(slash_command, rename = "name")] +pub(crate) async fn edit_gaijin_name( + ctx: ACtx<'_>, + id: serenity::Member, + name: String, +) -> Result<(), Error> { + tracing::info!("{} {name}", ctx.author().name); + if db::edit_gaijin_name(&ctx.data().db, id.user.id.into(), &name).await? { + ctx.say(format!("{id} Name updated to {name}")).await?; + } else { + ctx.say(format!("Failed to update Name for {id}")).await?; + } + Ok(()) +} + +/// Edit gaijin University +#[tracing::instrument(skip_all)] +#[poise::command(slash_command, rename = "university")] +pub(crate) async fn edit_gaijin_university( + ctx: ACtx<'_>, + id: serenity::Member, + university: String, +) -> Result<(), Error> { + tracing::info!("{} {university}", ctx.author().name); + if db::edit_gaijin_university(&ctx.data().db, id.user.id.into(), &university).await? { + ctx.say(format!("{id} University updated to {university}")) + .await?; + } else { + ctx.say(format!("Failed to update University for {id}")) + .await?; + } + Ok(()) +} diff --git a/src/cmds/mod.rs b/src/cmds/mod.rs index d641f82..b840316 100644 --- a/src/cmds/mod.rs +++ b/src/cmds/mod.rs @@ -17,6 +17,9 @@ pub(crate) use edit_members::*; pub(crate) mod whois; pub(crate) use whois::*; +pub(crate) mod extras; +pub(crate) use extras::*; + /// Buttons to (de-)register application commands globally or by guild #[tracing::instrument(skip_all)] #[poise::command(prefix_command, owners_only)] diff --git a/src/db/extras.rs b/src/db/extras.rs new file mode 100644 index 0000000..540e5f0 --- /dev/null +++ b/src/db/extras.rs @@ -0,0 +1,110 @@ +use crate::{Error, Gaijin}; + +/// Get count of entries in gaijin table +pub(crate) async fn count_gaijin(pool: &sqlx::PgPool) -> Result { + Ok(sqlx::query!("select count(*) as \"i64!\" from gaijin") + .fetch_one(pool) + .await? + .i64) +} + +/// Delete gaijin by Discord ID +pub(crate) async fn delete_gaijin_by_id(pool: &sqlx::PgPool, id: i64) -> Result { + let r = sqlx::query!("delete from gaijin where discord_id=$1", id) + .execute(pool) + .await? + .rows_affected(); + Ok(r == 1) +} + +/// Get all entries in gaijin table +pub(crate) async fn get_all_gaijin(pool: &sqlx::PgPool) -> Result, Error> { + Ok(sqlx::query_as!(Gaijin, "select * from gaijin") + .fetch_all(pool) + .await?) +} + +/// Get gaijin entry by Discord ID +pub(crate) async fn get_gaijin_by_id( + pool: &sqlx::PgPool, + id: i64, +) -> Result, Error> { + Ok( + sqlx::query_as!(Gaijin, "select * from gaijin where discord_id=$1", id) + .fetch_optional(pool) + .await?, + ) +} + +/// Get gaijin entry by Name +pub(crate) async fn get_gaijin_by_name( + pool: &sqlx::PgPool, + name: &str, +) -> Result, Error> { + Ok(sqlx::query_as!( + Gaijin, + "select * from gaijin where lower(name)=lower($1)", + name + ) + .fetch_optional(pool) + .await?) +} + +/// Get gaijin entry by Name (Fuzzy) TODO: add to whois +pub(crate) async fn get_gaijin_by_name_fuzzy( + pool: &sqlx::PgPool, + name: &str, + limit: i64, +) -> Result, Error> { + Ok(sqlx::query_as!( + Gaijin, + "select * from gaijin where similarity(name,$1) > 0.3 order by similarity(name,$1) desc limit $2", + name, + limit + ) + .fetch_all(pool) + .await?) +} + +/// Add entry to gaijin table +pub(crate) async fn insert_gaijin(pool: &sqlx::PgPool, g: Gaijin) -> Result<(), Error> { + sqlx::query!( + "insert into gaijin values ($1, $2, $3)", + g.discord_id, + g.name, + g.university + ) + .execute(pool) + .await?; + Ok(()) +} + +/// Edit gaijin name field +pub(crate) async fn edit_gaijin_name( + pool: &sqlx::PgPool, + id: i64, + name: &str, +) -> Result { + let r = sqlx::query!("update gaijin set name=$2 where discord_id=$1", id, name) + .execute(pool) + .await? + .rows_affected(); + Ok(r == 1) +} + +/// Edit gaijin university field +pub(crate) async fn edit_gaijin_university( + pool: &sqlx::PgPool, + id: i64, + university: &str, +) -> Result { + let r = sqlx::query!( + "update gaijin set university=$2 where discord_id=$1", + id, + university + ) + .execute(pool) + .await? + .rows_affected(); + Ok(r == 1) +} diff --git a/src/db/mod.rs b/src/db/mod.rs index 0d25bb8..5603f49 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -6,3 +6,6 @@ pub(crate) use pending::*; pub(crate) mod manual; pub(crate) use manual::*; + +pub(crate) mod extras; +pub(crate) use extras::*; diff --git a/src/main.rs b/src/main.rs index 5199dbb..09dfca6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -272,5 +272,11 @@ fn all_commands() -> Vec> { cmds::add_manual(), cmds::delete_all_manual(), cmds::whois(), + cmds::count_gaijin(), + cmds::delete_gaijin(), + cmds::get_all_gaijin(), + cmds::get_gaijin(), + cmds::add_gaijin(), + cmds::edit_gaijin(), ] }