Add integrations + better mentions handling + big red button
This commit is contained in:
parent
d1edef3623
commit
644bd66ec3
|
@ -10,7 +10,7 @@ edition = "2018"
|
||||||
music = ["serenity/voice", "songbird"]
|
music = ["serenity/voice", "songbird"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serenity = "0.10"
|
serenity = { version = "0.10", features = ["unstable_discord_api"] }
|
||||||
toml = "0.5"
|
toml = "0.5"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
reqwest = "0.11"
|
reqwest = "0.11"
|
||||||
|
|
|
@ -23,7 +23,8 @@ use serenity::{
|
||||||
older,
|
older,
|
||||||
ping,
|
ping,
|
||||||
random_mute,
|
random_mute,
|
||||||
uptime
|
uptime,
|
||||||
|
button
|
||||||
)]
|
)]
|
||||||
pub struct General;
|
pub struct General;
|
||||||
|
|
||||||
|
@ -283,6 +284,24 @@ async fn _image(ctx: &Context, msg: &Message, mut args: Args) -> crate::commands
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
async fn button(ctx: &Context, msg: &Message) -> CommandResult {
|
||||||
|
msg.channel_id
|
||||||
|
.send_message(ctx, |msg| {
|
||||||
|
msg.content("An innofensive button").components(|f| {
|
||||||
|
f.create_action_row(|row| {
|
||||||
|
row.create_button(|b| {
|
||||||
|
b.style(ButtonStyle::Danger)
|
||||||
|
.label("Big Red Fucking Button")
|
||||||
|
.custom_id("gulag_button")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
#[required_permissions(ADMINISTRATOR)]
|
#[required_permissions(ADMINISTRATOR)]
|
||||||
async fn random_mute(ctx: &Context, msg: &Message) -> CommandResult {
|
async fn random_mute(ctx: &Context, msg: &Message) -> CommandResult {
|
||||||
|
@ -336,19 +355,22 @@ async fn uptime(ctx: &Context, msg: &Message) -> CommandResult {
|
||||||
let min = (seconds - (hours * 60 * 60)) / 60;
|
let min = (seconds - (hours * 60 * 60)) / 60;
|
||||||
let seconds = seconds - (hours * 3600) - (min * 60);
|
let seconds = seconds - (hours * 3600) - (min * 60);
|
||||||
|
|
||||||
msg.channel_id.send_message(&ctx.http, |m| {
|
msg.channel_id
|
||||||
m.embed(|e| {
|
.send_message(&ctx.http, |m| {
|
||||||
e.field(
|
m.embed(|e| {
|
||||||
"Uptime",
|
e.field(
|
||||||
if hours > 0 {
|
"Uptime",
|
||||||
format!("{}h {:02}min {:02}s", hours, min, seconds)
|
if hours > 0 {
|
||||||
} else {
|
format!("{}h {:02}min {:02}s", hours, min, seconds)
|
||||||
format!("{}min {:02}s", min, seconds)
|
} else {
|
||||||
},
|
format!("{}min {:02}s", min, seconds)
|
||||||
false,
|
},
|
||||||
).colour((247, 76, 0))
|
false,
|
||||||
|
)
|
||||||
|
.colour((247, 76, 0))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}).await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,91 +1,85 @@
|
||||||
use serde::Serialize;
|
|
||||||
use serde_json::Value;
|
|
||||||
use serenity::{
|
use serenity::{
|
||||||
builder::CreateEmbed,
|
|
||||||
client::Context,
|
client::Context,
|
||||||
framework::standard::CommandResult,
|
framework::standard::CommandResult,
|
||||||
model::{channel::Embed, guild::Member, Permissions},
|
model::{
|
||||||
|
guild::Member,
|
||||||
|
interactions::{
|
||||||
|
ApplicationCommandInteractionData, Interaction,
|
||||||
|
InteractionApplicationCommandCallbackDataFlags, InteractionData,
|
||||||
|
InteractionResponseType, MessageComponent,
|
||||||
|
},
|
||||||
|
Permissions,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{data::GuildOptionsKey, utils::message::embed_author};
|
use crate::{data::GuildOptionsKey, utils::message::embed_author};
|
||||||
|
|
||||||
pub(crate) async fn handle_interaction(ctx: &Context, object: Value) -> CommandResult {
|
pub(crate) async fn handle_interaction(ctx: &Context, interaction: &Interaction) -> CommandResult {
|
||||||
let data = object.get("data").ok_or("Failed to get data")?;
|
let data = interaction.data.as_ref().ok_or("Failed to get data")?;
|
||||||
|
|
||||||
let id = object
|
match data {
|
||||||
.get("id")
|
InteractionData::ApplicationCommand(app) => {
|
||||||
.and_then(|i| i.as_str())
|
command(ctx, &interaction, &app).await?;
|
||||||
.ok_or("Failed to get ID")?;
|
|
||||||
|
|
||||||
let token = object
|
|
||||||
.get("token")
|
|
||||||
.and_then(|t| t.as_str())
|
|
||||||
.ok_or("Failed to get token")?;
|
|
||||||
|
|
||||||
let name = data
|
|
||||||
.get("name")
|
|
||||||
.and_then(|v| v.as_str())
|
|
||||||
.ok_or("Failed to get name")?;
|
|
||||||
|
|
||||||
let guild_id = object
|
|
||||||
.get("guild_id")
|
|
||||||
.and_then(|v| v.as_str())
|
|
||||||
.and_then(|v| v.parse::<u64>().ok())
|
|
||||||
.ok_or("Failed to get guild id")?;
|
|
||||||
|
|
||||||
let author = object.get("member").ok_or("Failed to get author")?;
|
|
||||||
|
|
||||||
let author_id = author
|
|
||||||
.get("user")
|
|
||||||
.and_then(|v| v.get("id"))
|
|
||||||
.and_then(|v| v.as_str())
|
|
||||||
.and_then(|v| v.parse::<u64>().ok())
|
|
||||||
.ok_or("Failed to get permission")?;
|
|
||||||
|
|
||||||
let permissions = Permissions::from_bits_truncate(
|
|
||||||
author
|
|
||||||
.get("permissions")
|
|
||||||
.and_then(|v| v.as_str())
|
|
||||||
.and_then(|v| v.parse::<u64>().ok())
|
|
||||||
.ok_or("Failed to get permission")?,
|
|
||||||
);
|
|
||||||
|
|
||||||
let author_member = ctx
|
|
||||||
.cache
|
|
||||||
.guild(guild_id)
|
|
||||||
.await
|
|
||||||
.ok_or("Failed to get guild")?
|
|
||||||
.member(&ctx.http, author_id)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
match name {
|
|
||||||
"goulag" => {
|
|
||||||
if permissions.administrator() {
|
|
||||||
goulag(ctx, data, guild_id, &author_member, id, token).await?
|
|
||||||
} else {
|
|
||||||
Response::new_with_embed(4, |e| {
|
|
||||||
embed_author(e, Some(&author_member))
|
|
||||||
.title("Error")
|
|
||||||
.description("You don't have the right to do that")
|
|
||||||
.colour((247, 76, 0))
|
|
||||||
})
|
|
||||||
.send(token, id)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => (),
|
InteractionData::MessageComponent(msg) => message_interact(ctx, interaction, msg).await?,
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn command(
|
||||||
|
ctx: &Context,
|
||||||
|
interaction: &Interaction,
|
||||||
|
data: &ApplicationCommandInteractionData,
|
||||||
|
) -> CommandResult {
|
||||||
|
let name = &data.name;
|
||||||
|
|
||||||
|
let guild_id = interaction.guild_id.ok_or("Failed to get guild id")?;
|
||||||
|
|
||||||
|
let author = interaction
|
||||||
|
.member
|
||||||
|
.as_ref()
|
||||||
|
.ok_or("Failed to get permission")?;
|
||||||
|
|
||||||
|
let permissions = Permissions::from_bits_truncate(
|
||||||
|
author
|
||||||
|
.permissions
|
||||||
|
.map(|v| v.bits)
|
||||||
|
.ok_or("Failed to get permission")?,
|
||||||
|
);
|
||||||
|
|
||||||
|
match name.as_str() {
|
||||||
|
"goulag" => {
|
||||||
|
if permissions.administrator() {
|
||||||
|
goulag(ctx, interaction, data, guild_id.0, &author).await?
|
||||||
|
} else {
|
||||||
|
interaction
|
||||||
|
.create_interaction_response(&ctx.http, |response| {
|
||||||
|
response
|
||||||
|
.kind(InteractionResponseType::ChannelMessageWithSource)
|
||||||
|
.interaction_response_data(|message| {
|
||||||
|
message.create_embed(|e| {
|
||||||
|
embed_author(e, Some(author))
|
||||||
|
.title("Error")
|
||||||
|
.description("You don't have the right to do that")
|
||||||
|
.colour((247, 76, 0))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
async fn goulag(
|
async fn goulag(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
data: &Value,
|
interaction: &Interaction,
|
||||||
|
data: &ApplicationCommandInteractionData,
|
||||||
guild_id: u64,
|
guild_id: u64,
|
||||||
author: &Member,
|
author: &Member,
|
||||||
id: &str,
|
|
||||||
token: &str,
|
|
||||||
) -> CommandResult {
|
) -> CommandResult {
|
||||||
let ctx_data = ctx.data.read().await;
|
let ctx_data = ctx.data.read().await;
|
||||||
let ctx_data = ctx_data
|
let ctx_data = ctx_data
|
||||||
|
@ -96,24 +90,16 @@ async fn goulag(
|
||||||
|
|
||||||
if let Some(guild_options) = guild_options {
|
if let Some(guild_options) = guild_options {
|
||||||
if let Some(mute_role) = guild_options.get_mute_role() {
|
if let Some(mute_role) = guild_options.get_mute_role() {
|
||||||
let options = data
|
let options = &data.options;
|
||||||
.get("options")
|
|
||||||
.and_then(|v| v.as_array())
|
|
||||||
.ok_or("Failed to get options")?;
|
|
||||||
|
|
||||||
let user = options
|
let user = options
|
||||||
.iter()
|
.iter()
|
||||||
.find(|f| {
|
.find(|f| f.name == "user")
|
||||||
if let Some(name) = f.get("name").map(|n| n.as_str()).and_then(|f| f) {
|
|
||||||
name == "user"
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.ok_or("Failed to get user")?;
|
.ok_or("Failed to get user")?;
|
||||||
|
|
||||||
let user_id = user
|
let user_id = user
|
||||||
.get("value")
|
.value
|
||||||
|
.as_ref()
|
||||||
.and_then(|v| v.as_str())
|
.and_then(|v| v.as_str())
|
||||||
.and_then(|v| v.parse::<u64>().ok())
|
.and_then(|v| v.parse::<u64>().ok())
|
||||||
.ok_or("Failed to get user id")?;
|
.ok_or("Failed to get user id")?;
|
||||||
|
@ -128,143 +114,83 @@ async fn goulag(
|
||||||
|
|
||||||
member.add_role(&ctx.http, mute_role).await?;
|
member.add_role(&ctx.http, mute_role).await?;
|
||||||
|
|
||||||
Response::new_with_embed(4, |e| {
|
interaction
|
||||||
embed_author(e, Some(author))
|
.create_interaction_response(&ctx.http, |response| {
|
||||||
.title("Mute")
|
response
|
||||||
.description(format!("{} was muted", member.display_name()))
|
.kind(InteractionResponseType::ChannelMessageWithSource)
|
||||||
.colour((247, 76, 0))
|
.interaction_response_data(|message| {
|
||||||
})
|
message.create_embed(|e| {
|
||||||
.send(token, id)
|
embed_author(e, Some(author))
|
||||||
.await?;
|
.title("Mute")
|
||||||
|
.description(format!("{} was muted", member.display_name()))
|
||||||
|
.colour((247, 76, 0))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Response::new_with_embed(4, |e| {
|
interaction
|
||||||
embed_author(e, Some(author))
|
.create_interaction_response(&ctx.http, |response| {
|
||||||
.title("Error")
|
response
|
||||||
.description("Unknown mute role")
|
.kind(InteractionResponseType::ChannelMessageWithSource)
|
||||||
.colour((247, 76, 0))
|
.interaction_response_data(|message| {
|
||||||
})
|
message.create_embed(|e| {
|
||||||
.send(token, id)
|
embed_author(e, Some(author))
|
||||||
.await?;
|
.title("Error")
|
||||||
|
.description("Unknown mute role")
|
||||||
|
.colour((247, 76, 0))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
async fn message_interact(
|
||||||
pub(crate) struct ResponseData {
|
ctx: &Context,
|
||||||
tts: bool,
|
interaction: &Interaction,
|
||||||
content: Option<String>,
|
msg_interaction: &MessageComponent,
|
||||||
embeds: Option<Vec<Value>>,
|
) -> CommandResult {
|
||||||
flags: u64,
|
match msg_interaction.custom_id.as_str() {
|
||||||
}
|
"gulag_button" => {
|
||||||
|
let guild_id = interaction
|
||||||
|
.guild_id
|
||||||
|
.as_ref()
|
||||||
|
.ok_or("Failed to get guild id")?;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
let mut member = interaction.member.clone().ok_or("Failed to get user")?;
|
||||||
impl ResponseData {
|
let role = match ctx
|
||||||
pub(crate) fn embed<F>(mut self, f: F) -> Self
|
.cache
|
||||||
where
|
.guild(guild_id)
|
||||||
F: FnOnce(&mut CreateEmbed) -> &mut CreateEmbed,
|
.await
|
||||||
{
|
.unwrap()
|
||||||
let embeds = self.embeds.get_or_insert(Default::default());
|
.role_by_name("mute")
|
||||||
|
{
|
||||||
|
Some(role) => Ok(role.id),
|
||||||
|
None => Err(tokio::io::Error::new(
|
||||||
|
tokio::io::ErrorKind::Other,
|
||||||
|
"Unkown role",
|
||||||
|
)),
|
||||||
|
}?;
|
||||||
|
|
||||||
embeds.push(Embed::fake(f));
|
member.add_role(ctx, role).await?;
|
||||||
|
interaction
|
||||||
self
|
.create_interaction_response(&ctx.http, |response| {
|
||||||
}
|
response
|
||||||
|
.kind(InteractionResponseType::ChannelMessageWithSource)
|
||||||
/// Get a reference to the response data's tts.
|
.interaction_response_data(|message| {
|
||||||
pub(crate) fn tts(&self) -> &bool {
|
message
|
||||||
&self.tts
|
.content("<:cheh:780736245675982909>")
|
||||||
}
|
.flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL)
|
||||||
|
})
|
||||||
/// Get a reference to the response data's content.
|
})
|
||||||
pub(crate) fn content(&self) -> &Option<String> {
|
.await?;
|
||||||
&self.content
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a reference to the response data's flags.
|
|
||||||
pub(crate) fn flags(&self) -> &u64 {
|
|
||||||
&self.flags
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the response data's content.
|
|
||||||
pub(crate) fn set_content(mut self, content: Option<String>) -> Self {
|
|
||||||
self.content = content;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the response data's tts.
|
|
||||||
pub(crate) fn set_tts(mut self, tts: bool) -> Self {
|
|
||||||
self.tts = tts;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the response data's flags.
|
|
||||||
pub(crate) fn set_flags(mut self, flags: u64) -> Self {
|
|
||||||
self.flags = flags;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a reference to the response data's embeds.
|
|
||||||
pub(crate) fn embeds(&self) -> &Option<Vec<Value>> {
|
|
||||||
&self.embeds
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a mutable reference to the response data's embeds.
|
|
||||||
pub(crate) fn embeds_mut(&mut self) -> &mut Option<Vec<Value>> {
|
|
||||||
&mut self.embeds
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the response data's embeds.
|
|
||||||
pub(crate) fn set_embeds(mut self, embeds: Option<Vec<Value>>) -> Self {
|
|
||||||
self.embeds = embeds;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ResponseData {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
tts: false,
|
|
||||||
content: None,
|
|
||||||
embeds: None,
|
|
||||||
flags: 0,
|
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
Ok(())
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub(crate) struct Response {
|
|
||||||
#[serde(rename(serialize = "type"))]
|
|
||||||
response_type: u64,
|
|
||||||
data: Option<ResponseData>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Response {
|
|
||||||
pub(crate) fn new_with_embed<F>(response_type: u64, f: F) -> Self
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut CreateEmbed) -> &mut CreateEmbed,
|
|
||||||
{
|
|
||||||
Response {
|
|
||||||
response_type,
|
|
||||||
data: Some(ResponseData::default().embed(f)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn send(&self, token: &str, id: &str) -> CommandResult {
|
|
||||||
reqwest::Client::new()
|
|
||||||
.post(
|
|
||||||
format!(
|
|
||||||
"https://discord.com/api/v8/interactions/{}/{}/callback",
|
|
||||||
id, token
|
|
||||||
)
|
|
||||||
.as_str(),
|
|
||||||
)
|
|
||||||
.json(self)
|
|
||||||
.send()
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -305,6 +305,7 @@ async fn play(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
/*
|
||||||
msg.channel_id
|
msg.channel_id
|
||||||
.send_message(&ctx.http, |m| {
|
.send_message(&ctx.http, |m| {
|
||||||
embed_response(
|
embed_response(
|
||||||
|
@ -361,6 +362,7 @@ async fn play(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||||
};
|
};
|
||||||
|
|
||||||
handler.enqueue_source(s);
|
handler.enqueue_source(s);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let track = handler.queue().current();
|
let track = handler.queue().current();
|
||||||
|
@ -385,6 +387,7 @@ async fn play(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
*/
|
||||||
} else {
|
} else {
|
||||||
match songbird::ytdl(&url).await {
|
match songbird::ytdl(&url).await {
|
||||||
Ok(source) => source,
|
Ok(source) => source,
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
|
/*
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use tokio::{
|
use tokio::{
|
||||||
io::{Error, ErrorKind, Result},
|
io::{Error, ErrorKind, Result},
|
||||||
process::Command,
|
process::Command,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
pub(crate) async fn get_list_of_urls(url: &str) -> Result<Vec<String>> {
|
pub(crate) async fn get_list_of_urls(url: &str) -> Result<Vec<String>> {
|
||||||
let output = Command::new("youtube-dl")
|
let output = Command::new("youtube-dl")
|
||||||
.args(&["-j", "--flat-playlist", &url])
|
.args(&["-j", "--flat-playlist", &url])
|
||||||
|
@ -30,3 +33,5 @@ pub(crate) async fn get_list_of_urls(url: &str) -> Result<Vec<String>> {
|
||||||
struct YtdlResponse {
|
struct YtdlResponse {
|
||||||
url: String,
|
url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
|
@ -8,6 +8,7 @@ pub(crate) struct Conf {
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub(crate) struct Bot {
|
pub(crate) struct Bot {
|
||||||
pub(crate) token: String,
|
pub(crate) token: String,
|
||||||
|
pub(crate) application_id: u64,
|
||||||
pub(crate) log_attachments: Option<bool>,
|
pub(crate) log_attachments: Option<bool>,
|
||||||
pub(crate) invite_url: Option<String>,
|
pub(crate) invite_url: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ impl TypeMapKey for BulletsContainer {
|
||||||
type Value = HashMap<u64, (u8, u8)>;
|
type Value = HashMap<u64, (u8, u8)>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub(crate) struct Uptime;
|
pub(crate) struct Uptime;
|
||||||
impl TypeMapKey for Uptime {
|
impl TypeMapKey for Uptime {
|
||||||
type Value = std::time::Instant;
|
type Value = std::time::Instant;
|
||||||
|
|
86
src/main.rs
86
src/main.rs
|
@ -8,7 +8,6 @@ use crate::{
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use commands::interaction;
|
use commands::interaction;
|
||||||
use log::{debug, error, info};
|
use log::{debug, error, info};
|
||||||
use serde_json::Value;
|
|
||||||
use serenity::{
|
use serenity::{
|
||||||
framework::standard::{
|
framework::standard::{
|
||||||
help_commands,
|
help_commands,
|
||||||
|
@ -27,7 +26,7 @@ use std::{
|
||||||
path::Path,
|
path::Path,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
time::Instant
|
time::Instant,
|
||||||
};
|
};
|
||||||
use tokio::{fs::File, io::AsyncWriteExt};
|
use tokio::{fs::File, io::AsyncWriteExt};
|
||||||
|
|
||||||
|
@ -44,7 +43,7 @@ mod data;
|
||||||
mod presence;
|
mod presence;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
const MINIMUM_MENTIONS: usize = 10;
|
const MINIMUM_MENTIONS: usize = 20;
|
||||||
|
|
||||||
const PREFIX: &str = "?";
|
const PREFIX: &str = "?";
|
||||||
static mut LOG_ATTACHMENTS: bool = false;
|
static mut LOG_ATTACHMENTS: bool = false;
|
||||||
|
@ -140,6 +139,7 @@ async fn main() -> IoResult<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut client = Client::builder(&token)
|
let mut client = Client::builder(&token)
|
||||||
|
.application_id(conf.bot.application_id)
|
||||||
.event_handler(Messages {})
|
.event_handler(Messages {})
|
||||||
.framework(framework);
|
.framework(framework);
|
||||||
|
|
||||||
|
@ -224,41 +224,85 @@ impl EventHandler for Messages {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn unknown(&self, ctx: Context, name: String, raw: Value) {
|
async fn interaction_create(&self, ctx: Context, interaction: Interaction) {
|
||||||
match name.as_str() {
|
if let Err(e) = interaction::handle_interaction(&ctx, &interaction).await {
|
||||||
"INTERACTION_CREATE" => {
|
error!("While handling interaction : {}", e);
|
||||||
if let Err(e) = interaction::handle_interaction(&ctx, raw).await {
|
}
|
||||||
error!("While handling interaction : {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => debug!("Unknown event : {}, {:?}", name, raw),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn log_mentions(ctx: Context, new_message: &Message) -> CommandResult {
|
async fn log_mentions(ctx: Context, new_message: &Message) -> CommandResult {
|
||||||
|
if !new_message.mention_everyone
|
||||||
|
&& new_message.mention_roles.is_empty()
|
||||||
|
&& new_message.mentions.is_empty()
|
||||||
|
{
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
let data = ctx.data.read().await;
|
let data = ctx.data.read().await;
|
||||||
|
|
||||||
let guilds_options = data
|
let guilds_options = data
|
||||||
.get::<GuildOptionsKey>()
|
.get::<GuildOptionsKey>()
|
||||||
.expect("Expected NonKickGuildsContainer in TypeMap.");
|
.expect("Expected NonKickGuildsContainer in TypeMap.");
|
||||||
|
|
||||||
if new_message.mention_everyone {
|
let mute = if new_message.mention_everyone {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
let mut user_mentioned: HashSet<u64> = HashSet::with_capacity(MINIMUM_MENTIONS); // TODO IN GUILD OPTIONS
|
||||||
|
|
||||||
|
let guild = new_message.guild(&ctx).await.ok_or("Failed to get guild")?;
|
||||||
|
|
||||||
|
let mut iter_users = new_message.mentions.iter();
|
||||||
|
while user_mentioned.len() < MINIMUM_MENTIONS {
|
||||||
|
if let Some(u) = iter_users.next() {
|
||||||
|
user_mentioned.insert(u.id.0);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut iter_roles = new_message.mention_roles.iter();
|
||||||
|
|
||||||
|
let mut continue_getting_members = true;
|
||||||
|
let mut after = None;
|
||||||
|
|
||||||
|
while continue_getting_members {
|
||||||
|
let members = guild.members(&ctx, Some(1000), after).await?;
|
||||||
|
|
||||||
|
while user_mentioned.len() < MINIMUM_MENTIONS {
|
||||||
|
if let Some(r) = iter_roles.next() {
|
||||||
|
for member in members.iter() {
|
||||||
|
if let Some(roles) = member.roles(&ctx).await {
|
||||||
|
if roles.iter().any(|role| role.id.0 == r.0) {
|
||||||
|
log::debug!("{:?}", member);
|
||||||
|
user_mentioned.insert(member.user.id.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if members.len() == 1000 {
|
||||||
|
continue_getting_members = true;
|
||||||
|
after = Some(members.last().unwrap().user.id);
|
||||||
|
} else {
|
||||||
|
continue_getting_members = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
user_mentioned.len() >= MINIMUM_MENTIONS
|
||||||
|
};
|
||||||
|
|
||||||
|
if mute {
|
||||||
if let Some(guild_id) = new_message.guild_id {
|
if let Some(guild_id) = new_message.guild_id {
|
||||||
if let Some(options) = guilds_options.get(&guild_id) {
|
if let Some(options) = guilds_options.get(&guild_id) {
|
||||||
if let Some(role_id) = options.mute_id {
|
if let Some(role_id) = options.mute_id {
|
||||||
let mut member = new_message.member(&ctx).await?;
|
let mut member = new_message.member(&ctx).await?;
|
||||||
member.add_role(&ctx.http, role_id).await?;
|
member.add_role(&ctx.http, role_id).await?;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if new_message.mention_everyone
|
|
||||||
|| (new_message.mention_roles.len() + new_message.mentions.len()) > MINIMUM_MENTIONS
|
|
||||||
{
|
|
||||||
if let Some(guild_id) = new_message.guild_id {
|
|
||||||
if let Some(options) = guilds_options.get(&guild_id) {
|
|
||||||
if let Some(channel_id) = options.mention_log_channel {
|
if let Some(channel_id) = options.mention_log_channel {
|
||||||
channel_id
|
channel_id
|
||||||
.send_message(&ctx.http, |m| {
|
.send_message(&ctx.http, |m| {
|
||||||
|
|
Loading…
Reference in New Issue