mirror of
https://github.com/revanced/revanced-discord-bot.git
synced 2025-04-29 14:04:28 +02:00
feat: Don't delete triggering message when replying to message
This commit is contained in:
parent
6bd4890cad
commit
b87c601e53
992
Cargo.lock
generated
992
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -5,12 +5,11 @@ homepage = "https://revanced.app"
|
||||
license = "GPL-3.0"
|
||||
name = "revanced-discord-bot"
|
||||
repository = "https://github.com/revanced/revanced-discord-bot"
|
||||
version = "2.7.0"
|
||||
version = "2.8.0"
|
||||
edition = "2021"
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
opt-level = 3
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
@ -20,14 +19,14 @@ bson = "2.4"
|
||||
serde_with_macros = "3.4"
|
||||
mongodb = "2.4"
|
||||
poise = "0.6"
|
||||
decancer = "1.5"
|
||||
decancer = "3.2"
|
||||
tokio = { version = "1.26", features = ["rt-multi-thread"] }
|
||||
dotenv = "0.15"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
regex = "1.7"
|
||||
serde_regex = "1.1"
|
||||
reqwest = { version = "0.11", features = [
|
||||
reqwest = { version = "0.12", features = [
|
||||
"rustls-tls",
|
||||
"json",
|
||||
], default-features = false }
|
||||
@ -39,5 +38,5 @@ tracing = { version = "0.1", features = [
|
||||
] }
|
||||
tracing-subscriber = "0.3"
|
||||
hmac-sha256 = "1.1"
|
||||
base64 = "0.21"
|
||||
base64 = "0.22"
|
||||
parse_duration = "2.1"
|
||||
|
@ -1,48 +1,28 @@
|
||||
{
|
||||
"general": {
|
||||
"embed_color": 0,
|
||||
"mute": {
|
||||
"role": 0,
|
||||
"take": [
|
||||
0
|
||||
]
|
||||
},
|
||||
"logging_channel": 0
|
||||
"default_embed": {
|
||||
"color": 0
|
||||
},
|
||||
"mute": {
|
||||
"role": 0,
|
||||
"take": [0]
|
||||
},
|
||||
"log_channel": 0,
|
||||
"administrators": {
|
||||
"roles": [
|
||||
0
|
||||
],
|
||||
"users": [
|
||||
0
|
||||
]
|
||||
"roles": [0],
|
||||
"users": [0]
|
||||
},
|
||||
"message_responses": [
|
||||
"responses": [
|
||||
{
|
||||
"includes": {
|
||||
"channels": [
|
||||
0
|
||||
],
|
||||
"match": []
|
||||
"whitelist": {
|
||||
"channels": [0],
|
||||
"regex": [""]
|
||||
},
|
||||
"excludes": {
|
||||
"roles": [
|
||||
0
|
||||
],
|
||||
"match": []
|
||||
"blacklist": {
|
||||
"roles": [0],
|
||||
"regex": [""]
|
||||
},
|
||||
"condition": {
|
||||
"user": {
|
||||
"server_age": 2
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"message": ""
|
||||
},
|
||||
"thread_options": {
|
||||
"lock_on_response": false,
|
||||
"close_on_response": false,
|
||||
"only_on_first_message": false
|
||||
"message": {
|
||||
"content": ""
|
||||
},
|
||||
"respond_to_reference": false
|
||||
}
|
||||
|
@ -1,33 +1,25 @@
|
||||
use poise::serenity_prelude::CreateEmbed;
|
||||
use poise::CreateReply;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::utils::bot::load_configuration;
|
||||
use crate::utils::create_default_embed;
|
||||
use crate::{Context, Error};
|
||||
|
||||
/// Reload the Discord bot.
|
||||
#[poise::command(slash_command)]
|
||||
pub async fn reload(ctx: Context<'_>) -> Result<(), Error> {
|
||||
// Update the configuration
|
||||
// Update the configuration.
|
||||
let configuration = load_configuration();
|
||||
// Use the embed color from the updated configuration
|
||||
let embed_color = configuration.general.embed_color;
|
||||
// Also save the new configuration to the user data
|
||||
let embed = create_default_embed(&configuration);
|
||||
ctx.data().write().await.configuration = configuration;
|
||||
|
||||
debug!("{} reloaded the configuration.", ctx.author().name);
|
||||
|
||||
ctx.send(
|
||||
CreateReply {
|
||||
embeds: vec![
|
||||
CreateEmbed::new()
|
||||
.description("Reloading configuration...")
|
||||
.color(embed_color),
|
||||
],
|
||||
ephemeral: Some(true),
|
||||
..Default::default()
|
||||
}
|
||||
)
|
||||
ctx.send(CreateReply {
|
||||
embeds: vec![embed.description("Reloading configuration...")],
|
||||
ephemeral: Some(true),
|
||||
..Default::default()
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
@ -38,19 +30,13 @@ pub async fn reload(ctx: Context<'_>) -> Result<(), Error> {
|
||||
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;
|
||||
let configuration = &ctx.data().read().await.configuration;
|
||||
|
||||
ctx.send(
|
||||
CreateReply {
|
||||
ephemeral: Some(true),
|
||||
embeds: vec![
|
||||
CreateEmbed::new()
|
||||
.description("Stopping the bot...")
|
||||
.color(color),
|
||||
],
|
||||
..Default::default()
|
||||
}
|
||||
)
|
||||
ctx.send(CreateReply {
|
||||
ephemeral: Some(true),
|
||||
embeds: vec![create_default_embed(configuration).description("Stopping the bot...")],
|
||||
..Default::default()
|
||||
})
|
||||
.await?;
|
||||
|
||||
ctx.framework().shard_manager().shutdown_all().await;
|
||||
|
@ -2,7 +2,7 @@ use bson::{doc, Document};
|
||||
use chrono::Utc;
|
||||
use mongodb::options::{UpdateModifications, UpdateOptions};
|
||||
use poise::serenity_prelude::{
|
||||
self as serenity, CreateEmbed, CreateEmbedFooter, EditMessage, GetMessages, Mentionable, UserId,
|
||||
CreateEmbedFooter, EditMessage, GetMessages, Mentionable, UserId,
|
||||
};
|
||||
use poise::CreateReply;
|
||||
use tracing::{debug, trace};
|
||||
@ -13,7 +13,7 @@ use crate::utils::macros::to_user;
|
||||
use crate::utils::moderation::{
|
||||
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};
|
||||
|
||||
/// Unmute a member.
|
||||
@ -41,7 +41,7 @@ pub async fn unmute(
|
||||
data.database.clone(),
|
||||
ctx.guild_id().unwrap(),
|
||||
id,
|
||||
configuration.general.mute.role,
|
||||
configuration.mute.role,
|
||||
0,
|
||||
)
|
||||
.await
|
||||
@ -73,7 +73,7 @@ pub async fn mute(
|
||||
let configuration = &data.configuration;
|
||||
let author = ctx.author();
|
||||
|
||||
let mute = &configuration.general.mute;
|
||||
let mute = &configuration.mute;
|
||||
let guild_id = ctx.guild_id().unwrap();
|
||||
|
||||
let discord = ctx.serenity_context();
|
||||
@ -185,7 +185,6 @@ pub async fn purge(
|
||||
|
||||
let data = ctx.data().read().await;
|
||||
let configuration = &data.configuration;
|
||||
let embed_color = configuration.general.embed_color;
|
||||
let channel = ctx.channel_id();
|
||||
let too_old_timestamp = Utc::now().timestamp() - MAX_BULK_DELETE_AGO_SECS;
|
||||
|
||||
@ -196,10 +195,9 @@ pub async fn purge(
|
||||
|
||||
let handle = ctx
|
||||
.send(CreateReply {
|
||||
embeds: vec![CreateEmbed::new()
|
||||
embeds: vec![create_default_embed(configuration)
|
||||
.title("Purging messages")
|
||||
.description("Accumulating...")
|
||||
.color(embed_color)
|
||||
.thumbnail(&image)],
|
||||
..Default::default()
|
||||
})
|
||||
@ -271,14 +269,12 @@ pub async fn purge(
|
||||
.edit(
|
||||
&ctx.serenity_context(),
|
||||
EditMessage::new().embed(
|
||||
serenity::CreateEmbed::default()
|
||||
create_default_embed(configuration)
|
||||
.title("Purge successful")
|
||||
.field("Deleted messages", deleted_amount.to_string(), false)
|
||||
.field("Action by", author.mention().to_string(), false)
|
||||
.color(embed_color)
|
||||
.thumbnail(&image)
|
||||
.footer(CreateEmbedFooter::new("ReVanced").icon_url(image))
|
||||
.clone(),
|
||||
.footer(CreateEmbedFooter::new("ReVanced").icon_url(image)),
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
|
@ -6,7 +6,7 @@ use crate::utils::moderation::queue_unmute_member;
|
||||
|
||||
pub async fn load_muted_members(ctx: &serenity::Context, data: &Arc<RwLock<Data>>) {
|
||||
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
|
||||
.database
|
||||
|
@ -6,11 +6,12 @@ use commands::{configuration, misc, moderation};
|
||||
use db::database::Database;
|
||||
use events::event_handler;
|
||||
use poise::serenity_prelude::prelude::{RwLock, TypeMapKey};
|
||||
use poise::serenity_prelude::{CreateEmbed, UserId};
|
||||
use poise::serenity_prelude::{UserId};
|
||||
use poise::CreateReply;
|
||||
use tokio::task::JoinHandle;
|
||||
use tracing::{error, trace};
|
||||
use utils::bot::load_configuration;
|
||||
use utils::create_default_embed;
|
||||
|
||||
use crate::model::application::Configuration;
|
||||
|
||||
@ -125,12 +126,11 @@ async fn main() {
|
||||
if let Err(e) = ctx
|
||||
.send(CreateReply {
|
||||
ephemeral: Some(true),
|
||||
embeds: vec![CreateEmbed::new()
|
||||
embeds: vec![create_default_embed(configuration)
|
||||
.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(),
|
||||
))],
|
||||
|
@ -9,9 +9,11 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Default, Serialize, Deserialize)]
|
||||
pub struct Configuration {
|
||||
pub general: General,
|
||||
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";
|
||||
@ -39,15 +41,15 @@ impl Configuration {
|
||||
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() {
|
||||
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() {
|
||||
File::open(sys_config)?
|
||||
}
|
||||
// create defalt config
|
||||
// Create a default config file.
|
||||
else {
|
||||
let default_config = Configuration::default();
|
||||
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)]
|
||||
pub struct Mute {
|
||||
pub role: u64,
|
||||
@ -81,89 +76,57 @@ pub struct Administrators {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct MessageResponse {
|
||||
pub includes: Option<Includes>,
|
||||
pub excludes: Option<Excludes>,
|
||||
pub condition: Option<Condition>,
|
||||
pub response: Response,
|
||||
pub thread_options: Option<ThreadOptions>,
|
||||
pub struct Response {
|
||||
pub whitelist: Option<Trigger>,
|
||||
pub blacklist: Option<Trigger>,
|
||||
pub message: Message,
|
||||
pub respond_to_reference: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ThreadOptions {
|
||||
pub close_on_response: bool,
|
||||
pub lock_on_response: bool,
|
||||
pub only_on_first_message: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Response {
|
||||
pub message: Option<String>,
|
||||
pub struct Message {
|
||||
pub content: Option<String>,
|
||||
pub embed: Option<Embed>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Default)]
|
||||
pub struct Embed {
|
||||
pub title: String,
|
||||
pub description: String,
|
||||
pub color: i32,
|
||||
pub fields: Vec<Field>,
|
||||
pub footer: Footer,
|
||||
pub image: Image,
|
||||
pub thumbnail: Thumbnail,
|
||||
pub author: Author,
|
||||
pub author: Option<Author>,
|
||||
pub color: Option<i32>,
|
||||
pub title: Option<String>,
|
||||
pub description: Option<String>,
|
||||
pub fields: Option<Vec<Field>>,
|
||||
pub footer: Option<Footer>,
|
||||
pub image_url: Option<String>,
|
||||
pub thumbnail_url: Option<String>,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Field {
|
||||
pub name: String,
|
||||
pub value: String,
|
||||
pub inline: bool,
|
||||
pub inline: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Footer {
|
||||
pub text: String,
|
||||
pub icon_url: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Image {
|
||||
pub url: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Thumbnail {
|
||||
pub url: String,
|
||||
pub icon_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Author {
|
||||
pub name: String,
|
||||
pub icon_url: String,
|
||||
pub url: String,
|
||||
pub icon_url: Option<String>,
|
||||
pub url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Includes {
|
||||
pub struct Trigger {
|
||||
pub channels: Option<HashSet<u64>>,
|
||||
pub roles: Option<HashSet<u64>>,
|
||||
#[serde(rename = "match", with = "serde_regex")]
|
||||
pub match_field: 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,
|
||||
#[serde(with = "serde_regex")]
|
||||
pub regex: Vec<Regex>,
|
||||
}
|
||||
|
@ -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()),
|
||||
"",
|
||||
);
|
||||
|
@ -1,18 +1,37 @@
|
||||
use poise::serenity_prelude::CreateMessage;
|
||||
|
||||
|
||||
use chrono::{DateTime, Duration, NaiveDateTime, Utc};
|
||||
use poise::serenity_prelude::{
|
||||
CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter, CreateMessage, EditThread, GetMessages,
|
||||
};
|
||||
use regex::Regex;
|
||||
|
||||
use serenity::Message;
|
||||
use tracing::log::error;
|
||||
|
||||
use super::*;
|
||||
use crate::{BotData};
|
||||
use crate::{
|
||||
model::application::Trigger,
|
||||
BotData,
|
||||
};
|
||||
|
||||
pub fn contains_match(regex: &[Regex], text: &str) -> bool {
|
||||
regex.iter().any(|r| r.is_match(text))
|
||||
impl Trigger {
|
||||
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(
|
||||
@ -24,191 +43,53 @@ pub async fn handle_message_response(
|
||||
return;
|
||||
}
|
||||
|
||||
let responses = &data.read().await.configuration.message_responses;
|
||||
let message = &new_message.content;
|
||||
let configuration = &data.read().await.configuration;
|
||||
|
||||
let mut guild_message = None;
|
||||
let member_roles = &new_message.member.as_ref().unwrap().roles;
|
||||
|
||||
let member = &new_message.member.as_ref().unwrap();
|
||||
let member_roles = &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");
|
||||
for response in &configuration.responses {
|
||||
if let Some(whitelist) = &response.whitelist {
|
||||
if whitelist.matches(new_message, member_roles) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(excludes) = &response.excludes {
|
||||
// check if the role is blacklisted
|
||||
if let Some(roles) = &excludes.roles {
|
||||
if roles.iter().any(|&role_id| {
|
||||
member_roles
|
||||
.iter()
|
||||
.any(|&member_role| role_id == member_role.get())
|
||||
}) {
|
||||
continue;
|
||||
}
|
||||
if let Some(blacklist) = &response.blacklist {
|
||||
if blacklist.matches(new_message, member_roles) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(condition) = &response.condition {
|
||||
let min_age = condition.user.server_age;
|
||||
|
||||
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
|
||||
if let Err(err) = new_message
|
||||
.channel_id
|
||||
.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) => 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()),
|
||||
message = message.reference_message(
|
||||
if let Some(reference) = &new_message.referenced_message {
|
||||
reference.as_ref()
|
||||
} else {
|
||||
new_message
|
||||
},
|
||||
);
|
||||
|
||||
if let Some(embed_configuration) = &response.message.embed {
|
||||
message = message.embed(create_embed(configuration, embed_configuration))
|
||||
}
|
||||
|
||||
if let Some(content) = &response.message.content {
|
||||
message = message.content(content)
|
||||
}
|
||||
|
||||
message
|
||||
})
|
||||
.await
|
||||
{
|
||||
error!(
|
||||
"Failed to reply to the message from {}. Error: {:?}",
|
||||
"Failed to reply to {}. Error: {:?}",
|
||||
new_message.author.tag(),
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,9 @@
|
||||
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 code_embed;
|
||||
@ -12,3 +16,94 @@ pub fn parse_duration(duration: String) -> Result<Duration, parse_duration::pars
|
||||
let d = parse_duration::parse(&duration)?;
|
||||
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)
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use std::sync::{Arc, Mutex};
|
||||
|
||||
use mongodb::options::FindOptions;
|
||||
use poise::serenity_prelude::{
|
||||
ChannelId, CreateEmbed, CreateEmbedFooter, CreateMessage, GuildId, Mentionable, User, UserId,
|
||||
ChannelId, CreateEmbedFooter, CreateMessage, GuildId, Mentionable, User, UserId,
|
||||
};
|
||||
use poise::CreateReply;
|
||||
use serenity::prelude::SerenityError;
|
||||
@ -48,7 +48,7 @@ pub async fn mute_on_join(ctx: &serenity::Context, new_member: &serenity::Member
|
||||
if found {
|
||||
debug!("Muted member {} rejoined the server", new_member.user.tag());
|
||||
if new_member
|
||||
.add_role(&ctx.http, data.configuration.general.mute.role)
|
||||
.add_role(&ctx.http, data.configuration.mute.role)
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
@ -126,15 +126,16 @@ pub async fn respond_moderation<'a>(
|
||||
let send_ephemeral = Arc::new(Mutex::new(false));
|
||||
|
||||
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) => {
|
||||
let embed = match error {
|
||||
Some(err) => {
|
||||
*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(
|
||||
"Action",
|
||||
@ -146,7 +147,7 @@ pub async fn respond_moderation<'a>(
|
||||
false,
|
||||
)
|
||||
},
|
||||
None => f.title(format!("Muted {}", user.tag())).field(
|
||||
None => create_embed.title(format!("Muted {}", user.tag())).field(
|
||||
"Action",
|
||||
format!("{} was muted by {}", user.mention(), author.mention()),
|
||||
false,
|
||||
@ -165,7 +166,8 @@ pub async fn respond_moderation<'a>(
|
||||
Some(err) => {
|
||||
*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(
|
||||
"Action",
|
||||
@ -177,7 +179,7 @@ pub async fn respond_moderation<'a>(
|
||||
false,
|
||||
)
|
||||
},
|
||||
None => f.title(format!("Unmuted {}", user.tag())).field(
|
||||
None => create_embed.title(format!("Unmuted {}", user.tag())).field(
|
||||
"Action",
|
||||
format!("{} was unmuted by {}", user.mention(), author.mention()),
|
||||
false,
|
||||
@ -188,7 +190,8 @@ pub async fn respond_moderation<'a>(
|
||||
Some(err) => {
|
||||
*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(
|
||||
"Action",
|
||||
@ -200,7 +203,7 @@ pub async fn respond_moderation<'a>(
|
||||
false,
|
||||
)
|
||||
},
|
||||
None => f.title(format!("Banned {}", user.tag())).field(
|
||||
None => create_embed.title(format!("Banned {}", user.tag())).field(
|
||||
"Action",
|
||||
format!("{} was banned by {}", user.mention(), author.mention()),
|
||||
false,
|
||||
@ -216,7 +219,8 @@ pub async fn respond_moderation<'a>(
|
||||
Some(err) => {
|
||||
*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(
|
||||
"Action",
|
||||
@ -228,18 +232,19 @@ pub async fn respond_moderation<'a>(
|
||||
false,
|
||||
)
|
||||
},
|
||||
None => f.title(format!("Unbanned {}", user.tag())).field(
|
||||
"Action",
|
||||
format!("{} was unbanned by {}", user.mention(), author.mention()),
|
||||
false,
|
||||
),
|
||||
None => create_embed
|
||||
.title(format!("Unbanned {}", user.tag()))
|
||||
.field(
|
||||
"Action",
|
||||
format!("{} was unbanned by {}", user.mention(), author.mention()),
|
||||
false,
|
||||
),
|
||||
},
|
||||
}
|
||||
.color(configuration.general.embed_color);
|
||||
};
|
||||
|
||||
let user = current_user.face();
|
||||
|
||||
result
|
||||
create_embed
|
||||
.thumbnail(&user)
|
||||
.footer(CreateEmbedFooter::new("ReVanced").icon_url(&user))
|
||||
};
|
||||
@ -256,7 +261,7 @@ pub async fn respond_moderation<'a>(
|
||||
|
||||
let response = reply.message().await?;
|
||||
|
||||
ChannelId::from(configuration.general.logging_channel)
|
||||
ChannelId::from(configuration.log_channel)
|
||||
.send_message(
|
||||
&ctx.serenity_context().http,
|
||||
CreateMessage::new().embed(create_embed().field(
|
||||
|
Loading…
x
Reference in New Issue
Block a user