mirror of
https://github.com/revanced/revanced-discord-bot.git
synced 2025-04-29 22:14:28 +02:00
build: Bump Serenity
This commit is contained in:
parent
21dd21c377
commit
a8e9a9bb49
899
Cargo.lock
generated
899
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
16
Cargo.toml
16
Cargo.toml
@ -5,7 +5,7 @@ homepage = "https://revanced.app"
|
||||
license = "GPL-3.0"
|
||||
name = "revanced-discord-bot"
|
||||
repository = "https://github.com/revanced/revanced-discord-bot"
|
||||
version = "2.5.1"
|
||||
version = "2.5.2"
|
||||
edition = "2021"
|
||||
|
||||
[profile.release]
|
||||
@ -17,9 +17,9 @@ panic = "abort"
|
||||
|
||||
[dependencies]
|
||||
bson = "2.4"
|
||||
serde_with_macros = "2.0.1"
|
||||
serde_with_macros = "3.4.0"
|
||||
mongodb = "2.4.0"
|
||||
poise = "0.5.2"
|
||||
poise = { git = "https://github.com/serenity-rs/poise.git", branch = "serenity-next" }
|
||||
decancer = "1.5.4"
|
||||
tokio = { version = "1.26.0", features = ["rt-multi-thread"] }
|
||||
dotenv = "0.15.0"
|
||||
@ -27,10 +27,16 @@ serde = { version = "1.0.158", features = ["derive"] }
|
||||
serde_json = "1.0.94"
|
||||
regex = "1.7.3"
|
||||
serde_regex = "1.1.0"
|
||||
reqwest = { version = "0.11.15", features= ["rustls-tls"], default-features = false }
|
||||
reqwest = { version = "0.11.15", features = [
|
||||
"rustls-tls",
|
||||
"json",
|
||||
], default-features = false }
|
||||
chrono = "0.4.24"
|
||||
dirs = "5.0.0"
|
||||
tracing = { version = "0.1.37", features = ["max_level_debug", "release_max_level_info"] }
|
||||
tracing = { version = "0.1.37", features = [
|
||||
"max_level_debug",
|
||||
"release_max_level_info",
|
||||
] }
|
||||
tracing-subscriber = "0.3.16"
|
||||
hmac-sha256 = "1.1.6"
|
||||
base64 = "0.21.0"
|
||||
|
@ -1,3 +1,5 @@
|
||||
use poise::serenity_prelude::CreateEmbed;
|
||||
use poise::CreateReply;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::utils::bot::load_configuration;
|
||||
@ -15,12 +17,13 @@ pub async fn reload(ctx: Context<'_>) -> Result<(), Error> {
|
||||
|
||||
debug!("{} reloaded the configuration.", ctx.author().name);
|
||||
|
||||
ctx.send(|f| {
|
||||
f.ephemeral(true).embed(|f| {
|
||||
f.description("Successfully reloaded configuration.")
|
||||
.color(embed_color)
|
||||
})
|
||||
})
|
||||
ctx.send(
|
||||
CreateReply::new().ephemeral(true).embed(
|
||||
CreateEmbed::new()
|
||||
.description("Successfully reloaded configuration.")
|
||||
.color(embed_color),
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
@ -32,18 +35,17 @@ pub async fn stop(ctx: Context<'_>) -> Result<(), Error> {
|
||||
debug!("{} stopped the bot.", ctx.author().name);
|
||||
|
||||
let color = ctx.data().read().await.configuration.general.embed_color;
|
||||
ctx.send(|f| {
|
||||
f.ephemeral(true)
|
||||
.embed(|f| f.description("Stopped the bot.").color(color))
|
||||
})
|
||||
|
||||
ctx.send(
|
||||
CreateReply::new().ephemeral(true).embed(
|
||||
CreateEmbed::new()
|
||||
.description("Stopped the bot.")
|
||||
.color(color),
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
|
||||
ctx.framework()
|
||||
.shard_manager()
|
||||
.lock()
|
||||
.await
|
||||
.shutdown_all()
|
||||
.await;
|
||||
ctx.framework().shard_manager().shutdown_all().await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use poise::serenity_prelude::{self as serenity, MessageId, ParseValue, ReactionType};
|
||||
use poise::ReplyHandle;
|
||||
use poise::serenity_prelude::{CreateActionRow, CreateAllowedMentions, CreateButton, ReactionType};
|
||||
use poise::{CreateReply, ReplyHandle};
|
||||
|
||||
use crate::utils::message::clone_message;
|
||||
use crate::{Context, Error};
|
||||
@ -14,8 +14,9 @@ pub async fn reply(
|
||||
async fn send_ephermal<'a>(
|
||||
ctx: &Context<'a>,
|
||||
content: &str,
|
||||
) -> Result<ReplyHandle<'a>, serenity::Error> {
|
||||
ctx.send(|f| f.ephemeral(true).content(content)).await
|
||||
) -> Result<ReplyHandle<'a>, poise::serenity_prelude::Error> {
|
||||
ctx.send(CreateReply::new().ephemeral(true).content(content))
|
||||
.await
|
||||
}
|
||||
|
||||
let http = &ctx.serenity_context().http;
|
||||
@ -23,7 +24,7 @@ pub async fn reply(
|
||||
|
||||
if let Some(reply_message) = reply_message {
|
||||
if let Ok(reply_message) = reply_message.parse::<u64>() {
|
||||
match channel.message(http, MessageId(reply_message)).await {
|
||||
match channel.message(http, reply_message).await {
|
||||
Ok(reply_message) => {
|
||||
reply_message.reply(http, &message).await?;
|
||||
},
|
||||
@ -71,29 +72,24 @@ pub async fn poll(
|
||||
let message = ctx
|
||||
.serenity_context()
|
||||
.http
|
||||
.get_message(channel_id, message_id)
|
||||
.get_message(channel_id.into(), message_id.into())
|
||||
.await?;
|
||||
|
||||
ctx.send(|m| {
|
||||
let mut clone = clone_message(&message, m).components(|c| {
|
||||
c.create_action_row(|r| {
|
||||
r.create_button(|b| {
|
||||
b.label("Vote")
|
||||
.emoji(ReactionType::Unicode("🗳️".to_string()))
|
||||
.custom_id(format!("poll:{id}:{age}"))
|
||||
})
|
||||
})
|
||||
});
|
||||
let message = clone_message(&message).components(vec![CreateActionRow::Buttons(vec![
|
||||
CreateButton::new(format!("poll:{id}:{age}"))
|
||||
.label("Vote")
|
||||
.emoji(ReactionType::Unicode("🗳️".to_string())),
|
||||
])]);
|
||||
|
||||
if ping {
|
||||
clone = clone.allowed_mentions(|am| {
|
||||
am.parse(ParseValue::Users)
|
||||
.parse(ParseValue::Roles)
|
||||
.parse(ParseValue::Everyone)
|
||||
});
|
||||
}
|
||||
|
||||
clone
|
||||
ctx.send(if ping {
|
||||
message.allowed_mentions(
|
||||
CreateAllowedMentions::default()
|
||||
.all_roles(true)
|
||||
.all_users(true)
|
||||
.everyone(true),
|
||||
)
|
||||
} else {
|
||||
message
|
||||
})
|
||||
.await?;
|
||||
|
||||
|
@ -1,16 +1,22 @@
|
||||
|
||||
|
||||
|
||||
use bson::{doc, Document};
|
||||
use chrono::Utc;
|
||||
use mongodb::options::{UpdateModifications, UpdateOptions};
|
||||
use poise::serenity_prelude::{
|
||||
self as serenity,
|
||||
CreateEmbed,
|
||||
CreateEmbedFooter,
|
||||
EditMessage,
|
||||
GetMessages,
|
||||
Mentionable,
|
||||
PermissionOverwrite,
|
||||
Permissions,
|
||||
UserId,
|
||||
};
|
||||
use tracing::{debug, error, trace};
|
||||
use poise::CreateReply;
|
||||
use tracing::{debug, trace};
|
||||
|
||||
use crate::db::model::{LockedChannel, Muted};
|
||||
use crate::db::model::{Muted};
|
||||
use crate::utils::bot::get_member;
|
||||
use crate::utils::macros::to_user;
|
||||
use crate::utils::moderation::{
|
||||
@ -23,145 +29,6 @@ use crate::utils::moderation::{
|
||||
use crate::utils::parse_duration;
|
||||
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.serenity_context();
|
||||
let cache = &discord.cache;
|
||||
let http = &discord.http;
|
||||
|
||||
let channel_id = ctx.channel_id().0;
|
||||
let channel = &cache.guild_channel(channel_id).unwrap();
|
||||
|
||||
let author = ctx.author();
|
||||
|
||||
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.clone(),
|
||||
author.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();
|
||||
|
||||
// save the original overwrites
|
||||
let updated: Document = LockedChannel {
|
||||
overwrites: Some(permission_overwrites.clone()),
|
||||
..Default::default()
|
||||
}
|
||||
.into();
|
||||
|
||||
database
|
||||
.update::<LockedChannel>(
|
||||
"locked",
|
||||
query,
|
||||
UpdateModifications::Document(doc! { "$set": updated}),
|
||||
Some(UpdateOptions::builder().upsert(true).build()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// lock the channel by and creating the new permission overwrite
|
||||
for permission_overwrite in &permission_overwrites {
|
||||
let permission = Permissions::SEND_MESSAGES & Permissions::ADD_REACTIONS;
|
||||
|
||||
if let Err(err) = channel
|
||||
.create_permission(http, &PermissionOverwrite {
|
||||
allow: permission_overwrite.allow & !permission,
|
||||
deny: permission_overwrite.deny | permission,
|
||||
kind: permission_overwrite.kind,
|
||||
})
|
||||
.await
|
||||
{
|
||||
error!("Failed to create the new permission: {:?}", err);
|
||||
}
|
||||
}
|
||||
|
||||
respond_moderation(
|
||||
&ctx,
|
||||
&ModerationKind::Lock(channel.clone(), author.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.serenity_context();
|
||||
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 author = ctx.author();
|
||||
|
||||
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.clone(), author.clone(), error), // TODO: handle error
|
||||
configuration,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Unmute a member.
|
||||
#[poise::command(slash_command)]
|
||||
pub async fn unmute(
|
||||
@ -175,8 +42,8 @@ pub async fn unmute(
|
||||
let data = &ctx.data().read().await;
|
||||
let configuration = &data.configuration;
|
||||
|
||||
if let Some(pending_unmute) = data.pending_unmutes.get(&id.0) {
|
||||
trace!("Cancelling pending unmute for {}", id.0);
|
||||
if let Some(pending_unmute) = data.pending_unmutes.get(&id.get()) {
|
||||
trace!("Cancelling pending unmute for {}", id);
|
||||
pending_unmute.abort();
|
||||
}
|
||||
|
||||
@ -231,7 +98,7 @@ pub async fn mute(
|
||||
};
|
||||
|
||||
let mut updated = Muted {
|
||||
guild_id: Some(guild_id.0.to_string()),
|
||||
guild_id: Some(guild_id.to_string()),
|
||||
expires: unmute_time,
|
||||
reason: Some(reason.clone()),
|
||||
..Default::default()
|
||||
@ -265,8 +132,8 @@ pub async fn mute(
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(pending_unmute) = data.pending_unmutes.get(&id.0) {
|
||||
trace!("Cancelling pending unmute for {}", id.0);
|
||||
if let Some(pending_unmute) = data.pending_unmutes.get(&id.get()) {
|
||||
trace!("Cancelling pending unmute for {}", id);
|
||||
pending_unmute.abort();
|
||||
}
|
||||
|
||||
@ -281,7 +148,7 @@ pub async fn mute(
|
||||
.await?;
|
||||
} else {
|
||||
data.pending_unmutes.insert(
|
||||
id.0,
|
||||
id.get(),
|
||||
queue_unmute_member(
|
||||
discord.clone(),
|
||||
data.database.clone(),
|
||||
@ -341,14 +208,15 @@ pub async fn purge(
|
||||
let author = ctx.author();
|
||||
|
||||
let handle = ctx
|
||||
.send(|f| {
|
||||
f.embed(|f| {
|
||||
f.title("Purging messages")
|
||||
.send(
|
||||
CreateReply::new().embed(
|
||||
CreateEmbed::new()
|
||||
.title("Purging messages")
|
||||
.description("Accumulating...")
|
||||
.color(embed_color)
|
||||
.thumbnail(&image)
|
||||
})
|
||||
})
|
||||
.thumbnail(&image),
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
let mut response = handle.message().await?;
|
||||
|
||||
@ -361,9 +229,12 @@ pub async fn purge(
|
||||
loop {
|
||||
// Filter out messages that are too old
|
||||
let mut messages = channel
|
||||
.messages(&ctx.serenity_context(), |m| {
|
||||
m.limit(count_to_delete as u64).before(response.id)
|
||||
})
|
||||
.messages(
|
||||
&ctx.serenity_context(),
|
||||
GetMessages::new()
|
||||
.limit(count_to_delete as u8)
|
||||
.before(response.id),
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.take_while(|m| m.timestamp.timestamp() > too_old_timestamp)
|
||||
@ -384,7 +255,7 @@ pub async fn purge(
|
||||
if let Ok(message_id) = message_id.parse::<u64>() {
|
||||
messages = messages
|
||||
.into_iter()
|
||||
.take_while(|m| m.id.0 > message_id)
|
||||
.take_while(|m| m.id.get() > message_id)
|
||||
.collect::<Vec<_>>();
|
||||
debug!(
|
||||
"Filtered messages until {}. Left: {}",
|
||||
@ -411,21 +282,19 @@ pub async fn purge(
|
||||
|
||||
response
|
||||
.to_mut()
|
||||
.edit(&ctx.serenity_context(), |e| {
|
||||
e.set_embed(
|
||||
.edit(
|
||||
&ctx.serenity_context(),
|
||||
EditMessage::new().embed(
|
||||
serenity::CreateEmbed::default()
|
||||
.title("Purge successful")
|
||||
.field("Deleted messages", deleted_amount.to_string(), false)
|
||||
.field("Action by", author.mention(), false)
|
||||
.field("Action by", author.mention().to_string(), false)
|
||||
.color(embed_color)
|
||||
.thumbnail(&image)
|
||||
.footer(|f| {
|
||||
f.text("ReVanced");
|
||||
f.icon_url(image)
|
||||
})
|
||||
.footer(CreateEmbedFooter::new("ReVanced").icon_url(image))
|
||||
.clone(),
|
||||
)
|
||||
})
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -3,8 +3,11 @@ use crate::utils::decancer::cure;
|
||||
|
||||
pub async fn guild_member_update(
|
||||
ctx: &serenity::Context,
|
||||
old_if_available: &Option<serenity::Member>,
|
||||
new: &serenity::Member,
|
||||
old_if_available: &Option<Member>,
|
||||
new: &Option<Member>,
|
||||
_: &GuildMemberUpdateEvent,
|
||||
) {
|
||||
cure(ctx, old_if_available, new).await;
|
||||
if let Some(member) = new {
|
||||
cure(ctx, old_if_available, member).await;
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
use chrono::{Duration, Utc};
|
||||
use poise::serenity_prelude::{
|
||||
ComponentType,
|
||||
MessageComponentInteraction,
|
||||
MessageComponentInteractionData,
|
||||
ComponentInteraction,
|
||||
ComponentInteractionData,
|
||||
ComponentInteractionDataKind,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
@ -10,11 +10,11 @@ use crate::utils;
|
||||
pub async fn interaction_create(
|
||||
ctx: &serenity::Context,
|
||||
interaction: &serenity::Interaction,
|
||||
) -> Result<(), crate::serenity::SerenityError> {
|
||||
if let serenity::Interaction::MessageComponent(MessageComponentInteraction {
|
||||
) -> Result<(), serenity::prelude::SerenityError> {
|
||||
if let serenity::Interaction::Component(ComponentInteraction {
|
||||
data:
|
||||
MessageComponentInteractionData {
|
||||
component_type: ComponentType::Button,
|
||||
ComponentInteractionData {
|
||||
kind: ComponentInteractionDataKind::Button,
|
||||
custom_id,
|
||||
..
|
||||
},
|
||||
@ -33,7 +33,7 @@ pub async fn handle_poll(
|
||||
ctx: &serenity::Context,
|
||||
interaction: &serenity::Interaction,
|
||||
custom_id: &str,
|
||||
) -> Result<(), crate::serenity::SerenityError> {
|
||||
) -> Result<(), serenity::prelude::SerenityError> {
|
||||
fn parse<T>(str: &str) -> T
|
||||
where
|
||||
<T as std::str::FromStr>::Err: std::fmt::Debug,
|
||||
|
@ -1,6 +1,14 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use poise::serenity_prelude::{self as serenity, Mutex, RwLock, ShardManager, UserId};
|
||||
use poise::serenity_prelude::prelude::RwLock;
|
||||
use poise::serenity_prelude::{
|
||||
self as serenity,
|
||||
GuildMemberUpdateEvent,
|
||||
Member,
|
||||
Presence,
|
||||
ShardManager,
|
||||
UserId,
|
||||
};
|
||||
use tracing::log::error;
|
||||
|
||||
use crate::{Data, Error};
|
||||
@ -15,7 +23,7 @@ pub struct Handler<T> {
|
||||
options: poise::FrameworkOptions<T, Error>,
|
||||
data: T,
|
||||
bot_id: RwLock<Option<UserId>>,
|
||||
shard_manager: RwLock<Option<Arc<Mutex<ShardManager>>>>,
|
||||
shard_manager: RwLock<Option<Arc<ShardManager>>>,
|
||||
}
|
||||
|
||||
// Custom handler to dispatch poise events
|
||||
@ -29,18 +37,18 @@ impl<T: Send + Sync> Handler<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn set_shard_manager(&self, shard_manager: Arc<Mutex<serenity::ShardManager>>) {
|
||||
pub async fn set_shard_manager(&self, shard_manager: Arc<serenity::ShardManager>) {
|
||||
*self.shard_manager.write().await = Some(shard_manager);
|
||||
}
|
||||
|
||||
async fn dispatch_poise_event(&self, ctx: &serenity::Context, event: &poise::Event<'_>) {
|
||||
async fn dispatch_poise_event(&self, event: &serenity::FullEvent) {
|
||||
let framework_data = poise::FrameworkContext {
|
||||
bot_id: self.bot_id.read().await.unwrap(),
|
||||
options: &self.options,
|
||||
user_data: &self.data,
|
||||
shard_manager: &(*self.shard_manager.read().await).clone().unwrap(), /* Shard manager can be read between all poise events without locks */
|
||||
};
|
||||
poise::dispatch_event(framework_data, ctx, event).await;
|
||||
poise::dispatch_event(framework_data, event).await;
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,16 +66,18 @@ impl serenity::EventHandler for Handler<Arc<RwLock<Data>>> {
|
||||
async fn guild_member_update(
|
||||
&self,
|
||||
ctx: serenity::Context,
|
||||
old_if_available: Option<serenity::Member>,
|
||||
new: serenity::Member,
|
||||
old_if_available: Option<Member>,
|
||||
new: Option<Member>,
|
||||
event: GuildMemberUpdateEvent,
|
||||
) {
|
||||
guild_member_update::guild_member_update(&ctx, &old_if_available, &new).await;
|
||||
guild_member_update::guild_member_update(&ctx, &old_if_available, &new, &event).await;
|
||||
}
|
||||
|
||||
async fn message(&self, ctx: serenity::Context, new_message: serenity::Message) {
|
||||
message_create::message_create(&ctx, &new_message).await;
|
||||
|
||||
self.dispatch_poise_event(&ctx, &poise::Event::Message {
|
||||
self.dispatch_poise_event(&serenity::FullEvent::Message {
|
||||
ctx,
|
||||
new_message,
|
||||
})
|
||||
.await;
|
||||
@ -80,7 +90,8 @@ impl serenity::EventHandler for Handler<Arc<RwLock<Data>>> {
|
||||
new: Option<serenity::Message>,
|
||||
event: serenity::MessageUpdateEvent,
|
||||
) {
|
||||
self.dispatch_poise_event(&ctx, &poise::Event::MessageUpdate {
|
||||
self.dispatch_poise_event(&serenity::FullEvent::MessageUpdate {
|
||||
ctx,
|
||||
old_if_available,
|
||||
new,
|
||||
event,
|
||||
@ -99,7 +110,8 @@ impl serenity::EventHandler for Handler<Arc<RwLock<Data>>> {
|
||||
error!("Failed to handle interaction: {:?}.", e);
|
||||
}
|
||||
|
||||
self.dispatch_poise_event(&ctx, &poise::Event::InteractionCreate {
|
||||
self.dispatch_poise_event(&serenity::FullEvent::InteractionCreate {
|
||||
ctx,
|
||||
interaction,
|
||||
})
|
||||
.await;
|
||||
|
25
src/events/presence_update.rs
Normal file
25
src/events/presence_update.rs
Normal file
@ -0,0 +1,25 @@
|
||||
use super::*;
|
||||
use crate::model::application::Configuration;
|
||||
use crate::utils::bot::get_data_lock;
|
||||
use crate::utils::decancer::cure;
|
||||
|
||||
pub async fn presence_update(ctx: &serenity::Context, new_data: &Presence) {
|
||||
let data = get_data_lock(ctx).await;
|
||||
let configuration: &Configuration = &data.read().await.configuration;
|
||||
|
||||
if !configuration.general.cure_on_presence_update {
|
||||
return;
|
||||
}
|
||||
|
||||
cure(
|
||||
ctx,
|
||||
&None,
|
||||
&new_data
|
||||
.guild_id
|
||||
.unwrap()
|
||||
.member(&ctx.http, new_data.user.id)
|
||||
.await
|
||||
.unwrap(),
|
||||
)
|
||||
.await;
|
||||
}
|
@ -25,7 +25,9 @@ pub async fn load_muted_members(ctx: &serenity::Context, _: &serenity::Ready) {
|
||||
|
||||
while cursor.advance().await.unwrap() {
|
||||
let current: Muted = cursor.deserialize_current().unwrap();
|
||||
let Some(expires) = current.expires else { continue };
|
||||
let Some(expires) = current.expires else {
|
||||
continue;
|
||||
};
|
||||
let guild_id = current.guild_id.unwrap().parse::<u64>().unwrap();
|
||||
let user_id = current.user_id.unwrap().parse::<u64>().unwrap();
|
||||
|
||||
@ -36,8 +38,8 @@ pub async fn load_muted_members(ctx: &serenity::Context, _: &serenity::Ready) {
|
||||
queue_unmute_member(
|
||||
ctx.clone(),
|
||||
data.database.clone(),
|
||||
serenity::GuildId(guild_id),
|
||||
serenity::UserId(user_id),
|
||||
guild_id.into(),
|
||||
user_id.into(),
|
||||
mute_role_id,
|
||||
amount_left as u64, // i64 as u64 is handled properly here
|
||||
),
|
||||
|
40
src/main.rs
40
src/main.rs
@ -6,7 +6,9 @@ use api::client::Api;
|
||||
use commands::{configuration, misc, moderation};
|
||||
use db::database::Database;
|
||||
use events::Handler;
|
||||
use poise::serenity_prelude::{self as serenity, RwLock, UserId};
|
||||
use poise::serenity_prelude::prelude::{RwLock, TypeMapKey};
|
||||
use poise::serenity_prelude::{CreateEmbed, UserId};
|
||||
use poise::CreateReply;
|
||||
use tokio::task::JoinHandle;
|
||||
use tracing::{error, trace};
|
||||
use utils::bot::load_configuration;
|
||||
@ -24,7 +26,7 @@ mod utils;
|
||||
type Error = Box<dyn std::error::Error + Send + Sync>;
|
||||
type Context<'a> = poise::Context<'a, Arc<RwLock<Data>>, Error>;
|
||||
|
||||
impl serenity::TypeMapKey for Data {
|
||||
impl TypeMapKey for Data {
|
||||
type Value = Arc<RwLock<Data>>;
|
||||
}
|
||||
|
||||
@ -54,8 +56,6 @@ async fn main() {
|
||||
moderation::purge(),
|
||||
moderation::ban(),
|
||||
moderation::unban(),
|
||||
moderation::lock(),
|
||||
moderation::unlock(),
|
||||
misc::reply(),
|
||||
misc::poll(),
|
||||
];
|
||||
@ -68,7 +68,7 @@ async fn main() {
|
||||
.users
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(UserId)
|
||||
.map(UserId::from)
|
||||
.collect::<Vec<UserId>>()
|
||||
.into_iter()
|
||||
.collect();
|
||||
@ -116,7 +116,7 @@ async fn main() {
|
||||
if !(administrators
|
||||
.users
|
||||
// Check if the user is an administrator
|
||||
.contains(&member.user.id.0)
|
||||
.contains(&member.user.id.get())
|
||||
|| administrators
|
||||
.roles
|
||||
.iter()
|
||||
@ -125,22 +125,23 @@ async fn main() {
|
||||
member
|
||||
.roles
|
||||
.iter()
|
||||
.any(|member_role| member_role.0 == role_id)
|
||||
.any(|member_role| member_role.get() == role_id)
|
||||
}))
|
||||
{
|
||||
if let Err(e) = ctx
|
||||
.send(|m| {
|
||||
m.ephemeral(true).embed(|e| {
|
||||
e.title("Permission error")
|
||||
.send(
|
||||
CreateReply::new().ephemeral(true).embed(
|
||||
CreateEmbed::new()
|
||||
.title("Permission error")
|
||||
.description(
|
||||
"You do not have permission to use this command.",
|
||||
)
|
||||
.color(configuration.general.embed_color)
|
||||
.thumbnail(member.user.avatar_url().unwrap_or_else(
|
||||
|| member.user.default_avatar_url(),
|
||||
))
|
||||
})
|
||||
})
|
||||
)),
|
||||
),
|
||||
)
|
||||
.await
|
||||
{
|
||||
error!("Error sending message: {:?}", e)
|
||||
@ -152,9 +153,9 @@ async fn main() {
|
||||
Ok(true)
|
||||
})
|
||||
}),
|
||||
event_handler: |_ctx, event, _framework, _data| {
|
||||
event_handler: |event, _framework, _data| {
|
||||
Box::pin(async move {
|
||||
tracing::trace!("{:?}", event.name());
|
||||
tracing::trace!("{:?}", event.snake_case_name());
|
||||
Ok(())
|
||||
})
|
||||
},
|
||||
@ -163,12 +164,13 @@ async fn main() {
|
||||
data.clone(), // Pass configuration as user data for the framework
|
||||
));
|
||||
|
||||
let mut client = serenity::Client::builder(
|
||||
let mut client = poise::serenity_prelude::Client::builder(
|
||||
env::var("DISCORD_AUTHORIZATION_TOKEN")
|
||||
.expect("DISCORD_AUTHORIZATION_TOKEN environment variable not set"),
|
||||
serenity::GatewayIntents::non_privileged()
|
||||
| serenity::GatewayIntents::MESSAGE_CONTENT
|
||||
| serenity::GatewayIntents::GUILD_MEMBERS,
|
||||
poise::serenity_prelude::GatewayIntents::non_privileged()
|
||||
| poise::serenity_prelude::GatewayIntents::MESSAGE_CONTENT
|
||||
| poise::serenity_prelude::GatewayIntents::GUILD_MEMBERS
|
||||
| poise::serenity_prelude::GatewayIntents::GUILD_PRESENCES,
|
||||
)
|
||||
.event_handler_arc(handler.clone())
|
||||
.await
|
||||
|
@ -66,6 +66,7 @@ pub struct General {
|
||||
pub embed_color: i32,
|
||||
pub mute: Mute,
|
||||
pub logging_channel: u64,
|
||||
pub cure_on_presence_update: bool,
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Deserialize)]
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use poise::serenity_prelude::{self as serenity, RwLock};
|
||||
use poise::serenity_prelude::{self as serenity};
|
||||
|
||||
use crate::model::application::Configuration;
|
||||
use crate::Data;
|
||||
@ -10,7 +10,9 @@ pub fn load_configuration() -> Configuration {
|
||||
}
|
||||
|
||||
// Share the lock reference between the threads in serenity framework
|
||||
pub async fn get_data_lock(ctx: &serenity::Context) -> Arc<RwLock<Data>> {
|
||||
pub async fn get_data_lock(
|
||||
ctx: &serenity::Context,
|
||||
) -> Arc<poise::serenity_prelude::prelude::RwLock<Data>> {
|
||||
ctx.data.read().await.get::<Data>().unwrap().clone()
|
||||
}
|
||||
|
||||
@ -21,7 +23,7 @@ pub async fn get_member(
|
||||
) -> serenity::Result<Option<serenity::Member>> {
|
||||
match guild_id.member(ctx, user_id).await {
|
||||
Ok(member) => Ok(Some(member)),
|
||||
Err(serenity::SerenityError::Http(err))
|
||||
Err(serenity::prelude::SerenityError::Http(err))
|
||||
if matches!(
|
||||
err.status_code(),
|
||||
Some(serenity::http::StatusCode::NOT_FOUND)
|
||||
|
@ -1,3 +1,4 @@
|
||||
use poise::serenity_prelude::{CreateAttachment, CreateMessage, EditMessage};
|
||||
use reqwest::Url;
|
||||
use tracing::{debug, error, trace};
|
||||
|
||||
@ -63,26 +64,29 @@ pub async fn code_preview(ctx: &serenity::Context, new_message: &serenity::Messa
|
||||
return; // Nothing to do
|
||||
}
|
||||
|
||||
if let Err(err) = new_message
|
||||
if let Err(err) = &new_message
|
||||
.channel_id
|
||||
.send_message(&ctx.http, |message| {
|
||||
let mut message = message.reference_message(new_message);
|
||||
.send_message(
|
||||
&ctx.http,
|
||||
{
|
||||
let mut message = CreateMessage::new();
|
||||
|
||||
for preview in code_previews.iter() {
|
||||
let language = match preview.code.language.as_ref() {
|
||||
Some(language) => language,
|
||||
None => "txt",
|
||||
};
|
||||
for preview in code_previews.iter() {
|
||||
let language = match preview.code.language.as_ref() {
|
||||
Some(language) => language,
|
||||
None => "txt",
|
||||
};
|
||||
|
||||
let name = format!("{}.{}", &preview.code.branch_or_sha, language);
|
||||
let name = format!("{}.{}", &preview.code.branch_or_sha, language);
|
||||
|
||||
let content = preview.preview.as_ref().unwrap().as_bytes();
|
||||
let content = preview.preview.as_ref().unwrap().as_bytes();
|
||||
|
||||
message = message.add_file((content, name.as_str()));
|
||||
}
|
||||
message = message.add_file(CreateAttachment::bytes(content, name.as_str()));
|
||||
}
|
||||
|
||||
message
|
||||
})
|
||||
message
|
||||
},
|
||||
)
|
||||
.await
|
||||
{
|
||||
error!(
|
||||
@ -93,7 +97,11 @@ pub async fn code_preview(ctx: &serenity::Context, new_message: &serenity::Messa
|
||||
return;
|
||||
}
|
||||
|
||||
if let Err(err) = new_message.clone().suppress_embeds(&ctx.http).await {
|
||||
if let Err(err) = new_message
|
||||
.clone()
|
||||
.edit(&ctx.http, EditMessage::new().suppress_embeds(true))
|
||||
.await
|
||||
{
|
||||
error!("Failed to remove embeds. Error: {:?}", err);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
extern crate decancer;
|
||||
|
||||
use poise::serenity_prelude::EditMember;
|
||||
use tracing::{error, info, trace};
|
||||
|
||||
use super::*;
|
||||
@ -17,7 +18,7 @@ pub async fn cure(
|
||||
let name = member.display_name().to_string();
|
||||
|
||||
if let Some(old) = old_if_available {
|
||||
if old.display_name().to_string() == name {
|
||||
if *old.display_name() == name {
|
||||
trace!(
|
||||
"Skipping decancer for {} because their name hasn't changed",
|
||||
member.user.tag()
|
||||
@ -41,9 +42,11 @@ pub async fn cure(
|
||||
|
||||
match member
|
||||
.guild_id
|
||||
.edit_member(&ctx.http, member.user.id, |edit_member| {
|
||||
edit_member.nickname(cured_name)
|
||||
})
|
||||
.edit_member(
|
||||
&ctx.http,
|
||||
member.user.id,
|
||||
EditMember::default().nickname(cured_name),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(_) => info!("Cured user {}", member.user.tag()),
|
||||
|
@ -1,54 +1,48 @@
|
||||
use chrono::Utc;
|
||||
use poise::serenity_prelude::Message;
|
||||
use poise::serenity_prelude::{CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter, Message};
|
||||
use poise::CreateReply;
|
||||
|
||||
pub fn clone_message<'a, 'b>(
|
||||
message: &'a Message,
|
||||
to_reply: &'b mut CreateReply<'a>,
|
||||
) -> &'b mut CreateReply<'a> {
|
||||
let mut reply = to_reply.content(message.content.as_str());
|
||||
pub fn clone_message(message: &Message) -> CreateReply {
|
||||
let mut reply = CreateReply::new().content(message.content.as_str());
|
||||
|
||||
if let Some(embed) = message.embeds.get(0) {
|
||||
reply = reply.embed(|e| {
|
||||
let mut new_embed = e;
|
||||
if let Some(embed) = message.embeds.first() {
|
||||
let mut new_embed = CreateEmbed::new();
|
||||
|
||||
if let Some(color) = embed.colour {
|
||||
new_embed = new_embed.color(color);
|
||||
}
|
||||
if let Some(color) = embed.colour {
|
||||
new_embed = new_embed.color(color);
|
||||
}
|
||||
|
||||
new_embed = new_embed.timestamp(Utc::now().to_rfc3339());
|
||||
new_embed = new_embed.timestamp(Utc::now());
|
||||
|
||||
if let Some(title) = &embed.title {
|
||||
new_embed = new_embed.title(title);
|
||||
}
|
||||
if let Some(title) = &embed.title {
|
||||
new_embed = new_embed.title(title);
|
||||
}
|
||||
|
||||
if let Some(description) = &embed.description {
|
||||
new_embed = new_embed.description(description);
|
||||
}
|
||||
if let Some(description) = &embed.description {
|
||||
new_embed = new_embed.description(description);
|
||||
}
|
||||
|
||||
if let Some(footer) = &embed.footer {
|
||||
new_embed = new_embed.footer(|f| f.text(&footer.text));
|
||||
}
|
||||
if let Some(footer) = &embed.footer {
|
||||
new_embed = new_embed.footer(CreateEmbedFooter::new(&footer.text));
|
||||
}
|
||||
|
||||
if let Some(author) = &embed.author {
|
||||
new_embed = new_embed.author(|a| a.name(&author.name));
|
||||
}
|
||||
if let Some(author) = &embed.author {
|
||||
new_embed = new_embed.author(CreateEmbedAuthor::new(&author.name));
|
||||
}
|
||||
|
||||
if let Some(image) = &embed.image {
|
||||
new_embed = new_embed.image(image.url.as_str());
|
||||
}
|
||||
if let Some(image) = &embed.image {
|
||||
new_embed = new_embed.image(image.url.as_str());
|
||||
}
|
||||
|
||||
if let Some(thumbnail) = &embed.thumbnail {
|
||||
new_embed = new_embed.thumbnail(thumbnail.url.as_str());
|
||||
}
|
||||
if let Some(thumbnail) = &embed.thumbnail {
|
||||
new_embed = new_embed.thumbnail(thumbnail.url.as_str());
|
||||
}
|
||||
|
||||
for field in &embed.fields {
|
||||
new_embed =
|
||||
new_embed.field(field.name.as_str(), field.value.as_str(), field.inline);
|
||||
}
|
||||
for field in &embed.fields {
|
||||
new_embed = new_embed.field(field.name.as_str(), field.value.as_str(), field.inline);
|
||||
}
|
||||
|
||||
new_embed
|
||||
})
|
||||
reply = reply.embed(new_embed);
|
||||
}
|
||||
|
||||
reply
|
||||
|
@ -1,4 +1,12 @@
|
||||
use chrono::{DateTime, Duration, NaiveDateTime, Utc};
|
||||
use poise::serenity_prelude::{
|
||||
CreateEmbed,
|
||||
CreateEmbedAuthor,
|
||||
CreateEmbedFooter,
|
||||
CreateMessage,
|
||||
EditThread,
|
||||
GetMessages,
|
||||
};
|
||||
use regex::Regex;
|
||||
use tracing::log::error;
|
||||
|
||||
@ -24,7 +32,7 @@ pub async fn handle_message_response(ctx: &serenity::Context, new_message: &sere
|
||||
let member_roles = &member.roles;
|
||||
|
||||
let joined_at = member.joined_at.unwrap().unix_timestamp();
|
||||
let must_joined_at = DateTime::<Utc>::from_utc(
|
||||
let must_joined_at = DateTime::<Utc>::from_naive_utc_and_offset(
|
||||
NaiveDateTime::from_timestamp_opt(joined_at, 0).unwrap(),
|
||||
Utc,
|
||||
);
|
||||
@ -36,7 +44,7 @@ pub async fn handle_message_response(ctx: &serenity::Context, new_message: &sere
|
||||
if !roles.iter().any(|&role_id| {
|
||||
member_roles
|
||||
.iter()
|
||||
.any(|&member_role| role_id == member_role.0)
|
||||
.any(|&member_role| role_id == member_role.get())
|
||||
}) {
|
||||
continue;
|
||||
}
|
||||
@ -44,7 +52,7 @@ pub async fn handle_message_response(ctx: &serenity::Context, new_message: &sere
|
||||
|
||||
if let Some(channels) = &includes.channels {
|
||||
// check if the channel is whitelisted, if not, check if the channel is a thread, if it is check if the parent id is whitelisted
|
||||
if !channels.contains(&new_message.channel_id.0) {
|
||||
if !channels.contains(&new_message.channel_id.get()) {
|
||||
if response.thread_options.is_some() {
|
||||
if guild_message.is_none() {
|
||||
guild_message = Some(
|
||||
@ -57,8 +65,10 @@ pub async fn handle_message_response(ctx: &serenity::Context, new_message: &sere
|
||||
);
|
||||
};
|
||||
|
||||
let Some(parent_id) = guild_message.as_ref().unwrap().parent_id else { continue; };
|
||||
if !channels.contains(&parent_id.0) {
|
||||
let Some(parent_id) = guild_message.as_ref().unwrap().parent_id else {
|
||||
continue;
|
||||
};
|
||||
if !channels.contains(&parent_id.get()) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
@ -80,7 +90,7 @@ pub async fn handle_message_response(ctx: &serenity::Context, new_message: &sere
|
||||
if roles.iter().any(|&role_id| {
|
||||
member_roles
|
||||
.iter()
|
||||
.any(|&member_role| role_id == member_role.0)
|
||||
.any(|&member_role| role_id == member_role.get())
|
||||
}) {
|
||||
continue;
|
||||
}
|
||||
@ -120,32 +130,40 @@ pub async fn handle_message_response(ctx: &serenity::Context, new_message: &sere
|
||||
}
|
||||
|
||||
if let Err(err) = channel_id
|
||||
.send_message(&ctx.http, |m| {
|
||||
if let Some(reference) = message_reference {
|
||||
m.reference_message(reference);
|
||||
} else {
|
||||
m.reference_message(new_message);
|
||||
}
|
||||
.send_message(
|
||||
&ctx.http,
|
||||
{
|
||||
let mut message = CreateMessage::default();
|
||||
message = if let Some(reference) = message_reference {
|
||||
message.reference_message(reference)
|
||||
} else {
|
||||
message.reference_message(new_message)
|
||||
};
|
||||
|
||||
match &response.response.embed {
|
||||
Some(embed) => m.embed(|e| {
|
||||
e.title(&embed.title)
|
||||
.description(&embed.description)
|
||||
.color(embed.color)
|
||||
.fields(embed.fields.iter().map(|field| {
|
||||
(field.name.clone(), field.value.clone(), field.inline)
|
||||
}))
|
||||
.footer(|f| {
|
||||
f.text(&embed.footer.text);
|
||||
f.icon_url(&embed.footer.icon_url)
|
||||
})
|
||||
.thumbnail(&embed.thumbnail.url)
|
||||
.image(&embed.image.url)
|
||||
.author(|a| a.name(&embed.author.name).icon_url(&embed.author.icon_url))
|
||||
}),
|
||||
None => m.content(response.response.message.as_ref().unwrap()),
|
||||
}
|
||||
})
|
||||
match &response.response.embed {
|
||||
Some(embed) => message.embed(
|
||||
CreateEmbed::new()
|
||||
.title(&embed.title)
|
||||
.description(&embed.description)
|
||||
.color(embed.color)
|
||||
.fields(embed.fields.iter().map(|field| {
|
||||
(field.name.clone(), field.value.clone(), field.inline)
|
||||
}))
|
||||
.footer(
|
||||
CreateEmbedFooter::new(&embed.footer.text)
|
||||
.icon_url(&embed.footer.icon_url),
|
||||
)
|
||||
.thumbnail(&embed.thumbnail.url)
|
||||
.image(&embed.image.url)
|
||||
.author(
|
||||
CreateEmbedAuthor::new(&embed.author.name)
|
||||
.icon_url(&embed.author.icon_url),
|
||||
),
|
||||
),
|
||||
None => message.content(response.response.message.as_ref().unwrap()),
|
||||
}
|
||||
},
|
||||
)
|
||||
.await
|
||||
{
|
||||
error!(
|
||||
@ -154,7 +172,7 @@ pub async fn handle_message_response(ctx: &serenity::Context, new_message: &sere
|
||||
err
|
||||
);
|
||||
} else if let Some(thread_options) = &response.thread_options {
|
||||
let channel = channel_id
|
||||
let mut channel = channel_id
|
||||
.to_channel(&ctx.http)
|
||||
.await
|
||||
.unwrap()
|
||||
@ -170,7 +188,7 @@ pub async fn handle_message_response(ctx: &serenity::Context, new_message: &sere
|
||||
|
||||
if thread_options.only_on_first_message
|
||||
&& !channel_id
|
||||
.messages(&ctx.http, |b| b.limit(1).before(new_message))
|
||||
.messages(&ctx.http, GetMessages::new().limit(1).before(new_message))
|
||||
.await
|
||||
.unwrap()
|
||||
.is_empty()
|
||||
@ -179,10 +197,12 @@ pub async fn handle_message_response(ctx: &serenity::Context, new_message: &sere
|
||||
}
|
||||
|
||||
if let Err(err) = channel
|
||||
.edit_thread(&ctx.http, |e| {
|
||||
e.locked(thread_options.lock_on_response)
|
||||
.archived(thread_options.close_on_response)
|
||||
})
|
||||
.edit_thread(
|
||||
&ctx.http,
|
||||
EditThread::new()
|
||||
.locked(thread_options.lock_on_response)
|
||||
.archived(thread_options.close_on_response),
|
||||
)
|
||||
.await
|
||||
{
|
||||
error!(
|
||||
|
@ -1,8 +1,19 @@
|
||||
use std::cmp;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use mongodb::options::FindOptions;
|
||||
use poise::serenity_prelude::{ChannelId, GuildChannel, GuildId, Mentionable, User, UserId};
|
||||
use poise::serenity_prelude::{
|
||||
ChannelId,
|
||||
CreateEmbed,
|
||||
CreateEmbedFooter,
|
||||
CreateMessage,
|
||||
GuildId,
|
||||
Mentionable,
|
||||
User,
|
||||
UserId,
|
||||
};
|
||||
use poise::CreateReply;
|
||||
use serenity::prelude::SerenityError;
|
||||
use tokio::task::JoinHandle;
|
||||
use tracing::{debug, error, warn};
|
||||
|
||||
@ -11,7 +22,6 @@ use super::*;
|
||||
use crate::db::database::Database;
|
||||
use crate::db::model::Muted;
|
||||
use crate::model::application::{Configuration, Mute};
|
||||
use crate::serenity::SerenityError;
|
||||
use crate::utils::bot::get_member;
|
||||
use crate::{Context, Error};
|
||||
|
||||
@ -20,8 +30,6 @@ pub enum ModerationKind {
|
||||
Unmute(User, User, Option<Error>), // User, Command author, Error
|
||||
Ban(User, User, Option<String>, Option<SerenityError>), // User, Command author, Reason, Error
|
||||
Unban(User, User, Option<SerenityError>), // User, Command author, Error
|
||||
Lock(GuildChannel, User, Option<Error>), // Channel name, Command author, Error
|
||||
Unlock(GuildChannel, User, Option<Error>), // Channel name, Command author, Error
|
||||
}
|
||||
pub enum BanKind {
|
||||
Ban(User, Option<u8>, Option<String>), // User, Amount of days to delete messages, Reason
|
||||
@ -48,7 +56,7 @@ pub async fn mute_on_join(ctx: &serenity::Context, new_member: &mut serenity::Me
|
||||
if found {
|
||||
debug!("Muted member {} rejoined the server", new_member.user.tag());
|
||||
if new_member
|
||||
.add_role(&ctx.http, RoleId(data.configuration.general.mute.role))
|
||||
.add_role(&ctx.http, data.configuration.general.mute.role)
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
@ -85,7 +93,7 @@ pub fn queue_unmute_member(
|
||||
.find_and_delete::<Muted>(
|
||||
"muted",
|
||||
Muted {
|
||||
user_id: Some(user_id.0.to_string()),
|
||||
user_id: Some(user_id.to_string()),
|
||||
..Default::default()
|
||||
}
|
||||
.into(),
|
||||
@ -123,18 +131,16 @@ pub async fn respond_moderation<'a>(
|
||||
) -> Result<(), Error> {
|
||||
let current_user = ctx.serenity_context().http.get_current_user().await?;
|
||||
|
||||
let send_ephemeral = RwLock::new(false);
|
||||
let send_ephemeral = Arc::new(Mutex::new(false));
|
||||
|
||||
let create_embed = |f: &mut serenity::CreateEmbed| {
|
||||
let mut moderated_user: Option<&User> = None;
|
||||
let create_embed = || {
|
||||
let f = CreateEmbed::new();
|
||||
|
||||
let result = match moderation {
|
||||
ModerationKind::Mute(user, author, reason, expires, error) => {
|
||||
moderated_user = Some(user);
|
||||
|
||||
let embed = match error {
|
||||
Some(err) => {
|
||||
*send_ephemeral.write().unwrap() = true;
|
||||
*send_ephemeral.lock().unwrap() = true;
|
||||
|
||||
f.title(format!("Failed to mute {}", user.tag()))
|
||||
.field("Exception", err.to_string(), false)
|
||||
@ -158,40 +164,37 @@ pub async fn respond_moderation<'a>(
|
||||
|
||||
// add expiration date to embed if mute has a duration
|
||||
if let Some(expire) = expires {
|
||||
embed.field("Expires", expire, true);
|
||||
embed.field("Expires", expire, true)
|
||||
} else {
|
||||
embed
|
||||
}
|
||||
embed
|
||||
},
|
||||
ModerationKind::Unmute(user, author, error) => {
|
||||
moderated_user = Some(user);
|
||||
match error {
|
||||
Some(err) => {
|
||||
*send_ephemeral.write().unwrap() = true;
|
||||
ModerationKind::Unmute(user, author, error) => match error {
|
||||
Some(err) => {
|
||||
*send_ephemeral.lock().unwrap() = true;
|
||||
|
||||
f.title(format!("Failed to unmute {}", user.tag()))
|
||||
.field("Exception", err.to_string(), false)
|
||||
.field(
|
||||
"Action",
|
||||
format!(
|
||||
"{} was unmuted by {} but failed",
|
||||
user.mention(),
|
||||
author.mention()
|
||||
),
|
||||
false,
|
||||
)
|
||||
},
|
||||
None => f.title(format!("Unmuted {}", user.tag())).field(
|
||||
"Action",
|
||||
format!("{} was unmuted by {}", user.mention(), author.mention()),
|
||||
false,
|
||||
),
|
||||
}
|
||||
f.title(format!("Failed to unmute {}", user.tag()))
|
||||
.field("Exception", err.to_string(), false)
|
||||
.field(
|
||||
"Action",
|
||||
format!(
|
||||
"{} was unmuted by {} but failed",
|
||||
user.mention(),
|
||||
author.mention()
|
||||
),
|
||||
false,
|
||||
)
|
||||
},
|
||||
None => f.title(format!("Unmuted {}", user.tag())).field(
|
||||
"Action",
|
||||
format!("{} was unmuted by {}", user.mention(), author.mention()),
|
||||
false,
|
||||
),
|
||||
},
|
||||
ModerationKind::Ban(user, author, reason, error) => {
|
||||
moderated_user = Some(user);
|
||||
let f = match error {
|
||||
Some(err) => {
|
||||
*send_ephemeral.write().unwrap() = true;
|
||||
*send_ephemeral.lock().unwrap() = true;
|
||||
|
||||
f.title(format!("Failed to ban {}", user.tag()))
|
||||
.field("Exception", err.to_string(), false)
|
||||
@ -217,164 +220,94 @@ pub async fn respond_moderation<'a>(
|
||||
f
|
||||
}
|
||||
},
|
||||
ModerationKind::Unban(user, author, error) => {
|
||||
moderated_user = Some(user);
|
||||
match error {
|
||||
Some(err) => {
|
||||
*send_ephemeral.write().unwrap() = true;
|
||||
|
||||
f.title(format!("Failed to unban {}", user.tag()))
|
||||
.field("Exception", err.to_string(), false)
|
||||
.field(
|
||||
"Action",
|
||||
format!(
|
||||
"{} was unbanned by {} but failed",
|
||||
user.mention(),
|
||||
author.mention()
|
||||
),
|
||||
false,
|
||||
)
|
||||
},
|
||||
None => f.title(format!("Unbanned {}", user.tag())).field(
|
||||
"Action",
|
||||
format!("{} was unbanned by {}", user.mention(), author.mention()),
|
||||
false,
|
||||
),
|
||||
}
|
||||
},
|
||||
ModerationKind::Lock(channel, author, error) => match error {
|
||||
ModerationKind::Unban(user, author, error) => match error {
|
||||
Some(err) => {
|
||||
*send_ephemeral.write().unwrap() = true;
|
||||
*send_ephemeral.lock().unwrap() = true;
|
||||
|
||||
f.title(format!("Failed to lock {} ", channel.name()))
|
||||
f.title(format!("Failed to unban {}", user.tag()))
|
||||
.field("Exception", err.to_string(), false)
|
||||
.field(
|
||||
"Action",
|
||||
format!(
|
||||
"{} was locked by {} but failed",
|
||||
channel.mention(),
|
||||
"{} was unbanned by {} but failed",
|
||||
user.mention(),
|
||||
author.mention()
|
||||
),
|
||||
false,
|
||||
)
|
||||
},
|
||||
None => f
|
||||
.title(format!("Locked {}", channel.name()))
|
||||
.description(
|
||||
"Unlocking the channel will restore the original permission overwrites.",
|
||||
)
|
||||
.field(
|
||||
"Action",
|
||||
format!("{} was locked by {}", channel.mention(), author.mention()),
|
||||
false,
|
||||
),
|
||||
},
|
||||
ModerationKind::Unlock(channel, author, error) => match error {
|
||||
Some(err) => {
|
||||
*send_ephemeral.write().unwrap() = true;
|
||||
|
||||
f.title(format!("Failed to unlock {}", channel.name()))
|
||||
.field("Exception", err.to_string(), false)
|
||||
.field(
|
||||
"Action",
|
||||
format!(
|
||||
"{} was unlocked by {} but failed",
|
||||
channel.mention(),
|
||||
author.mention()
|
||||
),
|
||||
false,
|
||||
)
|
||||
},
|
||||
None => f
|
||||
.title(format!("Unlocked {}", channel.name()))
|
||||
.description("Restored original permission overwrites.")
|
||||
.field(
|
||||
"Action",
|
||||
format!("{} was unlocked by {}", channel.mention(), author.mention()),
|
||||
false,
|
||||
),
|
||||
None => f.title(format!("Unbanned {}", user.tag())).field(
|
||||
"Action",
|
||||
format!("{} was unbanned by {}", user.mention(), author.mention()),
|
||||
false,
|
||||
),
|
||||
},
|
||||
}
|
||||
.color(configuration.general.embed_color);
|
||||
|
||||
let user = if let Some(user) = moderated_user {
|
||||
user.face()
|
||||
} else {
|
||||
current_user.face()
|
||||
};
|
||||
let user = current_user.face();
|
||||
|
||||
result.thumbnail(&user).footer(|f| {
|
||||
f.text("ReVanced");
|
||||
f.icon_url(current_user.face())
|
||||
});
|
||||
result
|
||||
.thumbnail(&user)
|
||||
.footer(CreateEmbedFooter::new("ReVanced").icon_url(&user))
|
||||
};
|
||||
|
||||
let send_ephemeral = *send_ephemeral.lock().unwrap();
|
||||
|
||||
let reply = ctx
|
||||
.send(|reply| {
|
||||
reply
|
||||
.embed(|embed| {
|
||||
create_embed(embed);
|
||||
embed
|
||||
})
|
||||
.ephemeral(*send_ephemeral.read().unwrap())
|
||||
})
|
||||
.send(
|
||||
CreateReply::new()
|
||||
.embed(create_embed())
|
||||
.ephemeral(send_ephemeral),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let response = reply.message().await?;
|
||||
ChannelId(configuration.general.logging_channel)
|
||||
.send_message(&ctx.serenity_context().http, |reply| {
|
||||
reply.embed(|embed| {
|
||||
create_embed(embed);
|
||||
embed.field(
|
||||
"Reference",
|
||||
format!(
|
||||
"[Jump to message](https://discord.com/channels/{}/{}/{})",
|
||||
ctx.guild_id().unwrap().0,
|
||||
response.channel_id,
|
||||
response.id
|
||||
),
|
||||
true,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
ChannelId::from(configuration.general.logging_channel)
|
||||
.send_message(
|
||||
&ctx.serenity_context().http,
|
||||
CreateMessage::new().embed(create_embed().field(
|
||||
"Reference",
|
||||
format!(
|
||||
"[Jump to message](https://discord.com/channels/{}/{}/{})",
|
||||
ctx.guild_id().unwrap(),
|
||||
response.channel_id,
|
||||
response.id
|
||||
),
|
||||
true,
|
||||
)),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn ban_moderation(ctx: &Context<'_>, kind: &BanKind) -> Option<SerenityError> {
|
||||
let guild_id = ctx.guild_id().unwrap().0;
|
||||
let guild_id = ctx.guild_id().unwrap();
|
||||
|
||||
let http = &ctx.serenity_context().http;
|
||||
|
||||
match kind {
|
||||
BanKind::Ban(user, dmd, reason) => {
|
||||
let reason = reason
|
||||
.clone()
|
||||
.or_else(|| Some("None specified".to_string()))
|
||||
.unwrap();
|
||||
let reason = reason.as_deref().or(Some("None specified"));
|
||||
|
||||
let ban_result = http
|
||||
.ban_user(
|
||||
guild_id,
|
||||
user.id.0,
|
||||
cmp::min(dmd.unwrap_or(0), 7),
|
||||
reason.as_ref(),
|
||||
)
|
||||
.ban_user(guild_id, user.id, cmp::min(dmd.unwrap_or(0), 7), reason)
|
||||
.await;
|
||||
|
||||
if let Err(err) = ban_result {
|
||||
error!("Failed to ban user {}: {}", user.id.0, err);
|
||||
error!("Failed to ban user {}: {}", user.id, err);
|
||||
Some(err)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
BanKind::Unban(user) => {
|
||||
let unban_result = http.remove_ban(guild_id, user.id.0, None).await;
|
||||
let unban_result = http.remove_ban(guild_id, user.id, None).await;
|
||||
|
||||
if let Err(err) = unban_result {
|
||||
error!("Failed to unban user {}: {}", user.id.0, err);
|
||||
error!("Failed to unban user {}: {}", user.id, err);
|
||||
Some(err)
|
||||
} else {
|
||||
None
|
||||
@ -391,7 +324,7 @@ pub async fn mute_moderation(
|
||||
let mute_role_id = config.role;
|
||||
let take = &config.take;
|
||||
|
||||
let is_currently_muted = member.roles.iter().any(|r| r.0 == mute_role_id);
|
||||
let is_currently_muted = member.roles.iter().any(|r| r.get() == mute_role_id);
|
||||
|
||||
member
|
||||
.add_role(&ctx.serenity_context().http, mute_role_id)
|
||||
@ -401,7 +334,7 @@ pub async fn mute_moderation(
|
||||
let removed_roles = member
|
||||
.roles
|
||||
.iter()
|
||||
.filter(|r| take.contains(&r.0))
|
||||
.filter(|r| take.contains(&r.get()))
|
||||
.copied()
|
||||
.collect::<Vec<_>>();
|
||||
// take them from the member.
|
||||
|
@ -1,5 +1,13 @@
|
||||
use base64::Engine;
|
||||
use poise::serenity_prelude::{ButtonStyle, ReactionType, Timestamp};
|
||||
use poise::serenity_prelude::{
|
||||
CreateActionRow,
|
||||
CreateButton,
|
||||
CreateEmbed,
|
||||
CreateEmbedFooter,
|
||||
CreateInteractionResponseMessage,
|
||||
ReactionType,
|
||||
Timestamp,
|
||||
};
|
||||
use reqwest::StatusCode;
|
||||
use tracing::log::{error, trace};
|
||||
|
||||
@ -11,7 +19,7 @@ pub async fn handle_poll(
|
||||
interaction: &serenity::Interaction,
|
||||
poll_id: u64,
|
||||
min_join_date: Timestamp,
|
||||
) -> Result<(), crate::serenity::SerenityError> {
|
||||
) -> Result<(), serenity::prelude::SerenityError> {
|
||||
trace!("Handling poll: {}.", poll_id);
|
||||
|
||||
let data = get_data_lock(ctx).await;
|
||||
@ -52,40 +60,32 @@ pub async fn handle_poll(
|
||||
.unwrap();
|
||||
|
||||
component
|
||||
.create_interaction_response(&ctx.http, |r| {
|
||||
r.interaction_response_data(|m| {
|
||||
if let Ok(token) = result.as_deref() {
|
||||
let url = format!("https://revanced.app/poll#{token}");
|
||||
m.components(|c| {
|
||||
c.create_action_row(|r| {
|
||||
r.create_button(|b| {
|
||||
b.label("Vote")
|
||||
.emoji(ReactionType::Unicode("🗳️".to_string()))
|
||||
.style(ButtonStyle::Link)
|
||||
.url(&url)
|
||||
})
|
||||
})
|
||||
})
|
||||
} else {
|
||||
m
|
||||
}
|
||||
.ephemeral(true)
|
||||
.embed(|e| {
|
||||
match result {
|
||||
Ok(_) => e
|
||||
.title("Cast your vote")
|
||||
.description("You can now vote on the poll."),
|
||||
Err(msg) => e.title("Error").description(msg),
|
||||
}
|
||||
.color(data.configuration.general.embed_color)
|
||||
.thumbnail(&icon_url)
|
||||
.footer(|f| {
|
||||
f.text("ReVanced");
|
||||
f.icon_url(&icon_url)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
.create_response(
|
||||
&ctx.http,
|
||||
serenity::CreateInteractionResponse::Message(if let Ok(token) = result.as_deref() {
|
||||
let url = format!("https://revanced.app/poll#{token}");
|
||||
|
||||
CreateInteractionResponseMessage::new().components(vec![CreateActionRow::Buttons(
|
||||
vec![CreateButton::new_link(url)
|
||||
.label("Vote")
|
||||
.emoji(ReactionType::Unicode("🗳️".to_string()))],
|
||||
)])
|
||||
} else {
|
||||
CreateInteractionResponseMessage::new()
|
||||
.ephemeral(true)
|
||||
.embed(
|
||||
match result {
|
||||
Ok(_) => CreateEmbed::new()
|
||||
.title("Cast your vote")
|
||||
.description("You can now vote on the poll."),
|
||||
Err(msg) => CreateEmbed::new().title("Error").description(msg),
|
||||
}
|
||||
.color(data.configuration.general.embed_color)
|
||||
.thumbnail(&icon_url)
|
||||
.footer(CreateEmbedFooter::new("ReVanced").icon_url(&icon_url)),
|
||||
)
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
|
Loading…
x
Reference in New Issue
Block a user