remove reactions from messages older than n days
This commit is contained in:
parent
a8d6a2b8d7
commit
94b12a1069
9 changed files with 86 additions and 18 deletions
9
.env.template
Normal file
9
.env.template
Normal file
|
@ -0,0 +1,9 @@
|
|||
DISCORD_BOT_TOKEN=
|
||||
DISCORD_DAYS_TO_DELETE_REACTION=
|
||||
DISCORD_LOG_CHANNEL_ID=
|
||||
DISCORD_WELCOME_CHANNEL_ID=
|
||||
|
||||
BOT_REDIS_URL="redis://localhost:6379"
|
||||
|
||||
RUST_LOG="refraction=debug"
|
||||
RUST_BACKTRACE=1
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -17,7 +17,7 @@ Cargo.lock
|
|||
# direnv secrets
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.template
|
||||
|
||||
# Nix
|
||||
.direnv/
|
||||
|
|
9
Cargo.lock
generated
9
Cargo.lock
generated
|
@ -241,15 +241,17 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.31"
|
||||
version = "0.4.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
|
||||
checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"windows-targets 0.48.5",
|
||||
"wasm-bindgen",
|
||||
"windows-targets 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1445,6 +1447,7 @@ dependencies = [
|
|||
name = "refraction"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"color-eyre",
|
||||
"dotenvy",
|
||||
"enum_dispatch",
|
||||
|
|
|
@ -15,6 +15,7 @@ serde = "1.0.196"
|
|||
serde_json = "1.0.112"
|
||||
|
||||
[dependencies]
|
||||
chrono = "0.4.37"
|
||||
color-eyre = "0.6.2"
|
||||
dotenvy = "0.15.7"
|
||||
enum_dispatch = "0.3.12"
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use eyre::{Context, Result};
|
||||
use log::{info, warn};
|
||||
use poise::serenity_prelude::ChannelId;
|
||||
|
||||
|
@ -12,6 +13,7 @@ pub struct RefractionChannels {
|
|||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Config {
|
||||
pub channels: RefractionChannels,
|
||||
pub days_to_delete_reaction: i64,
|
||||
}
|
||||
|
||||
impl RefractionChannels {
|
||||
|
@ -22,15 +24,15 @@ impl RefractionChannels {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn new_from_env() -> Self {
|
||||
let log_channel_id = Self::get_channel_from_env("DISCORD_LOG_CHANNEL_ID");
|
||||
pub fn from_env() -> Self {
|
||||
let log_channel_id = Self::channel_from_env("DISCORD_LOG_CHANNEL_ID");
|
||||
if let Some(channel_id) = log_channel_id {
|
||||
info!("Log channel is {channel_id}");
|
||||
} else {
|
||||
warn!("DISCORD_LOG_CHANNEL_ID is empty; this will disable logging in your server.");
|
||||
}
|
||||
|
||||
let welcome_channel_id = Self::get_channel_from_env("DISCORD_WELCOME_CHANNEL_ID");
|
||||
let welcome_channel_id = Self::channel_from_env("DISCORD_WELCOME_CHANNEL_ID");
|
||||
if let Some(channel_id) = welcome_channel_id {
|
||||
info!("Welcome channel is {channel_id}");
|
||||
} else {
|
||||
|
@ -40,7 +42,7 @@ impl RefractionChannels {
|
|||
Self::new(log_channel_id, welcome_channel_id)
|
||||
}
|
||||
|
||||
fn get_channel_from_env(var: &str) -> Option<ChannelId> {
|
||||
fn channel_from_env(var: &str) -> Option<ChannelId> {
|
||||
std::env::var(var)
|
||||
.ok()
|
||||
.and_then(|env_var| ChannelId::from_str(&env_var).ok())
|
||||
|
@ -48,13 +50,22 @@ impl RefractionChannels {
|
|||
}
|
||||
|
||||
impl Config {
|
||||
pub fn new(channels: RefractionChannels) -> Self {
|
||||
Self { channels }
|
||||
pub fn new(channels: RefractionChannels, days_to_delete_reaction: i64) -> Self {
|
||||
Self {
|
||||
channels,
|
||||
days_to_delete_reaction,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_env() -> Self {
|
||||
let channels = RefractionChannels::new_from_env();
|
||||
pub fn from_env() -> Result<Self> {
|
||||
let channels = RefractionChannels::from_env();
|
||||
let days_to_delete_reaction = std::env::var("DISCORD_DAYS_TO_DELETE_REACTION")
|
||||
.wrap_err("DISCORD_DAYS_TO_DELETE_REACTION is empty! This variable is required.")?
|
||||
.parse()
|
||||
.wrap_err("DISCORD_DAYS_TO_DELETE_REACTION is not a number!")?;
|
||||
|
||||
Self::new(channels)
|
||||
info!("Reactions will be deleted on messages older than {days_to_delete_reaction} days");
|
||||
|
||||
Ok(Self::new(channels, days_to_delete_reaction))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use eyre::Result;
|
||||
|
||||
mod bot;
|
||||
mod discord;
|
||||
|
||||
|
@ -15,10 +17,10 @@ impl Config {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn new_from_env() -> Self {
|
||||
pub fn from_env() -> Result<Self> {
|
||||
let bot = bot::Config::from_env();
|
||||
let discord = discord::Config::from_env();
|
||||
let discord = discord::Config::from_env()?;
|
||||
|
||||
Self::new(bot, discord)
|
||||
Ok(Self::new(bot, discord))
|
||||
}
|
||||
}
|
||||
|
|
39
src/handlers/event/block_reaction.rs
Normal file
39
src/handlers/event/block_reaction.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
use crate::Data;
|
||||
|
||||
use chrono::Duration;
|
||||
use eyre::{Context as _, Result};
|
||||
use log::{debug, trace};
|
||||
use poise::serenity_prelude::{Context, Reaction, Timestamp};
|
||||
|
||||
pub async fn handle(ctx: &Context, reaction: &Reaction, data: &Data) -> Result<()> {
|
||||
let reaction_type = reaction.emoji.clone();
|
||||
let reactor = reaction.user_id;
|
||||
let message = reaction.message(ctx).await.wrap_err_with(|| {
|
||||
format!(
|
||||
"Couldn't get message {} from reaction! We won't be able to check if it's old",
|
||||
reaction.message_id
|
||||
)
|
||||
})?;
|
||||
|
||||
let time_sent = message.timestamp.to_utc();
|
||||
let age = Timestamp::now().signed_duration_since(time_sent);
|
||||
let max_days = Duration::days(data.config.discord.days_to_delete_reaction);
|
||||
|
||||
if age >= max_days {
|
||||
// NOTE: if we for some reason **didn't** get the user_id associated with the reaction,
|
||||
// this will clear **all** reactions of this type. this is intentional as older reactions
|
||||
// being removed > harmful reactions being kept
|
||||
debug!(
|
||||
"Removing reaction {reaction_type} from message {}",
|
||||
message.id
|
||||
);
|
||||
message.delete_reaction(ctx, reactor, reaction_type).await?;
|
||||
} else {
|
||||
trace!(
|
||||
"Keeping reaction {reaction_type} for message {}",
|
||||
message.id
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -5,6 +5,7 @@ use poise::serenity_prelude::{ActivityData, Context, FullEvent, OnlineStatus};
|
|||
use poise::FrameworkContext;
|
||||
|
||||
mod analyze_logs;
|
||||
mod block_reaction;
|
||||
mod delete_on_reaction;
|
||||
mod eta;
|
||||
mod expand_link;
|
||||
|
@ -71,6 +72,8 @@ pub async fn handle(
|
|||
add_reaction.message_id.to_string(),
|
||||
add_reaction.user_id.unwrap_or_default().to_string()
|
||||
);
|
||||
|
||||
block_reaction::handle(ctx, add_reaction, data).await?;
|
||||
delete_on_reaction::handle(ctx, add_reaction).await?;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ async fn setup(
|
|||
_: &serenity::Ready,
|
||||
framework: &Framework<Data, Error>,
|
||||
) -> Result<Data, Error> {
|
||||
let config = Config::new_from_env();
|
||||
let config = Config::from_env()?;
|
||||
|
||||
let storage = if let Some(url) = &config.bot.redis_url {
|
||||
Some(Storage::from_url(url)?)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue