From eac793aca805e2a5fd154115aea9b3805a4d3a6d Mon Sep 17 00:00:00 2001 From: bluepython508 <16466646+bluepython508@users.noreply.github.com> Date: Sun, 8 Dec 2024 20:45:41 +0000 Subject: [PATCH] Add currently-playing and st granting commands --- src/main.rs | 85 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 73 insertions(+), 12 deletions(-) diff --git a/src/main.rs b/src/main.rs index b92cc75..8761bd3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ use std::path::Path; use eyre::{bail, Context as _, Error, OptionExt, Result}; -use futures::FutureExt; +use futures::{FutureExt, TryStreamExt}; use poise::serenity_prelude as serenity; use rand::seq::SliceRandom; use rusqlite::OptionalExtension; @@ -13,6 +13,8 @@ struct GuildData { guild: serenity::GuildId, town_square: serenity::ChannelId, cottages: serenity::ChannelId, + st: serenity::RoleId, + currently_playing: serenity::RoleId, } struct Data(tokio::sync::Mutex); @@ -20,7 +22,7 @@ struct Data(tokio::sync::Mutex); impl Data { fn open(path: impl AsRef) -> Result { let conn = rusqlite::Connection::open(path)?; - conn.execute_batch("CREATE TABLE IF NOT EXISTS guilds(guild TEXT PRIMARY KEY NOT NULL, town_square TEXT NOT NULL, cottages TEXT NOT NULL);")?; + conn.execute_batch("CREATE TABLE IF NOT EXISTS guilds(guild TEXT PRIMARY KEY NOT NULL, town_square TEXT NOT NULL, cottages TEXT NOT NULL, st TEXT, currently_playing TEXT);")?; Ok(Self(tokio::sync::Mutex::new(conn))) } @@ -28,12 +30,14 @@ impl Data { self.0 .lock() .await - .prepare_cached("SELECT guild, town_square, cottages FROM guilds WHERE guild = ?;")? + .prepare_cached("SELECT guild, town_square, cottages, st, currently_playing FROM guilds WHERE guild = ?;")? .query_row(rusqlite::params![guild.to_string()], |row| { Ok(GuildData { guild: row.get_unwrap::<_, String>("guild").parse().unwrap(), town_square: row.get_unwrap::<_, String>("town_square").parse().unwrap(), cottages: row.get_unwrap::<_, String>("cottages").parse().unwrap(), + st: row.get_unwrap::<_, String>("st").parse().unwrap(), + currently_playing: row.get_unwrap::<_, String>("currently_playing").parse().unwrap(), }) }) .optional()? @@ -46,16 +50,26 @@ impl Data { guild, town_square, cottages, + st, + currently_playing, }: GuildData, ) -> Result<()> { self.0 .lock() .await - .prepare_cached("INSERT INTO guilds (guild, town_square, cottages) VALUES (?, ?, ?) ON CONFLICT(guild) DO UPDATE SET town_square = excluded.town_square, cottages = excluded.cottages")? + .prepare_cached("INSERT INTO guilds (guild, town_square, cottages, st, currently_playing) VALUES (?, ?, ?, ?, ?) + ON CONFLICT(guild) DO UPDATE SET + town_square = excluded.town_square, + cottages = excluded.cottages, + st = excluded.st, + currently_playing = excluded.currently_playing; + ")? .execute(rusqlite::params![ guild.to_string(), town_square.to_string(), - cottages.to_string() + cottages.to_string(), + st.to_string(), + currently_playing.to_string(), ])?; Ok(()) } @@ -141,21 +155,68 @@ async fn dawn(ctx: Context<'_>) -> Result<()> { Ok(()) } +#[poise::command(slash_command, ephemeral)] +async fn st(ctx: Context<'_>, mut spectators: Vec) -> Result<()> { + let guild = ctx + .guild_id() + .ok_or_eyre("This bot only works in servers")?; + let GuildData { st, .. } = ctx.data().get(guild).await?; + spectators.push(ctx.author().id); + + + guild + .members_iter(&ctx) + .try_for_each(|member| { + let spectators = &spectators; + async move { + if spectators.contains(&member.user.id) { + member.add_role(&ctx, st).await + } else { + member.remove_role(&ctx, st).await + } + } + }) + .await?; + + ctx.reply(format!("{} members given ST", spectators.len())) + .await?; + + Ok(()) +} + +#[poise::command(slash_command, ephemeral)] +async fn currently_playing(ctx: Context<'_>, players: Vec) -> Result<()> { + let guild = ctx.guild_id().ok_or_eyre("This bot only works in servers")?; + let GuildData { currently_playing, .. } = ctx.data().get(guild).await?; + for player in &players { + guild.member(&ctx, player).await?.add_role(&ctx, currently_playing).await?; + } + ctx.reply(format!("Given {} members currently playing", players.len())).await?; + Ok(()) +} + #[poise::command(slash_command, prefix_command, ephemeral)] async fn configure( ctx: Context<'_>, #[channel_types("Voice")] town_square: serenity::ChannelId, #[channel_types("Category")] cottages: serenity::ChannelId, + st: serenity::RoleId, + currently_playing: serenity::RoleId ) -> Result<()> { let guild = ctx .guild_id() .ok_or_eyre("This bot only works in servers")?; - ctx.data().insert(GuildData { - guild, - town_square, - cottages, - }).await?; + ctx.data() + .insert(GuildData { + guild, + town_square, + cottages, + st, + currently_playing, + }) + .await?; + ctx.reply("Configured successfully!").await?; Ok(()) } @@ -189,11 +250,11 @@ async fn main() -> Result<()> { ) .wrap_err("A Discord token is required")?; let dbfile = std::env::var_os("DB_FILE").ok_or_eyre("A database file is required")?; - let intents = serenity::GatewayIntents::non_privileged(); + let intents = serenity::GatewayIntents::non_privileged() | serenity::GatewayIntents::GUILD_MEMBERS; let framework = poise::Framework::builder() .options(poise::FrameworkOptions { - commands: vec![dusk(), dawn(), configure()], + commands: vec![dusk(), dawn(), st(), currently_playing(), configure()], event_handler: |ctx, ev, ctxf, data| on_event(ctx, ev, ctxf, data).boxed(), ..Default::default() })