Skip to content

Commit

Permalink
Truly implement vote reminders
Browse files Browse the repository at this point in the history
  • Loading branch information
tazz4843 committed Dec 25, 2023
1 parent 9d3b04c commit 897176c
Show file tree
Hide file tree
Showing 12 changed files with 175 additions and 1 deletion.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions scripty_bot_utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ license = "EUPL-1.2"

[dependencies]
uuid = { version = "1", features = ["rand"] }
time = "0.3"
dashmap = "5"
tracing = "0.1"
once_cell = "1"
Expand Down
1 change: 1 addition & 0 deletions scripty_bot_utils/src/background_tasks/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,5 @@ pub fn init_background_tasks(ctx: Context) {
init_task!(crate::background_tasks::tasks::StatusUpdater, ctx);
init_task!(crate::background_tasks::tasks::CommandLatencyClearer, ctx);
init_task!(crate::background_tasks::tasks::BotListUpdater, ctx);
init_task!(crate::background_tasks::tasks::VoteReminderTask, ctx);
}
112 changes: 112 additions & 0 deletions scripty_bot_utils/src/background_tasks/tasks/bot_vote_reminder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
use std::{fmt, time::Duration};

use serenity::{
all::UserId,
builder::{CreateEmbed, CreateMessage},
client::Context as SerenityContext,
futures::StreamExt,

Check warning on line 7 in scripty_bot_utils/src/background_tasks/tasks/bot_vote_reminder.rs

View workflow job for this annotation

GitHub Actions / Clippy Output

unused import: `futures::StreamExt`

warning: unused import: `futures::StreamExt` --> scripty_bot_utils/src/background_tasks/tasks/bot_vote_reminder.rs:7:2 | 7 | futures::StreamExt, | ^^^^^^^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default
};

use crate::{background_tasks::core::BackgroundTask, Error};

/// Sends vote reminders to users every minute.
pub struct VoteReminderTask {
ctx: SerenityContext,
}

#[async_trait]
impl BackgroundTask for VoteReminderTask {
async fn init(ctx: SerenityContext) -> Result<Self, Error> {
Ok(Self { ctx })
}

fn interval(&mut self) -> Duration {
Duration::from_secs(60)
}

async fn run(&mut self) {
let mut vote_query = sqlx::query!(
"DELETE FROM vote_reminders WHERE next_reminder < NOW() RETURNING user_id, site_id, \
next_reminder"
)
.fetch_many(scripty_db::get_db());

while let Some(user) = vote_query.next().await {
let user = match user.map(|u| u.right()) {
Ok(Some(user)) => user,
Ok(None) => {
error!("got no user from vote reminder query");
continue;
}
Err(err) => {
error!("failed to get vote reminder: {}", err);
continue;
}
};
let site: VoteList = user.site_id.into();
let user_id = user.user_id as u64;
let reminder_unix_ts = user.next_reminder.assume_utc().unix_timestamp();

let msg =
CreateMessage::new().embed(CreateEmbed::new().title("Vote reminder").description(
format!(
"You can vote for Scripty on {} again, as of <t:{}:R>. You can do so at \
{}. Thanks for your support!",
site,
reminder_unix_ts,
site.vote_url()
),
));
let ctx2 = self.ctx.clone();
tokio::spawn(async move {
let res = match UserId::new(user_id).create_dm_channel(&ctx2.http).await {
Ok(channel) => channel.send_message(&ctx2.http, msg).await.map(|_| ()),
Err(e) => Err(e),
};
if let Err(e) = res {
error!("failed to send vote reminder: {}", e);
}
});
}
}

fn timeout(&mut self) -> Option<Duration> {
Some(Duration::from_secs(5))
}
}

pub enum VoteList {
TopGg = 1,
DiscordServicesNet = 2,
WumpusStore = 3,
}
impl From<i16> for VoteList {
fn from(i: i16) -> Self {
match i {
1 => Self::TopGg,
2 => Self::DiscordServicesNet,
3 => Self::WumpusStore,
_ => panic!("invalid vote list id"),
}
}
}

impl fmt::Display for VoteList {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::TopGg => write!(f, "top.gg"),
Self::DiscordServicesNet => write!(f, "discordservices.net"),
Self::WumpusStore => write!(f, "wumpus.store"),
}
}
}

impl VoteList {
pub fn vote_url(&self) -> &'static str {
match self {
Self::TopGg => "https://top.gg/bot/699453633624064849/vote",
Self::DiscordServicesNet => "https://discordservices.net/bot/scripty",
Self::WumpusStore => "https://wumpus.store/bot/811652199100317726/vote",
}
}
}
2 changes: 2 additions & 0 deletions scripty_bot_utils/src/background_tasks/tasks/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
mod basic_stats_update;
mod bot_list_poster;
mod bot_vote_reminder;
mod cmd_latency_clear;
mod prometheus_latency_update;
mod status_update;

pub use basic_stats_update::*;
pub use bot_list_poster::*;
pub use bot_vote_reminder::*;
pub use cmd_latency_clear::*;
pub use prometheus_latency_update::*;
pub use status_update::*;
2 changes: 1 addition & 1 deletion scripty_bot_utils/src/dm_support.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ impl DmSupportStatus {
.author
.global_name
.as_ref()
.unwrap_or_else(|| &message.author.name)
.unwrap_or(&message.author.name)
.to_string(),
);

Expand Down
1 change: 1 addition & 0 deletions scripty_commands/src/cmds/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub mod premium;
mod register_cmds;
mod terms_of_service;
mod throw_error;
mod vote_reminders;

pub use admin::*;
pub use data_storage::*;
Expand Down
36 changes: 36 additions & 0 deletions scripty_commands/src/cmds/vote_reminders.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use crate::{Context, Error};

/// Opt in or out of vote reminders
#[poise::command(prefix_command, slash_command)]
pub async fn vote_reminder(ctx: Context<'_>, enabled: bool) -> Result<(), Error> {
let resolved_language =
scripty_i18n::get_resolved_language(ctx.author().id.get(), ctx.guild_id().map(|g| g.get()))
.await;

let db = scripty_db::get_db();
sqlx::query!(
"INSERT INTO users (user_id) VALUES ($1) ON CONFLICT ON CONSTRAINT users_pkey DO NOTHING",
ctx.author().id.get() as i64
)
.execute(db)
.await?;
sqlx::query!(
"UPDATE users SET vote_reminder_disabled = $1 WHERE user_id = $2",
enabled,
ctx.author().id.get() as i64
)
.execute(db)
.await?;

ctx.say(format_message!(
resolved_language,
if enabled {
"vote-reminders-enabled"
} else {
"vote-reminders-disabled"
}
))
.await?;

Ok(())
}
8 changes: 8 additions & 0 deletions scripty_i18n/locales/en.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,14 @@ automod-list-rules-embed-field-value = Type: { $ruleType }
automod-list-rules-footer = Page { $page } of { $maxPage }
automod-list-rules-no-rules = You don't have any rules!
## vote reminder command
cmds_vote_reminder = vote_reminder
.description = Toggle whether Scripty will remind you to vote for the bot after the time limit has passed.
.enabled = enabled
.enabled-description = Enable vote reminders?
vote-reminders-enabled = Vote reminders enabled.
vote-reminders-disabled = Vote reminders disabled.
## blocked entities description

blocked-entity-no-reason-given = No reason was given for the block.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ pub async fn discordservices_net_incoming_webhook(
.await?;

// if they're opted in, set up a reminder for 20 hours from now
if opted_out {
return Ok(());
}

sqlx::query!(
"INSERT INTO vote_reminders (user_id, site_id, next_reminder)
VALUES ($1, 2, NOW() + INTERVAL '20 hours')
Expand Down
4 changes: 4 additions & 0 deletions scripty_webserver/src/endpoints/webhooks/top_gg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ pub async fn top_gg_incoming_webhook(
.await?;

// if they're opted in, set up a reminder for 12 hours from now
if opted_out {
return Ok(());
}

sqlx::query!(
"INSERT INTO vote_reminders (user_id, site_id, next_reminder)
VALUES ($1, 1, NOW() + INTERVAL '12 hours')
Expand Down
4 changes: 4 additions & 0 deletions scripty_webserver/src/endpoints/webhooks/wumpus_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ pub async fn wumpus_store_incoming_webhook(
.await?;

// if they're opted in, set up a reminder for 12 hours from now
if opted_out {
return Ok(());
}

sqlx::query!(
"INSERT INTO vote_reminders (user_id, site_id, next_reminder)
VALUES ($1, 3, NOW() + INTERVAL '12 hours')
Expand Down

0 comments on commit 897176c

Please sign in to comment.