build: Bump Serenity

This commit is contained in:
oSumAtrIX 2023-10-26 17:46:43 +02:00
parent 21dd21c377
commit a8e9a9bb49
No known key found for this signature in database
GPG Key ID: A9B3094ACDB604B4
19 changed files with 929 additions and 936 deletions

899
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@ 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.5.1" version = "2.5.2"
edition = "2021" edition = "2021"
[profile.release] [profile.release]
@ -17,9 +17,9 @@ panic = "abort"
[dependencies] [dependencies]
bson = "2.4" bson = "2.4"
serde_with_macros = "2.0.1" serde_with_macros = "3.4.0"
mongodb = "2.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" decancer = "1.5.4"
tokio = { version = "1.26.0", features = ["rt-multi-thread"] } tokio = { version = "1.26.0", features = ["rt-multi-thread"] }
dotenv = "0.15.0" dotenv = "0.15.0"
@ -27,10 +27,16 @@ serde = { version = "1.0.158", features = ["derive"] }
serde_json = "1.0.94" serde_json = "1.0.94"
regex = "1.7.3" regex = "1.7.3"
serde_regex = "1.1.0" 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" chrono = "0.4.24"
dirs = "5.0.0" 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" tracing-subscriber = "0.3.16"
hmac-sha256 = "1.1.6" hmac-sha256 = "1.1.6"
base64 = "0.21.0" base64 = "0.21.0"

View File

@ -1,3 +1,5 @@
use poise::serenity_prelude::CreateEmbed;
use poise::CreateReply;
use tracing::debug; use tracing::debug;
use crate::utils::bot::load_configuration; 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); debug!("{} reloaded the configuration.", ctx.author().name);
ctx.send(|f| { ctx.send(
f.ephemeral(true).embed(|f| { CreateReply::new().ephemeral(true).embed(
f.description("Successfully reloaded configuration.") CreateEmbed::new()
.color(embed_color) .description("Successfully reloaded configuration.")
}) .color(embed_color),
}) ),
)
.await?; .await?;
Ok(()) Ok(())
@ -32,18 +35,17 @@ 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 color = ctx.data().read().await.configuration.general.embed_color;
ctx.send(|f| {
f.ephemeral(true) ctx.send(
.embed(|f| f.description("Stopped the bot.").color(color)) CreateReply::new().ephemeral(true).embed(
}) CreateEmbed::new()
.description("Stopped the bot.")
.color(color),
),
)
.await?; .await?;
ctx.framework() ctx.framework().shard_manager().shutdown_all().await;
.shard_manager()
.lock()
.await
.shutdown_all()
.await;
Ok(()) Ok(())
} }

View File

@ -1,5 +1,5 @@
use poise::serenity_prelude::{self as serenity, MessageId, ParseValue, ReactionType}; use poise::serenity_prelude::{CreateActionRow, CreateAllowedMentions, CreateButton, ReactionType};
use poise::ReplyHandle; use poise::{CreateReply, ReplyHandle};
use crate::utils::message::clone_message; use crate::utils::message::clone_message;
use crate::{Context, Error}; use crate::{Context, Error};
@ -14,8 +14,9 @@ pub async fn reply(
async fn send_ephermal<'a>( async fn send_ephermal<'a>(
ctx: &Context<'a>, ctx: &Context<'a>,
content: &str, content: &str,
) -> Result<ReplyHandle<'a>, serenity::Error> { ) -> Result<ReplyHandle<'a>, poise::serenity_prelude::Error> {
ctx.send(|f| f.ephemeral(true).content(content)).await ctx.send(CreateReply::new().ephemeral(true).content(content))
.await
} }
let http = &ctx.serenity_context().http; let http = &ctx.serenity_context().http;
@ -23,7 +24,7 @@ pub async fn reply(
if let Some(reply_message) = reply_message { if let Some(reply_message) = reply_message {
if let Ok(reply_message) = reply_message.parse::<u64>() { 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) => { Ok(reply_message) => {
reply_message.reply(http, &message).await?; reply_message.reply(http, &message).await?;
}, },
@ -71,29 +72,24 @@ pub async fn poll(
let message = ctx let message = ctx
.serenity_context() .serenity_context()
.http .http
.get_message(channel_id, message_id) .get_message(channel_id.into(), message_id.into())
.await?; .await?;
ctx.send(|m| { let message = clone_message(&message).components(vec![CreateActionRow::Buttons(vec![
let mut clone = clone_message(&message, m).components(|c| { CreateButton::new(format!("poll:{id}:{age}"))
c.create_action_row(|r| { .label("Vote")
r.create_button(|b| { .emoji(ReactionType::Unicode("🗳️".to_string())),
b.label("Vote") ])]);
.emoji(ReactionType::Unicode("🗳️".to_string()))
.custom_id(format!("poll:{id}:{age}"))
})
})
});
if ping { ctx.send(if ping {
clone = clone.allowed_mentions(|am| { message.allowed_mentions(
am.parse(ParseValue::Users) CreateAllowedMentions::default()
.parse(ParseValue::Roles) .all_roles(true)
.parse(ParseValue::Everyone) .all_users(true)
}); .everyone(true),
} )
} else {
clone message
}) })
.await?; .await?;

View File

@ -1,16 +1,22 @@
use bson::{doc, Document}; 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, self as serenity,
CreateEmbed,
CreateEmbedFooter,
EditMessage,
GetMessages,
Mentionable, Mentionable,
PermissionOverwrite,
Permissions,
UserId, 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::bot::get_member;
use crate::utils::macros::to_user; use crate::utils::macros::to_user;
use crate::utils::moderation::{ use crate::utils::moderation::{
@ -23,145 +29,6 @@ use crate::utils::moderation::{
use crate::utils::parse_duration; use crate::utils::parse_duration;
use crate::{Context, Error}; 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. /// Unmute a member.
#[poise::command(slash_command)] #[poise::command(slash_command)]
pub async fn unmute( pub async fn unmute(
@ -175,8 +42,8 @@ pub async fn unmute(
let data = &ctx.data().read().await; let data = &ctx.data().read().await;
let configuration = &data.configuration; let configuration = &data.configuration;
if let Some(pending_unmute) = data.pending_unmutes.get(&id.0) { if let Some(pending_unmute) = data.pending_unmutes.get(&id.get()) {
trace!("Cancelling pending unmute for {}", id.0); trace!("Cancelling pending unmute for {}", id);
pending_unmute.abort(); pending_unmute.abort();
} }
@ -231,7 +98,7 @@ pub async fn mute(
}; };
let mut updated = Muted { let mut updated = Muted {
guild_id: Some(guild_id.0.to_string()), guild_id: Some(guild_id.to_string()),
expires: unmute_time, expires: unmute_time,
reason: Some(reason.clone()), reason: Some(reason.clone()),
..Default::default() ..Default::default()
@ -265,8 +132,8 @@ pub async fn mute(
) )
.await?; .await?;
if let Some(pending_unmute) = data.pending_unmutes.get(&id.0) { if let Some(pending_unmute) = data.pending_unmutes.get(&id.get()) {
trace!("Cancelling pending unmute for {}", id.0); trace!("Cancelling pending unmute for {}", id);
pending_unmute.abort(); pending_unmute.abort();
} }
@ -281,7 +148,7 @@ pub async fn mute(
.await?; .await?;
} else { } else {
data.pending_unmutes.insert( data.pending_unmutes.insert(
id.0, id.get(),
queue_unmute_member( queue_unmute_member(
discord.clone(), discord.clone(),
data.database.clone(), data.database.clone(),
@ -341,14 +208,15 @@ pub async fn purge(
let author = ctx.author(); let author = ctx.author();
let handle = ctx let handle = ctx
.send(|f| { .send(
f.embed(|f| { CreateReply::new().embed(
f.title("Purging messages") CreateEmbed::new()
.title("Purging messages")
.description("Accumulating...") .description("Accumulating...")
.color(embed_color) .color(embed_color)
.thumbnail(&image) .thumbnail(&image),
}) ),
}) )
.await?; .await?;
let mut response = handle.message().await?; let mut response = handle.message().await?;
@ -361,9 +229,12 @@ pub async fn purge(
loop { loop {
// Filter out messages that are too old // Filter out messages that are too old
let mut messages = channel let mut messages = channel
.messages(&ctx.serenity_context(), |m| { .messages(
m.limit(count_to_delete as u64).before(response.id) &ctx.serenity_context(),
}) GetMessages::new()
.limit(count_to_delete as u8)
.before(response.id),
)
.await? .await?
.into_iter() .into_iter()
.take_while(|m| m.timestamp.timestamp() > too_old_timestamp) .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>() { if let Ok(message_id) = message_id.parse::<u64>() {
messages = messages messages = messages
.into_iter() .into_iter()
.take_while(|m| m.id.0 > message_id) .take_while(|m| m.id.get() > message_id)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
debug!( debug!(
"Filtered messages until {}. Left: {}", "Filtered messages until {}. Left: {}",
@ -411,21 +282,19 @@ pub async fn purge(
response response
.to_mut() .to_mut()
.edit(&ctx.serenity_context(), |e| { .edit(
e.set_embed( &ctx.serenity_context(),
EditMessage::new().embed(
serenity::CreateEmbed::default() serenity::CreateEmbed::default()
.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(), false) .field("Action by", author.mention().to_string(), false)
.color(embed_color) .color(embed_color)
.thumbnail(&image) .thumbnail(&image)
.footer(|f| { .footer(CreateEmbedFooter::new("ReVanced").icon_url(image))
f.text("ReVanced");
f.icon_url(image)
})
.clone(), .clone(),
) ),
}) )
.await?; .await?;
Ok(()) Ok(())
} }

View File

@ -3,8 +3,11 @@ use crate::utils::decancer::cure;
pub async fn guild_member_update( pub async fn guild_member_update(
ctx: &serenity::Context, ctx: &serenity::Context,
old_if_available: &Option<serenity::Member>, old_if_available: &Option<Member>,
new: &serenity::Member, new: &Option<Member>,
_: &GuildMemberUpdateEvent,
) { ) {
cure(ctx, old_if_available, new).await; if let Some(member) = new {
cure(ctx, old_if_available, member).await;
}
} }

View File

@ -1,8 +1,8 @@
use chrono::{Duration, Utc}; use chrono::{Duration, Utc};
use poise::serenity_prelude::{ use poise::serenity_prelude::{
ComponentType, ComponentInteraction,
MessageComponentInteraction, ComponentInteractionData,
MessageComponentInteractionData, ComponentInteractionDataKind,
}; };
use super::*; use super::*;
@ -10,11 +10,11 @@ use crate::utils;
pub async fn interaction_create( pub async fn interaction_create(
ctx: &serenity::Context, ctx: &serenity::Context,
interaction: &serenity::Interaction, interaction: &serenity::Interaction,
) -> Result<(), crate::serenity::SerenityError> { ) -> Result<(), serenity::prelude::SerenityError> {
if let serenity::Interaction::MessageComponent(MessageComponentInteraction { if let serenity::Interaction::Component(ComponentInteraction {
data: data:
MessageComponentInteractionData { ComponentInteractionData {
component_type: ComponentType::Button, kind: ComponentInteractionDataKind::Button,
custom_id, custom_id,
.. ..
}, },
@ -33,7 +33,7 @@ pub async fn handle_poll(
ctx: &serenity::Context, ctx: &serenity::Context,
interaction: &serenity::Interaction, interaction: &serenity::Interaction,
custom_id: &str, custom_id: &str,
) -> Result<(), crate::serenity::SerenityError> { ) -> Result<(), serenity::prelude::SerenityError> {
fn parse<T>(str: &str) -> T fn parse<T>(str: &str) -> T
where where
<T as std::str::FromStr>::Err: std::fmt::Debug, <T as std::str::FromStr>::Err: std::fmt::Debug,

View File

@ -1,6 +1,14 @@
use std::sync::Arc; 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 tracing::log::error;
use crate::{Data, Error}; use crate::{Data, Error};
@ -15,7 +23,7 @@ pub struct Handler<T> {
options: poise::FrameworkOptions<T, Error>, options: poise::FrameworkOptions<T, Error>,
data: T, data: T,
bot_id: RwLock<Option<UserId>>, bot_id: RwLock<Option<UserId>>,
shard_manager: RwLock<Option<Arc<Mutex<ShardManager>>>>, shard_manager: RwLock<Option<Arc<ShardManager>>>,
} }
// Custom handler to dispatch poise events // 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); *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 { let framework_data = poise::FrameworkContext {
bot_id: self.bot_id.read().await.unwrap(), bot_id: self.bot_id.read().await.unwrap(),
options: &self.options, options: &self.options,
user_data: &self.data, user_data: &self.data,
shard_manager: &(*self.shard_manager.read().await).clone().unwrap(), /* Shard manager can be read between all poise events without locks */ 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( async fn guild_member_update(
&self, &self,
ctx: serenity::Context, ctx: serenity::Context,
old_if_available: Option<serenity::Member>, old_if_available: Option<Member>,
new: serenity::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) { async fn message(&self, ctx: serenity::Context, new_message: serenity::Message) {
message_create::message_create(&ctx, &new_message).await; 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, new_message,
}) })
.await; .await;
@ -80,7 +90,8 @@ impl serenity::EventHandler for Handler<Arc<RwLock<Data>>> {
new: Option<serenity::Message>, new: Option<serenity::Message>,
event: serenity::MessageUpdateEvent, event: serenity::MessageUpdateEvent,
) { ) {
self.dispatch_poise_event(&ctx, &poise::Event::MessageUpdate { self.dispatch_poise_event(&serenity::FullEvent::MessageUpdate {
ctx,
old_if_available, old_if_available,
new, new,
event, event,
@ -99,7 +110,8 @@ impl serenity::EventHandler for Handler<Arc<RwLock<Data>>> {
error!("Failed to handle interaction: {:?}.", e); error!("Failed to handle interaction: {:?}.", e);
} }
self.dispatch_poise_event(&ctx, &poise::Event::InteractionCreate { self.dispatch_poise_event(&serenity::FullEvent::InteractionCreate {
ctx,
interaction, interaction,
}) })
.await; .await;

View 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;
}

View File

@ -25,7 +25,9 @@ pub async fn load_muted_members(ctx: &serenity::Context, _: &serenity::Ready) {
while cursor.advance().await.unwrap() { while cursor.advance().await.unwrap() {
let current: Muted = cursor.deserialize_current().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 guild_id = current.guild_id.unwrap().parse::<u64>().unwrap();
let user_id = current.user_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( queue_unmute_member(
ctx.clone(), ctx.clone(),
data.database.clone(), data.database.clone(),
serenity::GuildId(guild_id), guild_id.into(),
serenity::UserId(user_id), user_id.into(),
mute_role_id, mute_role_id,
amount_left as u64, // i64 as u64 is handled properly here amount_left as u64, // i64 as u64 is handled properly here
), ),

View File

@ -6,7 +6,9 @@ use api::client::Api;
use commands::{configuration, misc, moderation}; use commands::{configuration, misc, moderation};
use db::database::Database; use db::database::Database;
use events::Handler; 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 tokio::task::JoinHandle;
use tracing::{error, trace}; use tracing::{error, trace};
use utils::bot::load_configuration; use utils::bot::load_configuration;
@ -24,7 +26,7 @@ mod utils;
type Error = Box<dyn std::error::Error + Send + Sync>; type Error = Box<dyn std::error::Error + Send + Sync>;
type Context<'a> = poise::Context<'a, Arc<RwLock<Data>>, Error>; type Context<'a> = poise::Context<'a, Arc<RwLock<Data>>, Error>;
impl serenity::TypeMapKey for Data { impl TypeMapKey for Data {
type Value = Arc<RwLock<Data>>; type Value = Arc<RwLock<Data>>;
} }
@ -54,8 +56,6 @@ async fn main() {
moderation::purge(), moderation::purge(),
moderation::ban(), moderation::ban(),
moderation::unban(), moderation::unban(),
moderation::lock(),
moderation::unlock(),
misc::reply(), misc::reply(),
misc::poll(), misc::poll(),
]; ];
@ -68,7 +68,7 @@ async fn main() {
.users .users
.iter() .iter()
.cloned() .cloned()
.map(UserId) .map(UserId::from)
.collect::<Vec<UserId>>() .collect::<Vec<UserId>>()
.into_iter() .into_iter()
.collect(); .collect();
@ -116,7 +116,7 @@ async fn main() {
if !(administrators if !(administrators
.users .users
// Check if the user is an administrator // Check if the user is an administrator
.contains(&member.user.id.0) .contains(&member.user.id.get())
|| administrators || administrators
.roles .roles
.iter() .iter()
@ -125,22 +125,23 @@ async fn main() {
member member
.roles .roles
.iter() .iter()
.any(|member_role| member_role.0 == role_id) .any(|member_role| member_role.get() == role_id)
})) }))
{ {
if let Err(e) = ctx if let Err(e) = ctx
.send(|m| { .send(
m.ephemeral(true).embed(|e| { CreateReply::new().ephemeral(true).embed(
e.title("Permission error") CreateEmbed::new()
.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) .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(),
)) )),
}) ),
}) )
.await .await
{ {
error!("Error sending message: {:?}", e) error!("Error sending message: {:?}", e)
@ -152,9 +153,9 @@ async fn main() {
Ok(true) Ok(true)
}) })
}), }),
event_handler: |_ctx, event, _framework, _data| { event_handler: |event, _framework, _data| {
Box::pin(async move { Box::pin(async move {
tracing::trace!("{:?}", event.name()); tracing::trace!("{:?}", event.snake_case_name());
Ok(()) Ok(())
}) })
}, },
@ -163,12 +164,13 @@ async fn main() {
data.clone(), // Pass configuration as user data for the framework 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") env::var("DISCORD_AUTHORIZATION_TOKEN")
.expect("DISCORD_AUTHORIZATION_TOKEN environment variable not set"), .expect("DISCORD_AUTHORIZATION_TOKEN environment variable not set"),
serenity::GatewayIntents::non_privileged() poise::serenity_prelude::GatewayIntents::non_privileged()
| serenity::GatewayIntents::MESSAGE_CONTENT | poise::serenity_prelude::GatewayIntents::MESSAGE_CONTENT
| serenity::GatewayIntents::GUILD_MEMBERS, | poise::serenity_prelude::GatewayIntents::GUILD_MEMBERS
| poise::serenity_prelude::GatewayIntents::GUILD_PRESENCES,
) )
.event_handler_arc(handler.clone()) .event_handler_arc(handler.clone())
.await .await

View File

@ -66,6 +66,7 @@ pub struct General {
pub embed_color: i32, pub embed_color: i32,
pub mute: Mute, pub mute: Mute,
pub logging_channel: u64, pub logging_channel: u64,
pub cure_on_presence_update: bool,
} }
#[derive(Default, Serialize, Deserialize)] #[derive(Default, Serialize, Deserialize)]

View File

@ -1,6 +1,6 @@
use std::sync::Arc; 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::model::application::Configuration;
use crate::Data; use crate::Data;
@ -10,7 +10,9 @@ pub fn load_configuration() -> Configuration {
} }
// Share the lock reference between the threads in serenity framework // 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() ctx.data.read().await.get::<Data>().unwrap().clone()
} }
@ -21,7 +23,7 @@ pub async fn get_member(
) -> serenity::Result<Option<serenity::Member>> { ) -> serenity::Result<Option<serenity::Member>> {
match guild_id.member(ctx, user_id).await { match guild_id.member(ctx, user_id).await {
Ok(member) => Ok(Some(member)), Ok(member) => Ok(Some(member)),
Err(serenity::SerenityError::Http(err)) Err(serenity::prelude::SerenityError::Http(err))
if matches!( if matches!(
err.status_code(), err.status_code(),
Some(serenity::http::StatusCode::NOT_FOUND) Some(serenity::http::StatusCode::NOT_FOUND)

View File

@ -1,3 +1,4 @@
use poise::serenity_prelude::{CreateAttachment, CreateMessage, EditMessage};
use reqwest::Url; use reqwest::Url;
use tracing::{debug, error, trace}; 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 return; // Nothing to do
} }
if let Err(err) = new_message if let Err(err) = &new_message
.channel_id .channel_id
.send_message(&ctx.http, |message| { .send_message(
let mut message = message.reference_message(new_message); &ctx.http,
{
let mut message = CreateMessage::new();
for preview in code_previews.iter() { for preview in code_previews.iter() {
let language = match preview.code.language.as_ref() { let language = match preview.code.language.as_ref() {
Some(language) => language, Some(language) => language,
None => "txt", 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 .await
{ {
error!( error!(
@ -93,7 +97,11 @@ pub async fn code_preview(ctx: &serenity::Context, new_message: &serenity::Messa
return; 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); error!("Failed to remove embeds. Error: {:?}", err);
} }
} }

View File

@ -1,5 +1,6 @@
extern crate decancer; extern crate decancer;
use poise::serenity_prelude::EditMember;
use tracing::{error, info, trace}; use tracing::{error, info, trace};
use super::*; use super::*;
@ -17,7 +18,7 @@ pub async fn cure(
let name = member.display_name().to_string(); let name = member.display_name().to_string();
if let Some(old) = old_if_available { if let Some(old) = old_if_available {
if old.display_name().to_string() == name { if *old.display_name() == name {
trace!( trace!(
"Skipping decancer for {} because their name hasn't changed", "Skipping decancer for {} because their name hasn't changed",
member.user.tag() member.user.tag()
@ -41,9 +42,11 @@ pub async fn cure(
match member match member
.guild_id .guild_id
.edit_member(&ctx.http, member.user.id, |edit_member| { .edit_member(
edit_member.nickname(cured_name) &ctx.http,
}) member.user.id,
EditMember::default().nickname(cured_name),
)
.await .await
{ {
Ok(_) => info!("Cured user {}", member.user.tag()), Ok(_) => info!("Cured user {}", member.user.tag()),

View File

@ -1,54 +1,48 @@
use chrono::Utc; use chrono::Utc;
use poise::serenity_prelude::Message; use poise::serenity_prelude::{CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter, Message};
use poise::CreateReply; use poise::CreateReply;
pub fn clone_message<'a, 'b>( pub fn clone_message(message: &Message) -> CreateReply {
message: &'a Message, let mut reply = CreateReply::new().content(message.content.as_str());
to_reply: &'b mut CreateReply<'a>,
) -> &'b mut CreateReply<'a> {
let mut reply = to_reply.content(message.content.as_str());
if let Some(embed) = message.embeds.get(0) { if let Some(embed) = message.embeds.first() {
reply = reply.embed(|e| { let mut new_embed = CreateEmbed::new();
let mut new_embed = e;
if let Some(color) = embed.colour { if let Some(color) = embed.colour {
new_embed = new_embed.color(color); 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 { if let Some(title) = &embed.title {
new_embed = new_embed.title(title); new_embed = new_embed.title(title);
} }
if let Some(description) = &embed.description { if let Some(description) = &embed.description {
new_embed = new_embed.description(description); new_embed = new_embed.description(description);
} }
if let Some(footer) = &embed.footer { if let Some(footer) = &embed.footer {
new_embed = new_embed.footer(|f| f.text(&footer.text)); new_embed = new_embed.footer(CreateEmbedFooter::new(&footer.text));
} }
if let Some(author) = &embed.author { if let Some(author) = &embed.author {
new_embed = new_embed.author(|a| a.name(&author.name)); new_embed = new_embed.author(CreateEmbedAuthor::new(&author.name));
} }
if let Some(image) = &embed.image { if let Some(image) = &embed.image {
new_embed = new_embed.image(image.url.as_str()); new_embed = new_embed.image(image.url.as_str());
} }
if let Some(thumbnail) = &embed.thumbnail { if let Some(thumbnail) = &embed.thumbnail {
new_embed = new_embed.thumbnail(thumbnail.url.as_str()); new_embed = new_embed.thumbnail(thumbnail.url.as_str());
} }
for field in &embed.fields { for field in &embed.fields {
new_embed = new_embed = new_embed.field(field.name.as_str(), field.value.as_str(), field.inline);
new_embed.field(field.name.as_str(), field.value.as_str(), field.inline); }
}
new_embed reply = reply.embed(new_embed);
})
} }
reply reply

View File

@ -1,4 +1,12 @@
use chrono::{DateTime, Duration, NaiveDateTime, Utc}; use chrono::{DateTime, Duration, NaiveDateTime, Utc};
use poise::serenity_prelude::{
CreateEmbed,
CreateEmbedAuthor,
CreateEmbedFooter,
CreateMessage,
EditThread,
GetMessages,
};
use regex::Regex; use regex::Regex;
use tracing::log::error; 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 member_roles = &member.roles;
let joined_at = member.joined_at.unwrap().unix_timestamp(); 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(), NaiveDateTime::from_timestamp_opt(joined_at, 0).unwrap(),
Utc, Utc,
); );
@ -36,7 +44,7 @@ pub async fn handle_message_response(ctx: &serenity::Context, new_message: &sere
if !roles.iter().any(|&role_id| { if !roles.iter().any(|&role_id| {
member_roles member_roles
.iter() .iter()
.any(|&member_role| role_id == member_role.0) .any(|&member_role| role_id == member_role.get())
}) { }) {
continue; continue;
} }
@ -44,7 +52,7 @@ pub async fn handle_message_response(ctx: &serenity::Context, new_message: &sere
if let Some(channels) = &includes.channels { 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 // 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 response.thread_options.is_some() {
if guild_message.is_none() { if guild_message.is_none() {
guild_message = Some( 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; }; let Some(parent_id) = guild_message.as_ref().unwrap().parent_id else {
if !channels.contains(&parent_id.0) { continue;
};
if !channels.contains(&parent_id.get()) {
continue; continue;
} }
} else { } else {
@ -80,7 +90,7 @@ pub async fn handle_message_response(ctx: &serenity::Context, new_message: &sere
if roles.iter().any(|&role_id| { if roles.iter().any(|&role_id| {
member_roles member_roles
.iter() .iter()
.any(|&member_role| role_id == member_role.0) .any(|&member_role| role_id == member_role.get())
}) { }) {
continue; continue;
} }
@ -120,32 +130,40 @@ pub async fn handle_message_response(ctx: &serenity::Context, new_message: &sere
} }
if let Err(err) = channel_id if let Err(err) = channel_id
.send_message(&ctx.http, |m| { .send_message(
if let Some(reference) = message_reference { &ctx.http,
m.reference_message(reference); {
} else { let mut message = CreateMessage::default();
m.reference_message(new_message); message = if let Some(reference) = message_reference {
} message.reference_message(reference)
} else {
message.reference_message(new_message)
};
match &response.response.embed { match &response.response.embed {
Some(embed) => m.embed(|e| { Some(embed) => message.embed(
e.title(&embed.title) CreateEmbed::new()
.description(&embed.description) .title(&embed.title)
.color(embed.color) .description(&embed.description)
.fields(embed.fields.iter().map(|field| { .color(embed.color)
(field.name.clone(), field.value.clone(), field.inline) .fields(embed.fields.iter().map(|field| {
})) (field.name.clone(), field.value.clone(), field.inline)
.footer(|f| { }))
f.text(&embed.footer.text); .footer(
f.icon_url(&embed.footer.icon_url) CreateEmbedFooter::new(&embed.footer.text)
}) .icon_url(&embed.footer.icon_url),
.thumbnail(&embed.thumbnail.url) )
.image(&embed.image.url) .thumbnail(&embed.thumbnail.url)
.author(|a| a.name(&embed.author.name).icon_url(&embed.author.icon_url)) .image(&embed.image.url)
}), .author(
None => m.content(response.response.message.as_ref().unwrap()), CreateEmbedAuthor::new(&embed.author.name)
} .icon_url(&embed.author.icon_url),
}) ),
),
None => message.content(response.response.message.as_ref().unwrap()),
}
},
)
.await .await
{ {
error!( error!(
@ -154,7 +172,7 @@ pub async fn handle_message_response(ctx: &serenity::Context, new_message: &sere
err err
); );
} else if let Some(thread_options) = &response.thread_options { } else if let Some(thread_options) = &response.thread_options {
let channel = channel_id let mut channel = channel_id
.to_channel(&ctx.http) .to_channel(&ctx.http)
.await .await
.unwrap() .unwrap()
@ -170,7 +188,7 @@ pub async fn handle_message_response(ctx: &serenity::Context, new_message: &sere
if thread_options.only_on_first_message if thread_options.only_on_first_message
&& !channel_id && !channel_id
.messages(&ctx.http, |b| b.limit(1).before(new_message)) .messages(&ctx.http, GetMessages::new().limit(1).before(new_message))
.await .await
.unwrap() .unwrap()
.is_empty() .is_empty()
@ -179,10 +197,12 @@ pub async fn handle_message_response(ctx: &serenity::Context, new_message: &sere
} }
if let Err(err) = channel if let Err(err) = channel
.edit_thread(&ctx.http, |e| { .edit_thread(
e.locked(thread_options.lock_on_response) &ctx.http,
.archived(thread_options.close_on_response) EditThread::new()
}) .locked(thread_options.lock_on_response)
.archived(thread_options.close_on_response),
)
.await .await
{ {
error!( error!(

View File

@ -1,8 +1,19 @@
use std::cmp; use std::cmp;
use std::sync::{Arc, RwLock}; use std::sync::{Arc, Mutex};
use mongodb::options::FindOptions; 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 tokio::task::JoinHandle;
use tracing::{debug, error, warn}; use tracing::{debug, error, warn};
@ -11,7 +22,6 @@ use super::*;
use crate::db::database::Database; use crate::db::database::Database;
use crate::db::model::Muted; use crate::db::model::Muted;
use crate::model::application::{Configuration, Mute}; use crate::model::application::{Configuration, Mute};
use crate::serenity::SerenityError;
use crate::utils::bot::get_member; use crate::utils::bot::get_member;
use crate::{Context, Error}; use crate::{Context, Error};
@ -20,8 +30,6 @@ pub enum ModerationKind {
Unmute(User, User, Option<Error>), // User, Command author, Error Unmute(User, User, Option<Error>), // User, Command author, Error
Ban(User, User, Option<String>, Option<SerenityError>), // User, Command author, Reason, Error Ban(User, User, Option<String>, Option<SerenityError>), // User, Command author, Reason, Error
Unban(User, User, Option<SerenityError>), // User, Command author, 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 { pub enum BanKind {
Ban(User, Option<u8>, Option<String>), // User, Amount of days to delete messages, Reason 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 { 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, RoleId(data.configuration.general.mute.role)) .add_role(&ctx.http, data.configuration.general.mute.role)
.await .await
.is_ok() .is_ok()
{ {
@ -85,7 +93,7 @@ pub fn queue_unmute_member(
.find_and_delete::<Muted>( .find_and_delete::<Muted>(
"muted", "muted",
Muted { Muted {
user_id: Some(user_id.0.to_string()), user_id: Some(user_id.to_string()),
..Default::default() ..Default::default()
} }
.into(), .into(),
@ -123,18 +131,16 @@ pub async fn respond_moderation<'a>(
) -> Result<(), Error> { ) -> Result<(), Error> {
let current_user = ctx.serenity_context().http.get_current_user().await?; 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 create_embed = || {
let mut moderated_user: Option<&User> = None; let f = CreateEmbed::new();
let result = match moderation { let result = match moderation {
ModerationKind::Mute(user, author, reason, expires, error) => { ModerationKind::Mute(user, author, reason, expires, error) => {
moderated_user = Some(user);
let embed = match error { let embed = match error {
Some(err) => { Some(err) => {
*send_ephemeral.write().unwrap() = true; *send_ephemeral.lock().unwrap() = true;
f.title(format!("Failed to mute {}", user.tag())) f.title(format!("Failed to mute {}", user.tag()))
.field("Exception", err.to_string(), false) .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 // add expiration date to embed if mute has a duration
if let Some(expire) = expires { if let Some(expire) = expires {
embed.field("Expires", expire, true); embed.field("Expires", expire, true)
} else {
embed
} }
embed
}, },
ModerationKind::Unmute(user, author, error) => { ModerationKind::Unmute(user, author, error) => match error {
moderated_user = Some(user); Some(err) => {
match error { *send_ephemeral.lock().unwrap() = true;
Some(err) => {
*send_ephemeral.write().unwrap() = true;
f.title(format!("Failed to unmute {}", user.tag())) f.title(format!("Failed to unmute {}", user.tag()))
.field("Exception", err.to_string(), false) .field("Exception", err.to_string(), false)
.field( .field(
"Action", "Action",
format!( format!(
"{} was unmuted by {} but failed", "{} was unmuted by {} but failed",
user.mention(), user.mention(),
author.mention() author.mention()
), ),
false, false,
) )
}, },
None => f.title(format!("Unmuted {}", user.tag())).field( None => f.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,
), ),
}
}, },
ModerationKind::Ban(user, author, reason, error) => { ModerationKind::Ban(user, author, reason, error) => {
moderated_user = Some(user);
let f = match error { let f = match error {
Some(err) => { Some(err) => {
*send_ephemeral.write().unwrap() = true; *send_ephemeral.lock().unwrap() = true;
f.title(format!("Failed to ban {}", user.tag())) f.title(format!("Failed to ban {}", user.tag()))
.field("Exception", err.to_string(), false) .field("Exception", err.to_string(), false)
@ -217,164 +220,94 @@ pub async fn respond_moderation<'a>(
f f
} }
}, },
ModerationKind::Unban(user, author, error) => { ModerationKind::Unban(user, author, error) => match 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 {
Some(err) => { 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("Exception", err.to_string(), false)
.field( .field(
"Action", "Action",
format!( format!(
"{} was locked by {} but failed", "{} was unbanned by {} but failed",
channel.mention(), user.mention(),
author.mention() author.mention()
), ),
false, false,
) )
}, },
None => f None => f.title(format!("Unbanned {}", user.tag())).field(
.title(format!("Locked {}", channel.name())) "Action",
.description( format!("{} was unbanned by {}", user.mention(), author.mention()),
"Unlocking the channel will restore the original permission overwrites.", false,
) ),
.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,
),
}, },
} }
.color(configuration.general.embed_color); .color(configuration.general.embed_color);
let user = if let Some(user) = moderated_user { let user = current_user.face();
user.face()
} else {
current_user.face()
};
result.thumbnail(&user).footer(|f| { result
f.text("ReVanced"); .thumbnail(&user)
f.icon_url(current_user.face()) .footer(CreateEmbedFooter::new("ReVanced").icon_url(&user))
});
}; };
let send_ephemeral = *send_ephemeral.lock().unwrap();
let reply = ctx let reply = ctx
.send(|reply| { .send(
reply CreateReply::new()
.embed(|embed| { .embed(create_embed())
create_embed(embed); .ephemeral(send_ephemeral),
embed )
})
.ephemeral(*send_ephemeral.read().unwrap())
})
.await?; .await?;
let response = reply.message().await?; let response = reply.message().await?;
ChannelId(configuration.general.logging_channel)
.send_message(&ctx.serenity_context().http, |reply| { ChannelId::from(configuration.general.logging_channel)
reply.embed(|embed| { .send_message(
create_embed(embed); &ctx.serenity_context().http,
embed.field( CreateMessage::new().embed(create_embed().field(
"Reference", "Reference",
format!( format!(
"[Jump to message](https://discord.com/channels/{}/{}/{})", "[Jump to message](https://discord.com/channels/{}/{}/{})",
ctx.guild_id().unwrap().0, ctx.guild_id().unwrap(),
response.channel_id, response.channel_id,
response.id response.id
), ),
true, true,
) )),
}) )
})
.await?; .await?;
Ok(()) Ok(())
} }
pub async fn ban_moderation(ctx: &Context<'_>, kind: &BanKind) -> Option<SerenityError> { 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; let http = &ctx.serenity_context().http;
match kind { match kind {
BanKind::Ban(user, dmd, reason) => { BanKind::Ban(user, dmd, reason) => {
let reason = reason let reason = reason.as_deref().or(Some("None specified"));
.clone()
.or_else(|| Some("None specified".to_string()))
.unwrap();
let ban_result = http let ban_result = http
.ban_user( .ban_user(guild_id, user.id, cmp::min(dmd.unwrap_or(0), 7), reason)
guild_id,
user.id.0,
cmp::min(dmd.unwrap_or(0), 7),
reason.as_ref(),
)
.await; .await;
if let Err(err) = ban_result { 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) Some(err)
} else { } else {
None None
} }
}, },
BanKind::Unban(user) => { 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 { 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) Some(err)
} else { } else {
None None
@ -391,7 +324,7 @@ pub async fn mute_moderation(
let mute_role_id = config.role; let mute_role_id = config.role;
let take = &config.take; 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 member
.add_role(&ctx.serenity_context().http, mute_role_id) .add_role(&ctx.serenity_context().http, mute_role_id)
@ -401,7 +334,7 @@ pub async fn mute_moderation(
let removed_roles = member let removed_roles = member
.roles .roles
.iter() .iter()
.filter(|r| take.contains(&r.0)) .filter(|r| take.contains(&r.get()))
.copied() .copied()
.collect::<Vec<_>>(); .collect::<Vec<_>>();
// take them from the member. // take them from the member.

View File

@ -1,5 +1,13 @@
use base64::Engine; 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 reqwest::StatusCode;
use tracing::log::{error, trace}; use tracing::log::{error, trace};
@ -11,7 +19,7 @@ pub async fn handle_poll(
interaction: &serenity::Interaction, interaction: &serenity::Interaction,
poll_id: u64, poll_id: u64,
min_join_date: Timestamp, min_join_date: Timestamp,
) -> Result<(), crate::serenity::SerenityError> { ) -> Result<(), serenity::prelude::SerenityError> {
trace!("Handling poll: {}.", poll_id); trace!("Handling poll: {}.", poll_id);
let data = get_data_lock(ctx).await; let data = get_data_lock(ctx).await;
@ -52,40 +60,32 @@ pub async fn handle_poll(
.unwrap(); .unwrap();
component component
.create_interaction_response(&ctx.http, |r| { .create_response(
r.interaction_response_data(|m| { &ctx.http,
if let Ok(token) = result.as_deref() { serenity::CreateInteractionResponse::Message(if let Ok(token) = result.as_deref() {
let url = format!("https://revanced.app/poll#{token}"); let url = format!("https://revanced.app/poll#{token}");
m.components(|c| {
c.create_action_row(|r| { CreateInteractionResponseMessage::new().components(vec![CreateActionRow::Buttons(
r.create_button(|b| { vec![CreateButton::new_link(url)
b.label("Vote") .label("Vote")
.emoji(ReactionType::Unicode("🗳️".to_string())) .emoji(ReactionType::Unicode("🗳️".to_string()))],
.style(ButtonStyle::Link) )])
.url(&url) } else {
}) CreateInteractionResponseMessage::new()
}) .ephemeral(true)
}) .embed(
} else { match result {
m Ok(_) => CreateEmbed::new()
} .title("Cast your vote")
.ephemeral(true) .description("You can now vote on the poll."),
.embed(|e| { Err(msg) => CreateEmbed::new().title("Error").description(msg),
match result { }
Ok(_) => e .color(data.configuration.general.embed_color)
.title("Cast your vote") .thumbnail(&icon_url)
.description("You can now vote on the poll."), .footer(CreateEmbedFooter::new("ReVanced").icon_url(&icon_url)),
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)
})
})
})
})
.await?; .await?;
Ok(()) Ok(())