mirror of
https://github.com/revanced/revanced-discord-bot.git
synced 2025-04-30 06:24:27 +02:00
build: Bump Serenity
This commit is contained in:
parent
21dd21c377
commit
a8e9a9bb49
899
Cargo.lock
generated
899
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
16
Cargo.toml
16
Cargo.toml
@ -5,7 +5,7 @@ homepage = "https://revanced.app"
|
|||||||
license = "GPL-3.0"
|
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"
|
||||||
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
@ -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?;
|
||||||
|
|
||||||
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
25
src/events/presence_update.rs
Normal file
25
src/events/presence_update.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
use super::*;
|
||||||
|
use crate::model::application::Configuration;
|
||||||
|
use crate::utils::bot::get_data_lock;
|
||||||
|
use crate::utils::decancer::cure;
|
||||||
|
|
||||||
|
pub async fn presence_update(ctx: &serenity::Context, new_data: &Presence) {
|
||||||
|
let data = get_data_lock(ctx).await;
|
||||||
|
let configuration: &Configuration = &data.read().await.configuration;
|
||||||
|
|
||||||
|
if !configuration.general.cure_on_presence_update {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cure(
|
||||||
|
ctx,
|
||||||
|
&None,
|
||||||
|
&new_data
|
||||||
|
.guild_id
|
||||||
|
.unwrap()
|
||||||
|
.member(&ctx.http, new_data.user.id)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
@ -25,7 +25,9 @@ pub async fn load_muted_members(ctx: &serenity::Context, _: &serenity::Ready) {
|
|||||||
|
|
||||||
while cursor.advance().await.unwrap() {
|
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
|
||||||
),
|
),
|
||||||
|
40
src/main.rs
40
src/main.rs
@ -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
|
||||||
|
@ -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)]
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()),
|
||||||
|
@ -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
|
||||||
|
@ -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!(
|
||||||
|
@ -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.
|
||||||
|
@ -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(())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user