Skip to content

Commit

Permalink
feat: lock & unlock commands
Browse files Browse the repository at this point in the history
  • Loading branch information
oSumAtrIX committed Aug 30, 2022
1 parent 4ab4c7e commit abdc092
Show file tree
Hide file tree
Showing 4 changed files with 260 additions and 68 deletions.
193 changes: 162 additions & 31 deletions src/commands/moderation.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
use bson::{doc, Document};
use chrono::{Duration, Utc};
use mongodb::options::{UpdateModifications, UpdateOptions};
use poise::serenity_prelude::{self as serenity, Member, RoleId, User};
use poise::serenity_prelude::{
self as serenity,
Member,
PermissionOverwrite,
Permissions,
RoleId,
User,
};
use tracing::{debug, trace};

use crate::db::model::Muted;
use crate::db::model::{LockedChannel, Muted};
use crate::utils::moderation::{
ban_moderation,
queue_unmute_member,
Expand All @@ -14,6 +21,135 @@ use crate::utils::moderation::{
};
use crate::{Context, Error};

/// Lock a channel.
#[poise::command(slash_command)]
pub async fn lock(ctx: Context<'_>) -> Result<(), Error> {
let data = &ctx.data().read().await;
let configuration = &data.configuration;
let database = &data.database;
let discord = &ctx.discord();
let cache = &discord.cache;
let http = &discord.http;

let channel_id = ctx.channel_id().0;
let channel = &cache.guild_channel(channel_id).unwrap();

let query: Document = LockedChannel {
channel_id: Some(channel_id.to_string()),
..Default::default()
}
.into();

// Check if channel is already muted, if so succeed.
if let Ok(mut cursor) = database
.find::<LockedChannel>("locked", query.clone(), None)
.await
{
if cursor.advance().await.unwrap() {
respond_moderation(
&ctx,
&ModerationKind::Lock(
channel.name.clone(),
Some(Error::from("Channel already locked")),
),
configuration,
)
.await?;
return Ok(());
}
}

// accumulate all roles with write permissions
let permission_overwrites: Vec<_> = channel
.permission_overwrites
.iter()
.filter_map(|r| {
if r.allow.send_messages() || !r.deny.send_messages() {
Some(r.clone())
} else {
None
}
})
.collect();

// lock the channel by and creating the new permission overwrite
for permission_overwrite in &permission_overwrites {
let permission = Permissions::SEND_MESSAGES & Permissions::ADD_REACTIONS;
channel
.create_permission(http, &PermissionOverwrite {
allow: permission_overwrite.allow & !permission,
deny: permission_overwrite.deny | permission,
kind: permission_overwrite.kind,
})
.await?;
}

// save the original overwrites
let updated: Document = LockedChannel {
overwrites: Some(permission_overwrites),
..Default::default()
}
.into();

database
.update::<LockedChannel>(
"locked",
query,
UpdateModifications::Document(doc! { "$set": updated}),
Some(UpdateOptions::builder().upsert(true).build()),
)
.await?;

respond_moderation(
&ctx,
&ModerationKind::Lock(channel.name.clone(), None),
configuration,
)
.await
}

/// Unlock a channel.
#[poise::command(slash_command)]
pub async fn unlock(ctx: Context<'_>) -> Result<(), Error> {
let data = &ctx.data().read().await;
let configuration = &data.configuration;
let database = &data.database;
let discord = &ctx.discord();
let cache = &discord.cache;
let http = &discord.http;

let channel_id = ctx.channel_id().0;

let delete_result = database
.find_and_delete::<LockedChannel>(
"locked",
LockedChannel {
channel_id: Some(channel_id.to_string()),
..Default::default()
}
.into(),
None,
)
.await;

let channel = cache.guild_channel(channel_id).unwrap();
let mut error = None;
if let Ok(Some(locked_channel)) = delete_result {
for overwrite in &locked_channel.overwrites.unwrap() {
channel.create_permission(http, overwrite).await?;
}
} else {
error = Some(Error::from("Channel already unlocked"))
}

respond_moderation(
&ctx,
&ModerationKind::Unlock(channel.name.clone(), error), // TODO: handle error
configuration,
)
.await
}

/// Unmute a member.
#[poise::command(slash_command)]
pub async fn unmute(
Expand All @@ -30,21 +166,20 @@ pub async fn unmute(
pending_unmute.abort();
}

let queue = queue_unmute_member(
&ctx.discord().http,
&data.database,
&member,
configuration.general.mute.role,
0,
)
.await
.unwrap();

respond_moderation(
&ctx,
&ModerationKind::Unmute(
queue_unmute_member(
&ctx.discord().http,
&data.database,
&member,
configuration.general.mute.role,
0,
)
.await
.unwrap(),
),
&member.user,
&configuration,
&ModerationKind::Unmute(member.user, queue),
configuration,
)
.await
}
Expand Down Expand Up @@ -175,9 +310,13 @@ pub async fn mute(

respond_moderation(
&ctx,
&ModerationKind::Mute(reason, format!("<t:{}:F>", unmute_time.timestamp()), result),
&member.user,
&configuration,
&ModerationKind::Mute(
member.user,
reason,
format!("<t:{}:F>", unmute_time.timestamp()),
result,
),
configuration,
)
.await
}
Expand Down Expand Up @@ -207,9 +346,7 @@ pub async fn purge(
let too_old_timestamp = Utc::now().timestamp() - MAX_BULK_DELETE_AGO_SECS;

let current_user = ctx.discord().http.get_current_user().await?;
let image = current_user
.avatar_url()
.unwrap_or_else(|| current_user.default_avatar_url());
let image = current_user.face();

let handle = ctx
.send(|f| {
Expand Down Expand Up @@ -314,22 +451,16 @@ pub async fn unban(ctx: Context<'_>, #[description = "User"] user: User) -> Resu
async fn handle_ban(ctx: &Context<'_>, kind: &BanKind) -> Result<(), Error> {
let data = ctx.data().read().await;

let ban_result = ban_moderation(&ctx, &kind).await;
let ban_result = ban_moderation(ctx, kind).await;

let moderated_user;
respond_moderation(
&ctx,
ctx,
&match kind {
BanKind::Ban(user, _, reason) => {
moderated_user = user;
ModerationKind::Ban(reason.clone(), ban_result)
},
BanKind::Unban(user) => {
moderated_user = user;
ModerationKind::Unban(ban_result)
ModerationKind::Ban(user.clone(), reason.clone(), ban_result)
},
BanKind::Unban(user) => ModerationKind::Unban(user.clone(), ban_result),
},
&moderated_user,
&data.configuration,
)
.await
Expand Down
23 changes: 22 additions & 1 deletion src/db/model.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::fmt::Display;

use bson::Document;
use poise::serenity_prelude::{PermissionOverwrite};
use serde::{Deserialize, Serialize};
use serde_with_macros::skip_serializing_none;

Expand All @@ -15,12 +16,32 @@ pub struct Muted {
pub reason: Option<String>,
}

#[skip_serializing_none]
#[derive(Serialize, Deserialize, Debug, Default)]
pub struct LockedChannel {
pub channel_id: Option<String>,
pub overwrites: Option<Vec<PermissionOverwrite>>,
}

impl From<Muted> for Document {
fn from(muted: Muted) -> Self {
bson::to_document(&muted).unwrap()
to_document(&muted)
}
}

impl From<LockedChannel> for Document {
fn from(locked: LockedChannel) -> Self {
to_document(&locked)
}
}

fn to_document<T>(t: &T) -> Document
where
T: Serialize,
{
bson::to_document(t).unwrap()
}

// Display trait
impl Display for Muted {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Expand Down
2 changes: 2 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ async fn main() {
moderation::purge(),
moderation::ban(),
moderation::unban(),
moderation::lock(),
moderation::unlock(),
misc::reply(),
];
poise::set_qualified_names(&mut commands);
Expand Down
Loading

0 comments on commit abdc092

Please sign in to comment.