Add currently-playing and st granting commands

This commit is contained in:
bluepython508
2024-12-08 20:45:41 +00:00
parent 49538af0ad
commit eac793aca8

View File

@@ -1,7 +1,7 @@
use std::path::Path; use std::path::Path;
use eyre::{bail, Context as _, Error, OptionExt, Result}; use eyre::{bail, Context as _, Error, OptionExt, Result};
use futures::FutureExt; use futures::{FutureExt, TryStreamExt};
use poise::serenity_prelude as serenity; use poise::serenity_prelude as serenity;
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
use rusqlite::OptionalExtension; use rusqlite::OptionalExtension;
@@ -13,6 +13,8 @@ struct GuildData {
guild: serenity::GuildId, guild: serenity::GuildId,
town_square: serenity::ChannelId, town_square: serenity::ChannelId,
cottages: serenity::ChannelId, cottages: serenity::ChannelId,
st: serenity::RoleId,
currently_playing: serenity::RoleId,
} }
struct Data(tokio::sync::Mutex<rusqlite::Connection>); struct Data(tokio::sync::Mutex<rusqlite::Connection>);
@@ -20,7 +22,7 @@ struct Data(tokio::sync::Mutex<rusqlite::Connection>);
impl Data { impl Data {
fn open(path: impl AsRef<Path>) -> Result<Self> { fn open(path: impl AsRef<Path>) -> Result<Self> {
let conn = rusqlite::Connection::open(path)?; 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))) Ok(Self(tokio::sync::Mutex::new(conn)))
} }
@@ -28,12 +30,14 @@ impl Data {
self.0 self.0
.lock() .lock()
.await .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| { .query_row(rusqlite::params![guild.to_string()], |row| {
Ok(GuildData { Ok(GuildData {
guild: row.get_unwrap::<_, String>("guild").parse().unwrap(), guild: row.get_unwrap::<_, String>("guild").parse().unwrap(),
town_square: row.get_unwrap::<_, String>("town_square").parse().unwrap(), town_square: row.get_unwrap::<_, String>("town_square").parse().unwrap(),
cottages: row.get_unwrap::<_, String>("cottages").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()? .optional()?
@@ -46,16 +50,26 @@ impl Data {
guild, guild,
town_square, town_square,
cottages, cottages,
st,
currently_playing,
}: GuildData, }: GuildData,
) -> Result<()> { ) -> Result<()> {
self.0 self.0
.lock() .lock()
.await .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![ .execute(rusqlite::params![
guild.to_string(), guild.to_string(),
town_square.to_string(), town_square.to_string(),
cottages.to_string() cottages.to_string(),
st.to_string(),
currently_playing.to_string(),
])?; ])?;
Ok(()) Ok(())
} }
@@ -141,21 +155,68 @@ async fn dawn(ctx: Context<'_>) -> Result<()> {
Ok(()) Ok(())
} }
#[poise::command(slash_command, ephemeral)]
async fn st(ctx: Context<'_>, mut spectators: Vec<serenity::UserId>) -> 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<serenity::UserId>) -> 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)] #[poise::command(slash_command, prefix_command, ephemeral)]
async fn configure( async fn configure(
ctx: Context<'_>, ctx: Context<'_>,
#[channel_types("Voice")] town_square: serenity::ChannelId, #[channel_types("Voice")] town_square: serenity::ChannelId,
#[channel_types("Category")] cottages: serenity::ChannelId, #[channel_types("Category")] cottages: serenity::ChannelId,
st: serenity::RoleId,
currently_playing: serenity::RoleId
) -> Result<()> { ) -> Result<()> {
let guild = ctx let guild = ctx
.guild_id() .guild_id()
.ok_or_eyre("This bot only works in servers")?; .ok_or_eyre("This bot only works in servers")?;
ctx.data().insert(GuildData { ctx.data()
guild, .insert(GuildData {
town_square, guild,
cottages, town_square,
}).await?; cottages,
st,
currently_playing,
})
.await?;
ctx.reply("Configured successfully!").await?; ctx.reply("Configured successfully!").await?;
Ok(()) Ok(())
} }
@@ -189,11 +250,11 @@ async fn main() -> Result<()> {
) )
.wrap_err("A Discord token is required")?; .wrap_err("A Discord token is required")?;
let dbfile = std::env::var_os("DB_FILE").ok_or_eyre("A database file 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() let framework = poise::Framework::builder()
.options(poise::FrameworkOptions { .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(), event_handler: |ctx, ev, ctxf, data| on_event(ctx, ev, ctxf, data).boxed(),
..Default::default() ..Default::default()
}) })