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
|
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -17,7 +17,7 @@ Cargo.lock
|
||||||
# direnv secrets
|
# direnv secrets
|
||||||
.env
|
.env
|
||||||
.env.*
|
.env.*
|
||||||
!.env.example
|
!.env.template
|
||||||
|
|
||||||
# Nix
|
# Nix
|
||||||
.direnv/
|
.direnv/
|
||||||
|
@ -29,4 +29,4 @@ repl-result-out*
|
||||||
*.rdb
|
*.rdb
|
||||||
|
|
||||||
# JetBrains
|
# JetBrains
|
||||||
.idea/
|
.idea/
|
||||||
|
|
9
Cargo.lock
generated
9
Cargo.lock
generated
|
@ -241,15 +241,17 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
version = "0.4.31"
|
version = "0.4.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
|
checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"android-tzdata",
|
"android-tzdata",
|
||||||
"iana-time-zone",
|
"iana-time-zone",
|
||||||
|
"js-sys",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"serde",
|
"serde",
|
||||||
"windows-targets 0.48.5",
|
"wasm-bindgen",
|
||||||
|
"windows-targets 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1445,6 +1447,7 @@ dependencies = [
|
||||||
name = "refraction"
|
name = "refraction"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
"color-eyre",
|
"color-eyre",
|
||||||
"dotenvy",
|
"dotenvy",
|
||||||
"enum_dispatch",
|
"enum_dispatch",
|
||||||
|
|
|
@ -15,6 +15,7 @@ serde = "1.0.196"
|
||||||
serde_json = "1.0.112"
|
serde_json = "1.0.112"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
chrono = "0.4.37"
|
||||||
color-eyre = "0.6.2"
|
color-eyre = "0.6.2"
|
||||||
dotenvy = "0.15.7"
|
dotenvy = "0.15.7"
|
||||||
enum_dispatch = "0.3.12"
|
enum_dispatch = "0.3.12"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use eyre::{Context, Result};
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use poise::serenity_prelude::ChannelId;
|
use poise::serenity_prelude::ChannelId;
|
||||||
|
|
||||||
|
@ -12,6 +13,7 @@ pub struct RefractionChannels {
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub channels: RefractionChannels,
|
pub channels: RefractionChannels,
|
||||||
|
pub days_to_delete_reaction: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RefractionChannels {
|
impl RefractionChannels {
|
||||||
|
@ -22,15 +24,15 @@ impl RefractionChannels {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_from_env() -> Self {
|
pub fn from_env() -> Self {
|
||||||
let log_channel_id = Self::get_channel_from_env("DISCORD_LOG_CHANNEL_ID");
|
let log_channel_id = Self::channel_from_env("DISCORD_LOG_CHANNEL_ID");
|
||||||
if let Some(channel_id) = log_channel_id {
|
if let Some(channel_id) = log_channel_id {
|
||||||
info!("Log channel is {channel_id}");
|
info!("Log channel is {channel_id}");
|
||||||
} else {
|
} else {
|
||||||
warn!("DISCORD_LOG_CHANNEL_ID is empty; this will disable logging in your server.");
|
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 {
|
if let Some(channel_id) = welcome_channel_id {
|
||||||
info!("Welcome channel is {channel_id}");
|
info!("Welcome channel is {channel_id}");
|
||||||
} else {
|
} else {
|
||||||
|
@ -40,7 +42,7 @@ impl RefractionChannels {
|
||||||
Self::new(log_channel_id, welcome_channel_id)
|
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)
|
std::env::var(var)
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|env_var| ChannelId::from_str(&env_var).ok())
|
.and_then(|env_var| ChannelId::from_str(&env_var).ok())
|
||||||
|
@ -48,13 +50,22 @@ impl RefractionChannels {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn new(channels: RefractionChannels) -> Self {
|
pub fn new(channels: RefractionChannels, days_to_delete_reaction: i64) -> Self {
|
||||||
Self { channels }
|
Self {
|
||||||
|
channels,
|
||||||
|
days_to_delete_reaction,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_env() -> Self {
|
pub fn from_env() -> Result<Self> {
|
||||||
let channels = RefractionChannels::new_from_env();
|
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 bot;
|
||||||
mod discord;
|
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 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;
|
use poise::FrameworkContext;
|
||||||
|
|
||||||
mod analyze_logs;
|
mod analyze_logs;
|
||||||
|
mod block_reaction;
|
||||||
mod delete_on_reaction;
|
mod delete_on_reaction;
|
||||||
mod eta;
|
mod eta;
|
||||||
mod expand_link;
|
mod expand_link;
|
||||||
|
@ -71,6 +72,8 @@ pub async fn handle(
|
||||||
add_reaction.message_id.to_string(),
|
add_reaction.message_id.to_string(),
|
||||||
add_reaction.user_id.unwrap_or_default().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?;
|
delete_on_reaction::handle(ctx, add_reaction).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ async fn setup(
|
||||||
_: &serenity::Ready,
|
_: &serenity::Ready,
|
||||||
framework: &Framework<Data, Error>,
|
framework: &Framework<Data, Error>,
|
||||||
) -> Result<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 {
|
let storage = if let Some(url) = &config.bot.redis_url {
|
||||||
Some(Storage::from_url(url)?)
|
Some(Storage::from_url(url)?)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue