Add mod extras in cmd/db for gaijin table

This commit is contained in:
Aadi Desai 2023-12-06 23:56:35 +00:00
parent 04d3cead63
commit 5a41806567
Signed by: supleed2
SSH key fingerprint: SHA256:CkbNRs0yVzXEiUp2zd0PSxsfRUMFF9bLlKXtE1xEbKM
15 changed files with 545 additions and 0 deletions

View file

@ -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"
}

View file

@ -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"
}

View file

@ -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"
}

View file

@ -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"
}

View file

@ -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"
}

View file

@ -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"
}

View file

@ -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"
}

View file

@ -0,0 +1,14 @@
{
"db_name": "PostgreSQL",
"query": "delete from gaijin where discord_id=$1",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": []
},
"hash": "c399a706e9f080ad5c3cac0e40a4cd05ab3dcdf4e04ea1491160b9395d0e96d5"
}

View file

@ -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"
}

View file

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

203
src/cmds/extras.rs Normal file
View file

@ -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<bool>,
) -> 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::<String>()
))
.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(())
}

View file

@ -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)]

110
src/db/extras.rs Normal file
View file

@ -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<i64, Error> {
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<bool, Error> {
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<Vec<Gaijin>, 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<Option<Gaijin>, 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<Option<Gaijin>, 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<Vec<Gaijin>, 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<bool, Error> {
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<bool, Error> {
let r = sqlx::query!(
"update gaijin set university=$2 where discord_id=$1",
id,
university
)
.execute(pool)
.await?
.rows_affected();
Ok(r == 1)
}

View file

@ -6,3 +6,6 @@ pub(crate) use pending::*;
pub(crate) mod manual;
pub(crate) use manual::*;
pub(crate) mod extras;
pub(crate) use extras::*;

View file

@ -272,5 +272,11 @@ fn all_commands() -> Vec<poise::Command<Data, Error>> {
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(),
]
}