Support ephemeral reminder confirmations
This commit is contained in:
parent
4b42966284
commit
aa931328b0
@ -1,2 +1 @@
|
||||
-- Add migration script here
|
||||
ALTER TABLE reminders ADD COLUMN `thread_id` BIGINT DEFAULT NULL;
|
||||
|
1
migrations/20230511180231_ephemeral_confirmations.sql
Normal file
1
migrations/20230511180231_ephemeral_confirmations.sql
Normal file
@ -0,0 +1 @@
|
||||
ALTER TABLE guilds ADD COLUMN ephemeral_confirmations BOOL NOT NULL DEFAULT 0;
|
@ -102,6 +102,78 @@ You may want to use one of the popular timezones below, otherwise click [here](h
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Configure server settings
|
||||
#[poise::command(
|
||||
slash_command,
|
||||
rename = "settings",
|
||||
identifying_name = "settings",
|
||||
guild_only = true
|
||||
)]
|
||||
pub async fn settings(_ctx: Context<'_>) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Configure ephemeral setup
|
||||
#[poise::command(
|
||||
slash_command,
|
||||
rename = "ephemeral",
|
||||
identifying_name = "ephemeral_confirmations",
|
||||
guild_only = true
|
||||
)]
|
||||
pub async fn ephemeral_confirmations(_ctx: Context<'_>) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set reminder confirmations to be sent "ephemerally" (private and cleared automatically)
|
||||
#[poise::command(
|
||||
slash_command,
|
||||
rename = "on",
|
||||
identifying_name = "set_ephemeral_confirmations",
|
||||
guild_only = true
|
||||
)]
|
||||
pub async fn set_ephemeral_confirmations(ctx: Context<'_>) -> Result<(), Error> {
|
||||
let mut guild_data = ctx.guild_data().await.unwrap()?;
|
||||
guild_data.ephemeral_confirmations = true;
|
||||
guild_data.commit_changes(&ctx.data().database).await;
|
||||
|
||||
ctx.send(|r| {
|
||||
r.ephemeral(true).embed(|e| {
|
||||
e.title("Confirmations ephemeral")
|
||||
.description("Reminder confirmations will be sent privately, and removed when your client restarts.")
|
||||
.color(*THEME_COLOR)
|
||||
})
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set reminder confirmations to persist indefinitely
|
||||
#[poise::command(
|
||||
slash_command,
|
||||
rename = "off",
|
||||
identifying_name = "unset_ephemeral_confirmations",
|
||||
guild_only = true
|
||||
)]
|
||||
pub async fn unset_ephemeral_confirmations(ctx: Context<'_>) -> Result<(), Error> {
|
||||
let mut guild_data = ctx.guild_data().await.unwrap()?;
|
||||
guild_data.ephemeral_confirmations = false;
|
||||
guild_data.commit_changes(&ctx.data().database).await;
|
||||
|
||||
ctx.send(|r| {
|
||||
r.ephemeral(true).embed(|e| {
|
||||
e.title("Confirmations public")
|
||||
.description(
|
||||
"Reminder confirmations will be sent as regular messages, and won't be removed automatically.",
|
||||
)
|
||||
.color(*THEME_COLOR)
|
||||
})
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Configure whether other users can set reminders to your direct messages
|
||||
#[poise::command(slash_command, rename = "dm", identifying_name = "allowed_dm")]
|
||||
pub async fn allowed_dm(_ctx: Context<'_>) -> Result<(), Error> {
|
||||
@ -109,7 +181,7 @@ pub async fn allowed_dm(_ctx: Context<'_>) -> Result<(), Error> {
|
||||
}
|
||||
|
||||
/// Allow other users to set reminders in your direct messages
|
||||
#[poise::command(slash_command, rename = "allow", identifying_name = "allowed_dm")]
|
||||
#[poise::command(slash_command, rename = "allow", identifying_name = "set_allowed_dm")]
|
||||
pub async fn set_allowed_dm(ctx: Context<'_>) -> Result<(), Error> {
|
||||
let mut user_data = ctx.author_data().await?;
|
||||
user_data.allowed_dm = true;
|
||||
@ -128,7 +200,7 @@ pub async fn set_allowed_dm(ctx: Context<'_>) -> Result<(), Error> {
|
||||
}
|
||||
|
||||
/// Block other users from setting reminders in your direct messages
|
||||
#[poise::command(slash_command, rename = "block", identifying_name = "allowed_dm")]
|
||||
#[poise::command(slash_command, rename = "block", identifying_name = "unset_allowed_dm")]
|
||||
pub async fn unset_allowed_dm(ctx: Context<'_>) -> Result<(), Error> {
|
||||
let mut user_data = ctx.author_data().await?;
|
||||
user_data.allowed_dm = false;
|
||||
|
@ -645,7 +645,13 @@ async fn create_reminder(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
ctx.defer().await?;
|
||||
let ephemeral =
|
||||
ctx.guild_data().await.map_or(false, |gr| gr.map_or(false, |g| g.ephemeral_confirmations));
|
||||
if ephemeral {
|
||||
ctx.defer_ephemeral().await?;
|
||||
} else {
|
||||
ctx.defer().await?;
|
||||
}
|
||||
|
||||
let user_data = ctx.author_data().await.unwrap();
|
||||
let timezone = timezone.unwrap_or(ctx.timezone().await);
|
||||
@ -692,9 +698,10 @@ async fn create_reminder(
|
||||
},
|
||||
)
|
||||
} else {
|
||||
ctx.say(
|
||||
"`repeat` is only available to Patreon subscribers or self-hosted users",
|
||||
)
|
||||
ctx.send(|b| {
|
||||
b.content(
|
||||
"`repeat` is only available to Patreon subscribers or self-hosted users")
|
||||
})
|
||||
.await?;
|
||||
|
||||
return Ok(());
|
||||
@ -704,13 +711,18 @@ async fn create_reminder(
|
||||
};
|
||||
|
||||
if processed_interval.is_none() && interval.is_some() {
|
||||
ctx.say(
|
||||
"Repeat interval could not be processed. Try similar to `1 hour` or `4 days`",
|
||||
)
|
||||
ctx.send(|b| {
|
||||
b.content(
|
||||
"Repeat interval could not be processed. Try similar to `1 hour` or `4 days`")
|
||||
})
|
||||
.await?;
|
||||
} else if processed_expires.is_none() && expires.is_some() {
|
||||
ctx.say("Expiry time failed to process. Please make it as clear as possible")
|
||||
.await?;
|
||||
ctx.send(|b| {
|
||||
b.ephemeral(true).content(
|
||||
"Expiry time failed to process. Please make it as clear as possible",
|
||||
)
|
||||
})
|
||||
.await?;
|
||||
} else {
|
||||
let mut builder = MultiReminderBuilder::new(&ctx, ctx.guild_id())
|
||||
.author(user_data)
|
||||
|
12
src/main.rs
12
src/main.rs
@ -91,7 +91,7 @@ async fn _main(tx: Sender<()>) -> Result<(), Box<dyn StdError + Send + Sync>> {
|
||||
if Path::new("/etc/reminder-rs/config.env").exists() {
|
||||
dotenv::from_path("/etc/reminder-rs/config.env")?;
|
||||
} else {
|
||||
dotenv::from_path(".env")?;
|
||||
let _ = dotenv::dotenv();
|
||||
}
|
||||
|
||||
let discord_token = env::var("DISCORD_TOKEN").expect("Missing DISCORD_TOKEN from environment");
|
||||
@ -112,6 +112,16 @@ async fn _main(tx: Sender<()>) -> Result<(), Box<dyn StdError + Send + Sync>> {
|
||||
],
|
||||
..moderation_cmds::allowed_dm()
|
||||
},
|
||||
poise::Command {
|
||||
subcommands: vec![poise::Command {
|
||||
subcommands: vec![
|
||||
moderation_cmds::set_ephemeral_confirmations(),
|
||||
moderation_cmds::unset_ephemeral_confirmations(),
|
||||
],
|
||||
..moderation_cmds::ephemeral_confirmations()
|
||||
}],
|
||||
..moderation_cmds::settings()
|
||||
},
|
||||
moderation_cmds::webhook(),
|
||||
poise::Command {
|
||||
subcommands: vec![
|
||||
|
@ -22,9 +22,7 @@ impl ChannelData {
|
||||
|
||||
if let Ok(c) = sqlx::query_as_unchecked!(
|
||||
Self,
|
||||
"
|
||||
SELECT id, name, nudge, blacklisted, webhook_id, webhook_token, paused, paused_until FROM channels WHERE channel = ?
|
||||
",
|
||||
"SELECT id, name, nudge, blacklisted, webhook_id, webhook_token, paused, paused_until FROM channels WHERE channel = ?",
|
||||
channel_id
|
||||
)
|
||||
.fetch_one(pool)
|
||||
@ -37,9 +35,7 @@ SELECT id, name, nudge, blacklisted, webhook_id, webhook_token, paused, paused_u
|
||||
let (guild_id, channel_name) = if let Some((a, b)) = props { (Some(a), Some(b)) } else { (None, None) };
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
INSERT IGNORE INTO channels (channel, name, guild_id) VALUES (?, ?, (SELECT id FROM guilds WHERE guild = ?))
|
||||
",
|
||||
"INSERT IGNORE INTO channels (channel, name, guild_id) VALUES (?, ?, (SELECT id FROM guilds WHERE guild = ?))",
|
||||
channel_id,
|
||||
channel_name,
|
||||
guild_id
|
||||
|
48
src/models/guild_data.rs
Normal file
48
src/models/guild_data.rs
Normal file
@ -0,0 +1,48 @@
|
||||
use poise::serenity_prelude::GuildId;
|
||||
use sqlx::MySqlPool;
|
||||
|
||||
pub struct GuildData {
|
||||
pub ephemeral_confirmations: bool,
|
||||
pub id: u32,
|
||||
}
|
||||
|
||||
impl GuildData {
|
||||
pub async fn from_guild(
|
||||
guild_id: GuildId,
|
||||
pool: &MySqlPool,
|
||||
) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
|
||||
if let Ok(c) = sqlx::query_as_unchecked!(
|
||||
Self,
|
||||
"SELECT id, ephemeral_confirmations FROM guilds WHERE guild = ?",
|
||||
guild_id.0
|
||||
)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
{
|
||||
Ok(c)
|
||||
} else {
|
||||
sqlx::query!("INSERT IGNORE INTO guilds (guild) VALUES (?)", guild_id.0)
|
||||
.execute(&pool.clone())
|
||||
.await?;
|
||||
|
||||
Ok(sqlx::query_as_unchecked!(
|
||||
Self,
|
||||
"SELECT id, ephemeral_confirmations FROM guilds WHERE guild = ?",
|
||||
guild_id.0
|
||||
)
|
||||
.fetch_one(pool)
|
||||
.await?)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn commit_changes(&self, pool: &MySqlPool) {
|
||||
sqlx::query!(
|
||||
"UPDATE guilds SET ephemeral_confirmations = ? WHERE id = ?",
|
||||
self.ephemeral_confirmations,
|
||||
self.id
|
||||
)
|
||||
.execute(pool)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
pub mod channel_data;
|
||||
pub mod command_macro;
|
||||
pub mod guild_data;
|
||||
pub mod reminder;
|
||||
pub mod timer;
|
||||
pub mod user_data;
|
||||
@ -8,7 +9,7 @@ use chrono_tz::Tz;
|
||||
use poise::serenity_prelude::{async_trait, model::id::UserId, ChannelType};
|
||||
|
||||
use crate::{
|
||||
models::{channel_data::ChannelData, user_data::UserData},
|
||||
models::{channel_data::ChannelData, guild_data::GuildData, user_data::UserData},
|
||||
CommandMacro, Context, Data, Error, GuildId,
|
||||
};
|
||||
|
||||
@ -18,6 +19,8 @@ pub trait CtxData {
|
||||
|
||||
async fn author_data(&self) -> Result<UserData, Error>;
|
||||
|
||||
async fn guild_data(&self) -> Option<Result<GuildData, Error>>;
|
||||
|
||||
async fn timezone(&self) -> Tz;
|
||||
|
||||
async fn channel_data(&self) -> Result<ChannelData, Error>;
|
||||
@ -27,17 +30,22 @@ pub trait CtxData {
|
||||
|
||||
#[async_trait]
|
||||
impl CtxData for Context<'_> {
|
||||
async fn user_data<U: Into<UserId> + Send>(
|
||||
&self,
|
||||
user_id: U,
|
||||
) -> Result<UserData, Box<dyn std::error::Error + Sync + Send>> {
|
||||
async fn user_data<U: Into<UserId> + Send>(&self, user_id: U) -> Result<UserData, Error> {
|
||||
UserData::from_user(user_id, &self.discord(), &self.data().database).await
|
||||
}
|
||||
|
||||
async fn author_data(&self) -> Result<UserData, Box<dyn std::error::Error + Sync + Send>> {
|
||||
async fn author_data(&self) -> Result<UserData, Error> {
|
||||
UserData::from_user(&self.author().id, &self.discord(), &self.data().database).await
|
||||
}
|
||||
|
||||
async fn guild_data(&self) -> Option<Result<GuildData, Error>> {
|
||||
if let Some(guild_id) = self.guild_id() {
|
||||
Some(GuildData::from_guild(guild_id, &self.data().database).await)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
async fn timezone(&self) -> Tz {
|
||||
UserData::timezone_of(self.author().id, &self.data().database).await
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user