feat: Don't delete triggering message when replying to message

This commit is contained in:
oSumAtrIX 2024-06-04 03:07:13 +02:00
parent 6bd4890cad
commit b87c601e53
No known key found for this signature in database
GPG Key ID: A9B3094ACDB604B4
12 changed files with 890 additions and 707 deletions

992
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -5,12 +5,11 @@ homepage = "https://revanced.app"
license = "GPL-3.0" license = "GPL-3.0"
name = "revanced-discord-bot" name = "revanced-discord-bot"
repository = "https://github.com/revanced/revanced-discord-bot" repository = "https://github.com/revanced/revanced-discord-bot"
version = "2.7.0" version = "2.8.0"
edition = "2021" edition = "2021"
[profile.release] [profile.release]
strip = true strip = true
opt-level = 3
lto = true lto = true
codegen-units = 1 codegen-units = 1
panic = "abort" panic = "abort"
@ -20,14 +19,14 @@ bson = "2.4"
serde_with_macros = "3.4" serde_with_macros = "3.4"
mongodb = "2.4" mongodb = "2.4"
poise = "0.6" poise = "0.6"
decancer = "1.5" decancer = "3.2"
tokio = { version = "1.26", features = ["rt-multi-thread"] } tokio = { version = "1.26", features = ["rt-multi-thread"] }
dotenv = "0.15" dotenv = "0.15"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
regex = "1.7" regex = "1.7"
serde_regex = "1.1" serde_regex = "1.1"
reqwest = { version = "0.11", features = [ reqwest = { version = "0.12", features = [
"rustls-tls", "rustls-tls",
"json", "json",
], default-features = false } ], default-features = false }
@ -39,5 +38,5 @@ tracing = { version = "0.1", features = [
] } ] }
tracing-subscriber = "0.3" tracing-subscriber = "0.3"
hmac-sha256 = "1.1" hmac-sha256 = "1.1"
base64 = "0.21" base64 = "0.22"
parse_duration = "2.1" parse_duration = "2.1"

View File

@ -1,48 +1,28 @@
{ {
"general": { "default_embed": {
"embed_color": 0, "color": 0
"mute": {
"role": 0,
"take": [
0
]
},
"logging_channel": 0
}, },
"mute": {
"role": 0,
"take": [0]
},
"log_channel": 0,
"administrators": { "administrators": {
"roles": [ "roles": [0],
0 "users": [0]
],
"users": [
0
]
}, },
"message_responses": [ "responses": [
{ {
"includes": { "whitelist": {
"channels": [ "channels": [0],
0 "regex": [""]
],
"match": []
}, },
"excludes": { "blacklist": {
"roles": [ "roles": [0],
0 "regex": [""]
],
"match": []
}, },
"condition": { "message": {
"user": { "content": ""
"server_age": 2
}
},
"response": {
"message": ""
},
"thread_options": {
"lock_on_response": false,
"close_on_response": false,
"only_on_first_message": false
}, },
"respond_to_reference": false "respond_to_reference": false
} }

View File

@ -1,33 +1,25 @@
use poise::serenity_prelude::CreateEmbed;
use poise::CreateReply; use poise::CreateReply;
use tracing::debug; use tracing::debug;
use crate::utils::bot::load_configuration; use crate::utils::bot::load_configuration;
use crate::utils::create_default_embed;
use crate::{Context, Error}; use crate::{Context, Error};
/// Reload the Discord bot. /// Reload the Discord bot.
#[poise::command(slash_command)] #[poise::command(slash_command)]
pub async fn reload(ctx: Context<'_>) -> Result<(), Error> { pub async fn reload(ctx: Context<'_>) -> Result<(), Error> {
// Update the configuration // Update the configuration.
let configuration = load_configuration(); let configuration = load_configuration();
// Use the embed color from the updated configuration let embed = create_default_embed(&configuration);
let embed_color = configuration.general.embed_color;
// Also save the new configuration to the user data
ctx.data().write().await.configuration = configuration; ctx.data().write().await.configuration = configuration;
debug!("{} reloaded the configuration.", ctx.author().name); debug!("{} reloaded the configuration.", ctx.author().name);
ctx.send( ctx.send(CreateReply {
CreateReply { embeds: vec![embed.description("Reloading configuration...")],
embeds: vec![ ephemeral: Some(true),
CreateEmbed::new() ..Default::default()
.description("Reloading configuration...") })
.color(embed_color),
],
ephemeral: Some(true),
..Default::default()
}
)
.await?; .await?;
Ok(()) Ok(())
@ -38,19 +30,13 @@ pub async fn reload(ctx: Context<'_>) -> Result<(), Error> {
pub async fn stop(ctx: Context<'_>) -> Result<(), Error> { pub async fn stop(ctx: Context<'_>) -> Result<(), Error> {
debug!("{} stopped the bot.", ctx.author().name); debug!("{} stopped the bot.", ctx.author().name);
let color = ctx.data().read().await.configuration.general.embed_color; let configuration = &ctx.data().read().await.configuration;
ctx.send( ctx.send(CreateReply {
CreateReply { ephemeral: Some(true),
ephemeral: Some(true), embeds: vec![create_default_embed(configuration).description("Stopping the bot...")],
embeds: vec![ ..Default::default()
CreateEmbed::new() })
.description("Stopping the bot...")
.color(color),
],
..Default::default()
}
)
.await?; .await?;
ctx.framework().shard_manager().shutdown_all().await; ctx.framework().shard_manager().shutdown_all().await;

View File

@ -2,7 +2,7 @@ use bson::{doc, Document};
use chrono::Utc; use chrono::Utc;
use mongodb::options::{UpdateModifications, UpdateOptions}; use mongodb::options::{UpdateModifications, UpdateOptions};
use poise::serenity_prelude::{ use poise::serenity_prelude::{
self as serenity, CreateEmbed, CreateEmbedFooter, EditMessage, GetMessages, Mentionable, UserId, CreateEmbedFooter, EditMessage, GetMessages, Mentionable, UserId,
}; };
use poise::CreateReply; use poise::CreateReply;
use tracing::{debug, trace}; use tracing::{debug, trace};
@ -13,7 +13,7 @@ use crate::utils::macros::to_user;
use crate::utils::moderation::{ use crate::utils::moderation::{
ban_moderation, queue_unmute_member, respond_moderation, BanKind, ModerationKind, ban_moderation, queue_unmute_member, respond_moderation, BanKind, ModerationKind,
}; };
use crate::utils::parse_duration; use crate::utils::{create_default_embed, parse_duration};
use crate::{Context, Error}; use crate::{Context, Error};
/// Unmute a member. /// Unmute a member.
@ -41,7 +41,7 @@ pub async fn unmute(
data.database.clone(), data.database.clone(),
ctx.guild_id().unwrap(), ctx.guild_id().unwrap(),
id, id,
configuration.general.mute.role, configuration.mute.role,
0, 0,
) )
.await .await
@ -73,7 +73,7 @@ pub async fn mute(
let configuration = &data.configuration; let configuration = &data.configuration;
let author = ctx.author(); let author = ctx.author();
let mute = &configuration.general.mute; let mute = &configuration.mute;
let guild_id = ctx.guild_id().unwrap(); let guild_id = ctx.guild_id().unwrap();
let discord = ctx.serenity_context(); let discord = ctx.serenity_context();
@ -185,7 +185,6 @@ pub async fn purge(
let data = ctx.data().read().await; let data = ctx.data().read().await;
let configuration = &data.configuration; let configuration = &data.configuration;
let embed_color = configuration.general.embed_color;
let channel = ctx.channel_id(); let channel = ctx.channel_id();
let too_old_timestamp = Utc::now().timestamp() - MAX_BULK_DELETE_AGO_SECS; let too_old_timestamp = Utc::now().timestamp() - MAX_BULK_DELETE_AGO_SECS;
@ -196,10 +195,9 @@ pub async fn purge(
let handle = ctx let handle = ctx
.send(CreateReply { .send(CreateReply {
embeds: vec![CreateEmbed::new() embeds: vec![create_default_embed(configuration)
.title("Purging messages") .title("Purging messages")
.description("Accumulating...") .description("Accumulating...")
.color(embed_color)
.thumbnail(&image)], .thumbnail(&image)],
..Default::default() ..Default::default()
}) })
@ -271,14 +269,12 @@ pub async fn purge(
.edit( .edit(
&ctx.serenity_context(), &ctx.serenity_context(),
EditMessage::new().embed( EditMessage::new().embed(
serenity::CreateEmbed::default() create_default_embed(configuration)
.title("Purge successful") .title("Purge successful")
.field("Deleted messages", deleted_amount.to_string(), false) .field("Deleted messages", deleted_amount.to_string(), false)
.field("Action by", author.mention().to_string(), false) .field("Action by", author.mention().to_string(), false)
.color(embed_color)
.thumbnail(&image) .thumbnail(&image)
.footer(CreateEmbedFooter::new("ReVanced").icon_url(image)) .footer(CreateEmbedFooter::new("ReVanced").icon_url(image)),
.clone(),
), ),
) )
.await?; .await?;

View File

@ -6,7 +6,7 @@ use crate::utils::moderation::queue_unmute_member;
pub async fn load_muted_members(ctx: &serenity::Context, data: &Arc<RwLock<Data>>) { pub async fn load_muted_members(ctx: &serenity::Context, data: &Arc<RwLock<Data>>) {
let data = &mut *data.write().await; let data = &mut *data.write().await;
let mute_role_id = data.configuration.general.mute.role; let mute_role_id = data.configuration.mute.role;
let mut cursor = data let mut cursor = data
.database .database

View File

@ -6,11 +6,12 @@ use commands::{configuration, misc, moderation};
use db::database::Database; use db::database::Database;
use events::event_handler; use events::event_handler;
use poise::serenity_prelude::prelude::{RwLock, TypeMapKey}; use poise::serenity_prelude::prelude::{RwLock, TypeMapKey};
use poise::serenity_prelude::{CreateEmbed, UserId}; use poise::serenity_prelude::{UserId};
use poise::CreateReply; use poise::CreateReply;
use tokio::task::JoinHandle; use tokio::task::JoinHandle;
use tracing::{error, trace}; use tracing::{error, trace};
use utils::bot::load_configuration; use utils::bot::load_configuration;
use utils::create_default_embed;
use crate::model::application::Configuration; use crate::model::application::Configuration;
@ -125,12 +126,11 @@ async fn main() {
if let Err(e) = ctx if let Err(e) = ctx
.send(CreateReply { .send(CreateReply {
ephemeral: Some(true), ephemeral: Some(true),
embeds: vec![CreateEmbed::new() embeds: vec![create_default_embed(configuration)
.title("Permission error") .title("Permission error")
.description( .description(
"You do not have permission to use this command.", "You do not have permission to use this command.",
) )
.color(configuration.general.embed_color)
.thumbnail(member.user.avatar_url().unwrap_or_else( .thumbnail(member.user.avatar_url().unwrap_or_else(
|| member.user.default_avatar_url(), || member.user.default_avatar_url(),
))], ))],

View File

@ -9,9 +9,11 @@ use serde::{Deserialize, Serialize};
#[derive(Default, Serialize, Deserialize)] #[derive(Default, Serialize, Deserialize)]
pub struct Configuration { pub struct Configuration {
pub general: General,
pub administrators: Administrators, pub administrators: Administrators,
pub message_responses: Vec<MessageResponse>, pub default_embed: Embed,
pub mute: Mute,
pub log_channel: u64,
pub responses: Vec<Response>,
} }
const CONFIG_PATH: &str = "configuration.json"; const CONFIG_PATH: &str = "configuration.json";
@ -39,15 +41,15 @@ impl Configuration {
sys_config_dir.to_string_lossy() sys_config_dir.to_string_lossy()
); );
// config file in current dir // Config file in the current directory.
let mut file = if Path::new(CONFIG_PATH).exists() { let mut file = if Path::new(CONFIG_PATH).exists() {
File::open(CONFIG_PATH)? File::open(CONFIG_PATH)?
} }
// config file in system dir (on *nix: `~/.config/revanced-discord-bot/`) // Config file in the system directory.
else if Path::new(&sys_config).exists() { else if Path::new(&sys_config).exists() {
File::open(sys_config)? File::open(sys_config)?
} }
// create defalt config // Create a default config file.
else { else {
let default_config = Configuration::default(); let default_config = Configuration::default();
default_config.save()?; default_config.save()?;
@ -62,13 +64,6 @@ impl Configuration {
} }
} }
#[derive(Default, Serialize, Deserialize)]
pub struct General {
pub embed_color: i32,
pub mute: Mute,
pub logging_channel: u64,
}
#[derive(Default, Serialize, Deserialize)] #[derive(Default, Serialize, Deserialize)]
pub struct Mute { pub struct Mute {
pub role: u64, pub role: u64,
@ -81,89 +76,57 @@ pub struct Administrators {
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct MessageResponse { pub struct Response {
pub includes: Option<Includes>, pub whitelist: Option<Trigger>,
pub excludes: Option<Excludes>, pub blacklist: Option<Trigger>,
pub condition: Option<Condition>, pub message: Message,
pub response: Response,
pub thread_options: Option<ThreadOptions>,
pub respond_to_reference: Option<bool>, pub respond_to_reference: Option<bool>,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct ThreadOptions { pub struct Message {
pub close_on_response: bool, pub content: Option<String>,
pub lock_on_response: bool,
pub only_on_first_message: bool,
}
#[derive(Serialize, Deserialize)]
pub struct Response {
pub message: Option<String>,
pub embed: Option<Embed>, pub embed: Option<Embed>,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
#[derive(Default)]
pub struct Embed { pub struct Embed {
pub title: String, pub author: Option<Author>,
pub description: String, pub color: Option<i32>,
pub color: i32, pub title: Option<String>,
pub fields: Vec<Field>, pub description: Option<String>,
pub footer: Footer, pub fields: Option<Vec<Field>>,
pub image: Image, pub footer: Option<Footer>,
pub thumbnail: Thumbnail, pub image_url: Option<String>,
pub author: Author, pub thumbnail_url: Option<String>,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Field { pub struct Field {
pub name: String, pub name: String,
pub value: String, pub value: String,
pub inline: bool, pub inline: Option<bool>,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Footer { pub struct Footer {
pub text: String, pub text: String,
pub icon_url: String, pub icon_url: Option<String>,
}
#[derive(Serialize, Deserialize)]
pub struct Image {
pub url: String,
}
#[derive(Serialize, Deserialize)]
pub struct Thumbnail {
pub url: String,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Author { pub struct Author {
pub name: String, pub name: String,
pub icon_url: String, pub icon_url: Option<String>,
pub url: String, pub url: Option<String>,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Includes { pub struct Trigger {
pub channels: Option<HashSet<u64>>, pub channels: Option<HashSet<u64>>,
pub roles: Option<HashSet<u64>>, pub roles: Option<HashSet<u64>>,
#[serde(rename = "match", with = "serde_regex")] #[serde(with = "serde_regex")]
pub match_field: Vec<Regex>, pub regex: Vec<Regex>,
}
#[derive(Serialize, Deserialize)]
pub struct Excludes {
pub roles: Option<HashSet<u64>>,
}
#[derive(Serialize, Deserialize)]
pub struct Condition {
pub user: User,
}
#[derive(Serialize, Deserialize)]
pub struct User {
pub server_age: i64,
} }

View File

@ -27,7 +27,7 @@ pub async fn cure(
} }
} }
let mut cured_name = decancer::cure(&name).replace( let mut cured_name = decancer::cure!(&name).unwrap().as_str().replace(
|c: char| !(c == ' ' || c == '-' || c == '_' || c.is_ascii_alphanumeric()), |c: char| !(c == ' ' || c == '-' || c == '_' || c.is_ascii_alphanumeric()),
"", "",
); );

View File

@ -1,18 +1,37 @@
use poise::serenity_prelude::CreateMessage;
use serenity::Message;
use chrono::{DateTime, Duration, NaiveDateTime, Utc};
use poise::serenity_prelude::{
CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter, CreateMessage, EditThread, GetMessages,
};
use regex::Regex;
use tracing::log::error; use tracing::log::error;
use super::*; use super::*;
use crate::{BotData}; use crate::{
model::application::Trigger,
BotData,
};
pub fn contains_match(regex: &[Regex], text: &str) -> bool { impl Trigger {
regex.iter().any(|r| r.is_match(text)) fn matches(&self, new_message: &Message, member_roles: &[RoleId]) -> bool {
if let Some(channels) = &self.channels {
if !channels.contains(&new_message.channel_id.get()) {
return true;
}
}
if let Some(roles) = &self.roles {
if !member_roles
.iter()
.any(|&member_role| roles.contains(&member_role.get()))
{
return true;
}
}
if !self.regex.iter().any(|r| r.is_match(&new_message.content)) {
return true;
}
false
}
} }
pub async fn handle_message_response( pub async fn handle_message_response(
@ -24,191 +43,53 @@ pub async fn handle_message_response(
return; return;
} }
let responses = &data.read().await.configuration.message_responses; let configuration = &data.read().await.configuration;
let message = &new_message.content;
let mut guild_message = None; let member_roles = &new_message.member.as_ref().unwrap().roles;
let member = &new_message.member.as_ref().unwrap(); for response in &configuration.responses {
let member_roles = &member.roles; if let Some(whitelist) = &response.whitelist {
if whitelist.matches(new_message, member_roles) {
let joined_at = member.joined_at.unwrap().unix_timestamp();
let must_joined_at = DateTime::<Utc>::from_naive_utc_and_offset(
NaiveDateTime::from_timestamp_opt(joined_at, 0).unwrap(),
Utc,
);
for response in responses {
if let Some(includes) = &response.includes {
if let Some(roles) = &includes.roles {
// check if the role is whitelisted
if !roles.iter().any(|&role_id| {
member_roles
.iter()
.any(|&member_role| role_id == member_role.get())
}) {
continue;
}
}
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.get()) {
if response.thread_options.is_some() {
if guild_message.is_none() {
guild_message = Some(
new_message
.channel(&ctx.http)
.await
.unwrap()
.guild()
.unwrap(),
);
};
let Some(parent_id) = guild_message.as_ref().unwrap().parent_id else {
continue;
};
if !channels.contains(&parent_id.get()) {
continue;
}
} else {
continue;
}
}
}
// check if message matches regex
if !contains_match(&includes.match_field, message) {
tracing::log::trace!("Message does not match regex");
continue; continue;
} }
} }
if let Some(excludes) = &response.excludes { if let Some(blacklist) = &response.blacklist {
// check if the role is blacklisted if blacklist.matches(new_message, member_roles) {
if let Some(roles) = &excludes.roles { continue;
if roles.iter().any(|&role_id| {
member_roles
.iter()
.any(|&member_role| role_id == member_role.get())
}) {
continue;
}
} }
} }
if let Some(condition) = &response.condition { if let Err(err) = new_message
let min_age = condition.user.server_age; .channel_id
if min_age != 0 {
let but_joined_at = Utc::now() - Duration::days(min_age);
if must_joined_at <= but_joined_at {
continue;
}
}
}
let channel_id = new_message.channel_id;
let mut message_reference: Option<&serenity::Message> = None;
// If the message has a reference and the response is set to respond to references, respond to the reference
if let Some(respond_to_reference) = response.respond_to_reference {
if respond_to_reference {
if let Some(reference) = &new_message.referenced_message {
message_reference = Some(reference.as_ref());
if let Err(err) = new_message.delete(&ctx.http).await {
error!(
"Failed to delete the message from {}. Error: {:?}",
new_message.author.tag(),
err
);
}
}
}
}
if let Err(err) = channel_id
.send_message(&ctx.http, { .send_message(&ctx.http, {
let mut message = CreateMessage::default(); 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 { message = message.reference_message(
Some(embed) => message.embed( if let Some(reference) = &new_message.referenced_message {
CreateEmbed::new() reference.as_ref()
.title(&embed.title) } else {
.description(&embed.description) new_message
.color(embed.color) },
.fields(embed.fields.iter().map(|field| { );
(field.name.clone(), field.value.clone(), field.inline)
})) if let Some(embed_configuration) = &response.message.embed {
.footer( message = message.embed(create_embed(configuration, embed_configuration))
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()),
} }
if let Some(content) = &response.message.content {
message = message.content(content)
}
message
}) })
.await .await
{ {
error!( error!(
"Failed to reply to the message from {}. Error: {:?}", "Failed to reply to {}. Error: {:?}",
new_message.author.tag(), new_message.author.tag(),
err err
); );
} else if let Some(thread_options) = &response.thread_options {
let mut channel = channel_id
.to_channel(&ctx.http)
.await
.unwrap()
.guild()
.unwrap();
// only apply this thread if the channel is a thread
if channel.thread_metadata.is_none() {
return;
}
// only edit this thread if the message is the first one
if thread_options.only_on_first_message
&& !channel_id
.messages(&ctx.http, GetMessages::new().limit(1).before(new_message))
.await
.unwrap()
.is_empty()
{
return;
}
if let Err(err) = channel
.edit_thread(
&ctx.http,
EditThread::new()
.locked(thread_options.lock_on_response)
.archived(thread_options.close_on_response),
)
.await
{
error!(
"Failed to edit the thread from {}. Error: {:?}",
new_message.author.tag(),
err
);
}
} }
} }
} }

View File

@ -1,5 +1,9 @@
use chrono::Duration; use chrono::Duration;
use poise::serenity_prelude::{self as serenity, Member, RoleId}; use poise::serenity_prelude::{
self as serenity, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter, Member, RoleId,
};
use crate::model::application::{Configuration, Embed};
pub mod bot; pub mod bot;
pub mod code_embed; pub mod code_embed;
@ -12,3 +16,94 @@ pub fn parse_duration(duration: String) -> Result<Duration, parse_duration::pars
let d = parse_duration::parse(&duration)?; let d = parse_duration::parse(&duration)?;
Ok(Duration::nanoseconds(d.as_nanos() as i64)) Ok(Duration::nanoseconds(d.as_nanos() as i64))
} }
pub fn create_embed(configuration: &Configuration, embed_configuration: &Embed) -> CreateEmbed {
let mut create_embed = CreateEmbed::new();
if let Some(title) = embed_configuration
.title
.as_ref()
.or(configuration.default_embed.title.as_ref())
{
create_embed = create_embed.title(title);
}
if let Some(description) = embed_configuration
.description
.as_ref()
.or(configuration.default_embed.description.as_ref())
{
create_embed = create_embed.description(description);
}
if let Some(color) = embed_configuration
.color
.or(configuration.default_embed.color)
{
create_embed = create_embed.color(color);
}
if let Some(fields) = embed_configuration
.fields
.as_ref()
.or(configuration.default_embed.fields.as_ref())
{
for field in fields {
create_embed =
create_embed.field(&field.name, &field.value, field.inline.unwrap_or(false));
}
}
if let Some(footer) = embed_configuration
.footer
.as_ref()
.or(configuration.default_embed.footer.as_ref())
{
let mut create_footer = CreateEmbedFooter::new(&footer.text);
if let Some(icon_url) = &footer.icon_url {
create_footer = create_footer.icon_url(icon_url);
}
create_embed = create_embed.footer(create_footer);
}
if let Some(image_url) = embed_configuration
.image_url
.as_ref()
.or(configuration.default_embed.image_url.as_ref())
{
create_embed = create_embed.image(image_url);
}
if let Some(thumbnail_url) = embed_configuration
.thumbnail_url
.as_ref()
.or(configuration.default_embed.thumbnail_url.as_ref())
{
create_embed = create_embed.thumbnail(thumbnail_url);
}
if let Some(author) = embed_configuration
.author
.as_ref()
.or(configuration.default_embed.author.as_ref())
{
let mut create_author = CreateEmbedAuthor::new(&author.name);
if let Some(icon_url) = &author.icon_url {
create_author = create_author.icon_url(icon_url);
}
if let Some(url) = &author.url {
create_author = create_author.url(url);
}
create_embed = create_embed.author(create_author);
}
create_embed
}
pub fn create_default_embed(configuration: &Configuration) -> CreateEmbed {
create_embed(configuration, &configuration.default_embed)
}

View File

@ -3,7 +3,7 @@ use std::sync::{Arc, Mutex};
use mongodb::options::FindOptions; use mongodb::options::FindOptions;
use poise::serenity_prelude::{ use poise::serenity_prelude::{
ChannelId, CreateEmbed, CreateEmbedFooter, CreateMessage, GuildId, Mentionable, User, UserId, ChannelId, CreateEmbedFooter, CreateMessage, GuildId, Mentionable, User, UserId,
}; };
use poise::CreateReply; use poise::CreateReply;
use serenity::prelude::SerenityError; use serenity::prelude::SerenityError;
@ -48,7 +48,7 @@ pub async fn mute_on_join(ctx: &serenity::Context, new_member: &serenity::Member
if found { if found {
debug!("Muted member {} rejoined the server", new_member.user.tag()); debug!("Muted member {} rejoined the server", new_member.user.tag());
if new_member if new_member
.add_role(&ctx.http, data.configuration.general.mute.role) .add_role(&ctx.http, data.configuration.mute.role)
.await .await
.is_ok() .is_ok()
{ {
@ -126,15 +126,16 @@ pub async fn respond_moderation<'a>(
let send_ephemeral = Arc::new(Mutex::new(false)); let send_ephemeral = Arc::new(Mutex::new(false));
let create_embed = || { let create_embed = || {
let f = CreateEmbed::new(); let mut create_embed = create_default_embed(configuration);
let result = match moderation { create_embed = match moderation {
ModerationKind::Mute(user, author, reason, expires, error) => { ModerationKind::Mute(user, author, reason, expires, error) => {
let embed = match error { let embed = match error {
Some(err) => { Some(err) => {
*send_ephemeral.lock().unwrap() = true; *send_ephemeral.lock().unwrap() = true;
f.title(format!("Failed to mute {}", user.tag())) create_embed
.title(format!("Failed to mute {}", user.tag()))
.field("Exception", err.to_string(), false) .field("Exception", err.to_string(), false)
.field( .field(
"Action", "Action",
@ -146,7 +147,7 @@ pub async fn respond_moderation<'a>(
false, false,
) )
}, },
None => f.title(format!("Muted {}", user.tag())).field( None => create_embed.title(format!("Muted {}", user.tag())).field(
"Action", "Action",
format!("{} was muted by {}", user.mention(), author.mention()), format!("{} was muted by {}", user.mention(), author.mention()),
false, false,
@ -165,7 +166,8 @@ pub async fn respond_moderation<'a>(
Some(err) => { Some(err) => {
*send_ephemeral.lock().unwrap() = true; *send_ephemeral.lock().unwrap() = true;
f.title(format!("Failed to unmute {}", user.tag())) create_embed
.title(format!("Failed to unmute {}", user.tag()))
.field("Exception", err.to_string(), false) .field("Exception", err.to_string(), false)
.field( .field(
"Action", "Action",
@ -177,7 +179,7 @@ pub async fn respond_moderation<'a>(
false, false,
) )
}, },
None => f.title(format!("Unmuted {}", user.tag())).field( None => create_embed.title(format!("Unmuted {}", user.tag())).field(
"Action", "Action",
format!("{} was unmuted by {}", user.mention(), author.mention()), format!("{} was unmuted by {}", user.mention(), author.mention()),
false, false,
@ -188,7 +190,8 @@ pub async fn respond_moderation<'a>(
Some(err) => { Some(err) => {
*send_ephemeral.lock().unwrap() = true; *send_ephemeral.lock().unwrap() = true;
f.title(format!("Failed to ban {}", user.tag())) create_embed
.title(format!("Failed to ban {}", user.tag()))
.field("Exception", err.to_string(), false) .field("Exception", err.to_string(), false)
.field( .field(
"Action", "Action",
@ -200,7 +203,7 @@ pub async fn respond_moderation<'a>(
false, false,
) )
}, },
None => f.title(format!("Banned {}", user.tag())).field( None => create_embed.title(format!("Banned {}", user.tag())).field(
"Action", "Action",
format!("{} was banned by {}", user.mention(), author.mention()), format!("{} was banned by {}", user.mention(), author.mention()),
false, false,
@ -216,7 +219,8 @@ pub async fn respond_moderation<'a>(
Some(err) => { Some(err) => {
*send_ephemeral.lock().unwrap() = true; *send_ephemeral.lock().unwrap() = true;
f.title(format!("Failed to unban {}", user.tag())) create_embed
.title(format!("Failed to unban {}", user.tag()))
.field("Exception", err.to_string(), false) .field("Exception", err.to_string(), false)
.field( .field(
"Action", "Action",
@ -228,18 +232,19 @@ pub async fn respond_moderation<'a>(
false, false,
) )
}, },
None => f.title(format!("Unbanned {}", user.tag())).field( None => create_embed
"Action", .title(format!("Unbanned {}", user.tag()))
format!("{} was unbanned by {}", user.mention(), author.mention()), .field(
false, "Action",
), format!("{} was unbanned by {}", user.mention(), author.mention()),
false,
),
}, },
} };
.color(configuration.general.embed_color);
let user = current_user.face(); let user = current_user.face();
result create_embed
.thumbnail(&user) .thumbnail(&user)
.footer(CreateEmbedFooter::new("ReVanced").icon_url(&user)) .footer(CreateEmbedFooter::new("ReVanced").icon_url(&user))
}; };
@ -256,7 +261,7 @@ pub async fn respond_moderation<'a>(
let response = reply.message().await?; let response = reply.message().await?;
ChannelId::from(configuration.general.logging_channel) ChannelId::from(configuration.log_channel)
.send_message( .send_message(
&ctx.serenity_context().http, &ctx.serenity_context().http,
CreateMessage::new().embed(create_embed().field( CreateMessage::new().embed(create_embed().field(