core: Remove soft ban (#64)
Some checks failed
Build and Test / Format check (push) Has been cancelled
Build and Test / Lint (push) Has been cancelled
Build and Test / Test (push) Has been cancelled
Build and Test / Build (push) Has been cancelled

Since Discord implemented silence, soft bans are not really used.
This commit is contained in:
Natsu Kagami 2025-03-29 17:06:20 +01:00 committed by GitHub
parent 79b17414c5
commit a146883620
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 4 additions and 210 deletions

View file

@ -7,17 +7,13 @@ use serenity::{
},
model::channel::{Channel, Message},
};
use soft_ban::{SOFT_BAN_COMMAND, SOFT_BAN_INIT_COMMAND};
use youmubot_prelude::*;
mod soft_ban;
pub use soft_ban::watch_soft_bans;
pub mod ignore;
#[group]
#[description = "Administrative commands for the server."]
#[commands(clean, ban, kick, soft_ban, soft_ban_init)]
#[commands(clean, ban, kick)]
struct Admin;
#[command]

View file

@ -1,164 +0,0 @@
use crate::db::{ServerSoftBans, SoftBans};
use chrono::offset::Utc;
use futures_util::{stream, TryStreamExt};
use serenity::{
framework::standard::{macros::command, Args, CommandResult},
model::{channel::Message, id},
};
use youmubot_prelude::*;
#[command]
#[required_permissions(ADMINISTRATOR)]
#[description = "Soft-ban an user, might be with a certain amount of time. Re-banning an user removes the ban itself."]
#[usage = "user#1234 [time]"]
#[example = "user#1234 5s"]
#[min_args(1)]
#[max_args(2)]
#[only_in("guilds")]
pub async fn soft_ban(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
let user = args.single::<UserId>()?.0.to_user(&ctx).await?;
let data = ctx.data.read().await;
let duration = if args.is_empty() {
None
} else {
Some(args.single::<args::Duration>()?)
};
let guild = msg
.guild_id
.ok_or_else(|| Error::msg("Command is guild only"))?;
let mut db = SoftBans::open(&data);
let val = db
.borrow()?
.get(&guild)
.map(|v| (v.role, v.periodical_bans.get(&user.id).cloned()));
let (role, current_ban_deadline) = match val {
None => {
msg.reply(&ctx, "⚠ This server has not enabled the soft-ban feature. Check out `y!a soft-ban-init`.").await?;
return Ok(());
}
Some(v) => v,
};
let member = guild.member(&ctx, &user).await?;
match duration {
None if member.roles.contains(&role) => {
msg.reply(&ctx, format!("⛓ Lifting soft-ban for user {}.", user.tag()))
.await?;
member.remove_role(&ctx, role).await?;
return Ok(());
}
None => {
msg.reply(&ctx, format!("⛓ Soft-banning user {}.", user.tag()))
.await?;
}
Some(v) => {
// Add the duration into the ban timeout.
let until =
current_ban_deadline.unwrap_or_else(Utc::now) + chrono::Duration::from_std(v.0)?;
msg.reply(
&ctx,
format!("⛓ Soft-banning user {} until {}.", user.tag(), until),
)
.await?;
db.borrow_mut()?
.get_mut(&guild)
.map(|v| v.periodical_bans.insert(user.id, until));
}
}
member.add_role(&ctx, role).await?;
Ok(())
}
#[command]
#[required_permissions(ADMINISTRATOR)]
#[description = "Sets up the soft-ban command. This command can only be run once.\nThe soft-ban command assigns a role, temporarily, to a user."]
#[usage = "{soft_ban_role_id}"]
#[num_args(1)]
#[only_in("guilds")]
pub async fn soft_ban_init(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
let role_id = args.single::<RoleId>()?.0;
let data = ctx.data.read().await;
let guild = msg.guild_id.unwrap().to_partial_guild(&ctx).await?;
// Check whether the role_id is the one we wanted
if !guild.roles.contains_key(&role_id) {
return Err(Error::msg(format!("{} is not a role in this server.", role_id)).into());
}
// Check if we already set up
let mut db = SoftBans::open(&data);
let set_up = db.borrow()?.contains_key(&guild.id);
if !set_up {
db.borrow_mut()?
.insert(guild.id, ServerSoftBans::new(role_id));
msg.react(&ctx, '👌').await?;
} else {
return Err(Error::msg("Server already set up soft-bans.").into());
}
Ok(())
}
// Watch the soft bans. Blocks forever.
pub async fn watch_soft_bans(cache_http: impl CacheHttp, data: AppData) {
loop {
// Scope so that locks are released
{
// Poll the data for any changes.
let data = data.read().await;
let mut data = SoftBans::open(&data);
let mut db = data.borrow().unwrap().clone();
let now = Utc::now();
for (server_id, bans) in db.iter_mut() {
let server_name: String = match server_id.to_partial_guild(cache_http.http()).await
{
Err(_) => continue,
Ok(v) => v.name,
};
let to_remove: Vec<_> = bans
.periodical_bans
.iter()
.filter_map(|(user, time)| if time <= &now { Some(user) } else { None })
.cloned()
.collect();
if let Err(e) = to_remove
.into_iter()
.map(|user_id| {
bans.periodical_bans.remove(&user_id);
lift_soft_ban_for(
&cache_http,
*server_id,
&server_name[..],
bans.role,
user_id,
)
})
.collect::<stream::FuturesUnordered<_>>()
.try_collect::<()>()
.await
{
eprintln!("Error while scanning soft-bans list: {}", e)
}
}
*(data.borrow_mut().unwrap()) = db;
}
// Sleep the thread for a minute
tokio::time::sleep(std::time::Duration::from_secs(60)).await
}
}
async fn lift_soft_ban_for(
cache_http: impl CacheHttp,
server_id: id::GuildId,
server_name: &str,
ban_role: id::RoleId,
user_id: id::UserId,
) -> Result<()> {
let m = server_id.member(&cache_http, user_id).await?;
println!(
"Soft-ban for `{}` in server `{}` unlifted.",
m.user.name, server_name
);
m.remove_role(cache_http.http(), ban_role).await?;
Ok(())
}

View file

@ -1,42 +1,15 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serenity::model::{
channel::ReactionType,
id::{MessageId, RoleId, UserId},
id::{MessageId, RoleId},
};
use std::collections::HashMap;
use youmubot_db::{GuildMap, DB};
use youmubot_prelude::*;
/// A list of SoftBans for all servers.
pub type SoftBans = DB<GuildMap<ServerSoftBans>>;
/// A list of assignable roles for all servers.
pub type Roles = DB<GuildMap<RoleList>>;
/// For the admin commands:
/// - Each server might have a `soft ban` role implemented.
/// - We allow periodical `soft ban` applications.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ServerSoftBans {
/// The soft-ban role.
pub role: RoleId,
/// List of all to-unban people.
pub periodical_bans: HashMap<UserId, DateTime<Utc>>,
}
impl ServerSoftBans {
// Create a new, implemented role.
pub fn new(role: RoleId) -> Self {
Self {
role,
periodical_bans: HashMap::new(),
}
}
}
/// Represents a server's role list.
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct RoleList {

View file

@ -10,7 +10,7 @@ use serenity::{
pub use admin::ADMIN_GROUP;
pub use community::COMMUNITY_GROUP;
pub use fun::FUN_GROUP;
use youmubot_prelude::{announcer::CacheAndHttp, *};
use youmubot_prelude::*;
pub mod admin;
pub mod community;
@ -43,7 +43,6 @@ impl<T: AsRef<CoreEnv> + Send + Sync> HasCoreEnv for T {
/// Sets up all databases in the client.
pub async fn setup(path: &std::path::Path, data: &mut TypeMap, prelude: Env) -> Result<CoreEnv> {
db::SoftBans::insert_into(&mut *data, &path.join("soft_bans.yaml"))?;
db::load_role_list(
&mut *data,
&path.join("roles_v2.yaml"),
@ -56,15 +55,6 @@ pub async fn setup(path: &std::path::Path, data: &mut TypeMap, prelude: Env) ->
CoreEnv::new(prelude).await
}
pub fn ready_hook(ctx: &Context) -> CommandResult {
// Create handler threads
tokio::spawn(admin::watch_soft_bans(
CacheAndHttp::from_context(ctx),
ctx.data.clone(),
));
Ok(())
}
// A help command
#[help]
pub async fn help(

View file

@ -38,6 +38,7 @@ impl Handler {
self.hooks.push(RwLock::new(Box::new(f)));
}
#[allow(unused)]
fn push_ready_hook(&mut self, f: fn(&Context) -> CommandResult) {
self.ready_hooks.push(f);
}
@ -184,8 +185,6 @@ async fn main() {
}
let mut handler = Handler::new();
#[cfg(feature = "core")]
handler.push_ready_hook(youmubot_core::ready_hook);
// Set up hooks
#[cfg(feature = "osu")]
{