mirror of
https://github.com/arthur-pbty/shadowbot.git
synced 2026-06-14 08:08:29 +02:00
chore(commands): reorganize command files by metadata categories
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
use serenity::builder::CreateEmbed;
|
||||
use serenity::model::prelude::*;
|
||||
use serenity::prelude::*;
|
||||
|
||||
use crate::commands::advanced_tools;
|
||||
use crate::commands::common::{send_embed, theme_color};
|
||||
use crate::db::DbPoolKey;
|
||||
|
||||
async fn pool(ctx: &Context) -> Option<sqlx::PgPool> {
|
||||
let data = ctx.data.read().await;
|
||||
data.get::<DbPoolKey>().cloned()
|
||||
}
|
||||
|
||||
pub async fn handle_autobackup(ctx: &Context, msg: &Message, args: &[&str]) {
|
||||
let Some(guild_id) = msg.guild_id else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(kind_raw) = args.first() else {
|
||||
return;
|
||||
};
|
||||
let Some(days_raw) = args.get(1) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(kind) = advanced_tools::backup_kind_from_input(kind_raw) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Ok(days) = days_raw.parse::<i32>() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(pool) = pool(ctx).await else {
|
||||
return;
|
||||
};
|
||||
|
||||
let bot_id = ctx.cache.current_user().id;
|
||||
let _ = sqlx::query(
|
||||
r#"
|
||||
INSERT INTO bot_autobackups (bot_id, guild_id, kind, interval_days, next_run_at)
|
||||
VALUES ($1, $2, $3, $4, NOW() + make_interval(days => $4))
|
||||
ON CONFLICT (bot_id, guild_id, kind)
|
||||
DO UPDATE SET interval_days = EXCLUDED.interval_days,
|
||||
next_run_at = NOW() + make_interval(days => EXCLUDED.interval_days);
|
||||
"#,
|
||||
)
|
||||
.bind(bot_id.get() as i64)
|
||||
.bind(guild_id.get() as i64)
|
||||
.bind(kind)
|
||||
.bind(days.max(1))
|
||||
.execute(&pool)
|
||||
.await;
|
||||
|
||||
send_embed(
|
||||
ctx,
|
||||
msg,
|
||||
CreateEmbed::new()
|
||||
.title("AutoBackup")
|
||||
.description(format!(
|
||||
"Auto-backup `{}` configuree toutes les {} jours.",
|
||||
kind,
|
||||
days.max(1)
|
||||
))
|
||||
.color(theme_color(ctx).await),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub struct AutoBackupCommand;
|
||||
pub static COMMAND_DESCRIPTOR: AutoBackupCommand = AutoBackupCommand;
|
||||
|
||||
impl crate::commands::command_contract::CommandSpec for AutoBackupCommand {
|
||||
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
|
||||
crate::commands::command_contract::CommandMetadata {
|
||||
name: "autobackup",
|
||||
category: "automation",
|
||||
params: "<serveur/emoji> <jours>",
|
||||
description: "Definit l'intervalle en jours des backups automatiques.",
|
||||
examples: &["+autobackup serveur 3", "+autobackup emoji 7"],
|
||||
default_aliases: &["abkp"],
|
||||
allow_in_dm: false,
|
||||
default_permission: 7,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
use chrono::Utc;
|
||||
use serenity::builder::CreateEmbed;
|
||||
use serenity::model::Colour;
|
||||
use serenity::model::prelude::*;
|
||||
use serenity::prelude::*;
|
||||
|
||||
use crate::commands::common::{parse_channel_id, send_embed};
|
||||
use crate::db;
|
||||
|
||||
pub async fn handle_autopublish(ctx: &Context, msg: &Message, args: &[&str]) {
|
||||
let Some(guild_id) = msg.guild_id else {
|
||||
return;
|
||||
};
|
||||
|
||||
if args.is_empty() {
|
||||
let Some(pool) = ({
|
||||
let data = ctx.data.read().await;
|
||||
data.get::<db::DbPoolKey>().cloned()
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let bot_id = ctx.cache.current_user().id.get() as i64;
|
||||
let channels = db::get_autopublish_channels(&pool, bot_id, guild_id.get() as i64)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
let description = if channels.is_empty() {
|
||||
"Aucun salon d'annonces configuré.".to_string()
|
||||
} else {
|
||||
channels
|
||||
.into_iter()
|
||||
.map(|channel| format!("<#{}>", channel.channel_id))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
};
|
||||
|
||||
send_embed(
|
||||
ctx,
|
||||
msg,
|
||||
CreateEmbed::new()
|
||||
.title("Autopublish")
|
||||
.description(description)
|
||||
.colour(Colour::from_rgb(100, 150, 255)),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
|
||||
let enabled = args[0].eq_ignore_ascii_case("on") || args[0].eq_ignore_ascii_case("enable");
|
||||
let disabled = args[0].eq_ignore_ascii_case("off") || args[0].eq_ignore_ascii_case("disable");
|
||||
if !enabled && !disabled {
|
||||
send_embed(
|
||||
ctx,
|
||||
msg,
|
||||
CreateEmbed::new()
|
||||
.title("Autopublish")
|
||||
.description("Utilisation: +autopublish on|off [#canal]")
|
||||
.color(0xED4245),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(pool) = ({
|
||||
let data = ctx.data.read().await;
|
||||
data.get::<db::DbPoolKey>().cloned()
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let bot_id = ctx.cache.current_user().id.get() as i64;
|
||||
let guild_id_i64 = guild_id.get() as i64;
|
||||
let channel_id = args
|
||||
.get(1)
|
||||
.and_then(|value| parse_channel_id(value))
|
||||
.unwrap_or(msg.channel_id);
|
||||
|
||||
let result = if enabled {
|
||||
db::add_autopublish_channel(&pool, bot_id, guild_id_i64, channel_id.get() as i64).await
|
||||
} else {
|
||||
db::remove_autopublish_channel(&pool, bot_id, guild_id_i64, channel_id.get() as i64).await
|
||||
};
|
||||
|
||||
if result.is_err() {
|
||||
send_embed(
|
||||
ctx,
|
||||
msg,
|
||||
CreateEmbed::new()
|
||||
.title("Autopublish")
|
||||
.description("Impossible de mettre à jour le salon d'annonces.")
|
||||
.color(0xED4245),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
|
||||
let embed = if enabled {
|
||||
CreateEmbed::new()
|
||||
.title("Autopublish activé")
|
||||
.description(format!("Salon: <#{}>", channel_id.get()))
|
||||
.colour(Colour::from_rgb(0, 200, 120))
|
||||
.timestamp(Utc::now())
|
||||
} else {
|
||||
CreateEmbed::new()
|
||||
.title("Autopublish désactivé")
|
||||
.description(format!("Salon: <#{}>", channel_id.get()))
|
||||
.colour(Colour::from_rgb(255, 120, 0))
|
||||
.timestamp(Utc::now())
|
||||
};
|
||||
|
||||
send_embed(ctx, msg, embed).await;
|
||||
}
|
||||
|
||||
pub struct AutopublishCommand;
|
||||
pub static COMMAND_DESCRIPTOR: AutopublishCommand = AutopublishCommand;
|
||||
|
||||
impl crate::commands::command_contract::CommandSpec for AutopublishCommand {
|
||||
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
|
||||
crate::commands::command_contract::CommandMetadata {
|
||||
name: "autopublish",
|
||||
category: "automation",
|
||||
params: "on|off [#canal]",
|
||||
description: "Affiche, active ou desactive la publication automatique des annonces.",
|
||||
examples: &[
|
||||
"+autopublish",
|
||||
"+autopublish on #annonces",
|
||||
"+help autopublish",
|
||||
],
|
||||
default_aliases: &["apb"],
|
||||
allow_in_dm: false,
|
||||
default_permission: 5,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
use serenity::builder::{CreateActionRow, CreateButton, CreateEmbed, CreateMessage};
|
||||
use serenity::model::prelude::*;
|
||||
use serenity::prelude::*;
|
||||
|
||||
use crate::commands::common::{parse_channel_id, send_embed, theme_color};
|
||||
use crate::db::DbPoolKey;
|
||||
|
||||
fn owned_component_id(action: &str, owner_id: UserId) -> String {
|
||||
format!("{}:{}", action, owner_id.get())
|
||||
}
|
||||
|
||||
async fn pool(ctx: &Context) -> Option<sqlx::PgPool> {
|
||||
let data = ctx.data.read().await;
|
||||
data.get::<DbPoolKey>().cloned()
|
||||
}
|
||||
|
||||
pub async fn handle_autoreact(ctx: &Context, msg: &Message, args: &[&str]) {
|
||||
let Some(guild_id) = msg.guild_id else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(action) = args.first().map(|s| s.to_lowercase()) else {
|
||||
let embed = CreateEmbed::new()
|
||||
.title("AutoReact")
|
||||
.description("Utilise les boutons pour ajouter/supprimer/lister via UI.")
|
||||
.color(theme_color(ctx).await);
|
||||
let components = vec![CreateActionRow::Buttons(vec![
|
||||
CreateButton::new(owned_component_id("adv:autoreact:add_modal", msg.author.id))
|
||||
.label("Ajouter")
|
||||
.style(ButtonStyle::Success),
|
||||
CreateButton::new(owned_component_id("adv:autoreact:del_modal", msg.author.id))
|
||||
.label("Supprimer")
|
||||
.style(ButtonStyle::Danger),
|
||||
CreateButton::new(owned_component_id("adv:autoreact:list", msg.author.id))
|
||||
.label("Lister")
|
||||
.style(ButtonStyle::Primary),
|
||||
])];
|
||||
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.send_message(
|
||||
&ctx.http,
|
||||
CreateMessage::new().embed(embed).components(components),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(pool) = pool(ctx).await else {
|
||||
return;
|
||||
};
|
||||
|
||||
let bot_id = ctx.cache.current_user().id;
|
||||
|
||||
if action == "list" {
|
||||
let rows = sqlx::query_as::<_, (i64, String)>(
|
||||
r#"
|
||||
SELECT channel_id, emoji
|
||||
FROM bot_autoreacts
|
||||
WHERE bot_id = $1 AND guild_id = $2
|
||||
ORDER BY channel_id ASC, emoji ASC;
|
||||
"#,
|
||||
)
|
||||
.bind(bot_id.get() as i64)
|
||||
.bind(guild_id.get() as i64)
|
||||
.fetch_all(&pool)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
|
||||
let desc = if rows.is_empty() {
|
||||
"Aucun autoreact configure.".to_string()
|
||||
} else {
|
||||
rows.into_iter()
|
||||
.map(|(channel_id, emoji)| format!("- <#{}> -> {}", channel_id, emoji))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
};
|
||||
|
||||
send_embed(
|
||||
ctx,
|
||||
msg,
|
||||
CreateEmbed::new()
|
||||
.title("AutoReact")
|
||||
.description(desc)
|
||||
.color(theme_color(ctx).await),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
|
||||
if args.len() < 3 {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(channel_id) = parse_channel_id(args[1]) else {
|
||||
return;
|
||||
};
|
||||
let emoji = args[2];
|
||||
|
||||
if action == "add" {
|
||||
let _ = sqlx::query(
|
||||
r#"
|
||||
INSERT INTO bot_autoreacts (bot_id, guild_id, channel_id, emoji)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
ON CONFLICT (bot_id, guild_id, channel_id, emoji) DO NOTHING;
|
||||
"#,
|
||||
)
|
||||
.bind(bot_id.get() as i64)
|
||||
.bind(guild_id.get() as i64)
|
||||
.bind(channel_id.get() as i64)
|
||||
.bind(emoji)
|
||||
.execute(&pool)
|
||||
.await;
|
||||
} else if action == "del" {
|
||||
let _ = sqlx::query(
|
||||
r#"
|
||||
DELETE FROM bot_autoreacts
|
||||
WHERE bot_id = $1 AND guild_id = $2 AND channel_id = $3 AND emoji = $4;
|
||||
"#,
|
||||
)
|
||||
.bind(bot_id.get() as i64)
|
||||
.bind(guild_id.get() as i64)
|
||||
.bind(channel_id.get() as i64)
|
||||
.bind(emoji)
|
||||
.execute(&pool)
|
||||
.await;
|
||||
}
|
||||
|
||||
send_embed(
|
||||
ctx,
|
||||
msg,
|
||||
CreateEmbed::new()
|
||||
.title("AutoReact")
|
||||
.description("Configuration mise a jour.")
|
||||
.color(theme_color(ctx).await),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub struct AutoReactCommand;
|
||||
pub static COMMAND_DESCRIPTOR: AutoReactCommand = AutoReactCommand;
|
||||
|
||||
impl crate::commands::command_contract::CommandSpec for AutoReactCommand {
|
||||
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
|
||||
crate::commands::command_contract::CommandMetadata {
|
||||
name: "autoreact",
|
||||
category: "automation",
|
||||
params: "<add/del> <salon> <emoji> | list",
|
||||
description: "Ajoute, retire et liste les reactions automatiquement appliquees aux messages d'un salon.",
|
||||
examples: &["+autoreact add #general 😀", "+autoreact list"],
|
||||
default_aliases: &["ar", "reactauto"],
|
||||
allow_in_dm: false,
|
||||
default_permission: 6,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
use serenity::builder::CreateEmbed;
|
||||
use serenity::model::prelude::*;
|
||||
use serenity::prelude::*;
|
||||
|
||||
use crate::commands::advanced_tools;
|
||||
use crate::commands::common::{send_embed, theme_color};
|
||||
|
||||
pub async fn handle_backup(ctx: &Context, msg: &Message, args: &[&str]) {
|
||||
let Some(guild_id) = msg.guild_id else {
|
||||
return;
|
||||
};
|
||||
|
||||
if args.is_empty() {
|
||||
send_embed(
|
||||
ctx,
|
||||
msg,
|
||||
CreateEmbed::new()
|
||||
.title("Backup")
|
||||
.description("Utilisation: +backup <serveur/emoji> <nom> | list/delete/load")
|
||||
.color(0xED4245),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
|
||||
let mut action = "create";
|
||||
let mut index = 0usize;
|
||||
if let Some(first) = args.first() {
|
||||
match first.to_lowercase().as_str() {
|
||||
"list" | "ls" => {
|
||||
action = "list";
|
||||
index = 1;
|
||||
}
|
||||
"delete" | "del" | "rm" => {
|
||||
action = "delete";
|
||||
index = 1;
|
||||
}
|
||||
"load" => {
|
||||
action = "load";
|
||||
index = 1;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let Some(kind_raw) = args.get(index) else {
|
||||
send_embed(
|
||||
ctx,
|
||||
msg,
|
||||
CreateEmbed::new()
|
||||
.title("Backup")
|
||||
.description("Type invalide: utilise serveur ou emoji.")
|
||||
.color(0xED4245),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(kind) = advanced_tools::backup_kind_from_input(kind_raw) else {
|
||||
send_embed(
|
||||
ctx,
|
||||
msg,
|
||||
CreateEmbed::new()
|
||||
.title("Backup")
|
||||
.description("Type invalide: utilise serveur ou emoji.")
|
||||
.color(0xED4245),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
};
|
||||
|
||||
match action {
|
||||
"list" => {
|
||||
advanced_tools::backup_list(ctx, msg, guild_id, kind).await;
|
||||
}
|
||||
"delete" => {
|
||||
let Some(name) = args
|
||||
.get(index + 1)
|
||||
.map(|s| s.trim())
|
||||
.filter(|s| !s.is_empty())
|
||||
else {
|
||||
send_embed(
|
||||
ctx,
|
||||
msg,
|
||||
CreateEmbed::new()
|
||||
.title("Backup")
|
||||
.description("Tu dois preciser un nom de backup.")
|
||||
.color(0xED4245),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
};
|
||||
advanced_tools::backup_delete(ctx, msg, guild_id, kind, name).await;
|
||||
}
|
||||
"load" => {
|
||||
let Some(name) = args
|
||||
.get(index + 1)
|
||||
.map(|s| s.trim())
|
||||
.filter(|s| !s.is_empty())
|
||||
else {
|
||||
send_embed(
|
||||
ctx,
|
||||
msg,
|
||||
CreateEmbed::new()
|
||||
.title("Backup")
|
||||
.description("Tu dois preciser un nom de backup.")
|
||||
.color(0xED4245),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
};
|
||||
advanced_tools::backup_load(ctx, msg, guild_id, kind, name).await;
|
||||
}
|
||||
_ => {
|
||||
let Some(name) = args
|
||||
.get(index + 1)
|
||||
.map(|s| s.trim())
|
||||
.filter(|s| !s.is_empty())
|
||||
else {
|
||||
send_embed(
|
||||
ctx,
|
||||
msg,
|
||||
CreateEmbed::new()
|
||||
.title("Backup")
|
||||
.description("Tu dois preciser un nom de backup.")
|
||||
.color(0xED4245),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
};
|
||||
|
||||
match advanced_tools::backup_create(ctx, guild_id, kind, name).await {
|
||||
Ok(()) => {
|
||||
send_embed(
|
||||
ctx,
|
||||
msg,
|
||||
CreateEmbed::new()
|
||||
.title("Backup")
|
||||
.description(format!("Backup `{}` de type `{}` creee.", name, kind))
|
||||
.color(theme_color(ctx).await),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
Err(err) => {
|
||||
send_embed(
|
||||
ctx,
|
||||
msg,
|
||||
CreateEmbed::new()
|
||||
.title("Backup")
|
||||
.description(format!("Erreur: {}", err))
|
||||
.color(0xED4245),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BackupCommand;
|
||||
pub static COMMAND_DESCRIPTOR: BackupCommand = BackupCommand;
|
||||
|
||||
impl crate::commands::command_contract::CommandSpec for BackupCommand {
|
||||
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
|
||||
crate::commands::command_contract::CommandMetadata {
|
||||
name: "backup",
|
||||
category: "automation",
|
||||
params: "<serveur/emoji> <nom> | list/delete/load",
|
||||
description: "Cree, liste, supprime et recharge des backups serveur ou emojis.",
|
||||
examples: &[
|
||||
"+backup serveur prod_1",
|
||||
"+backup list serveur",
|
||||
"+backup load emoji nightly",
|
||||
],
|
||||
default_aliases: &["bkp"],
|
||||
allow_in_dm: false,
|
||||
default_permission: 7,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
use serenity::builder::{CreateActionRow, CreateButton, CreateEmbed, CreateMessage};
|
||||
use serenity::model::prelude::*;
|
||||
use serenity::prelude::*;
|
||||
|
||||
use crate::commands::common::{send_embed, theme_color};
|
||||
|
||||
pub async fn handle_button(ctx: &Context, msg: &Message, args: &[&str]) {
|
||||
if args.len() < 2 {
|
||||
return;
|
||||
}
|
||||
|
||||
let action = args[0].to_lowercase();
|
||||
let link = args[1];
|
||||
|
||||
if action == "add" {
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.send_message(
|
||||
&ctx.http,
|
||||
CreateMessage::new()
|
||||
.content("Bouton personnalisé")
|
||||
.components(vec![CreateActionRow::Buttons(vec![
|
||||
CreateButton::new_link(link).label("Ouvrir"),
|
||||
])]),
|
||||
)
|
||||
.await;
|
||||
} else if action == "del" {
|
||||
send_embed(
|
||||
ctx,
|
||||
msg,
|
||||
CreateEmbed::new()
|
||||
.title("Button")
|
||||
.description("Suppression: supprime simplement le message contenant le bouton.")
|
||||
.color(theme_color(ctx).await),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ButtonCommand;
|
||||
pub static COMMAND_DESCRIPTOR: ButtonCommand = ButtonCommand;
|
||||
|
||||
impl crate::commands::command_contract::CommandSpec for ButtonCommand {
|
||||
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
|
||||
crate::commands::command_contract::CommandMetadata {
|
||||
name: "button",
|
||||
category: "automation",
|
||||
params: "<add/del> <lien>",
|
||||
description: "Ajoute ou supprime un bouton de decoration personnalise sur un message du bot.",
|
||||
examples: &[
|
||||
"+button add https://example.com",
|
||||
"+button del https://example.com",
|
||||
],
|
||||
default_aliases: &["btn"],
|
||||
allow_in_dm: false,
|
||||
default_permission: 6,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
use serenity::builder::CreateEmbed;
|
||||
use serenity::model::prelude::*;
|
||||
use serenity::prelude::*;
|
||||
|
||||
use crate::commands::common::{send_embed, theme_color};
|
||||
|
||||
fn emoji_url_from_source(msg: &Message, source: &str) -> String {
|
||||
if source.starts_with("http://") || source.starts_with("https://") {
|
||||
return source.to_string();
|
||||
}
|
||||
|
||||
if source.starts_with("<:") || source.starts_with("<a:") {
|
||||
let cleaned = source.trim_matches(|c| c == '<' || c == '>');
|
||||
let parts = cleaned.split(':').collect::<Vec<_>>();
|
||||
if parts.len() == 3 {
|
||||
let animated = parts[0] == "a";
|
||||
return format!(
|
||||
"https://cdn.discordapp.com/emojis/{}.{}",
|
||||
parts[2],
|
||||
if animated { "gif" } else { "png" }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(att) = msg.attachments.first() {
|
||||
return att.url.clone();
|
||||
}
|
||||
|
||||
String::new()
|
||||
}
|
||||
|
||||
pub async fn handle_create(ctx: &Context, msg: &Message, args: &[&str]) {
|
||||
let Some(guild_id) = msg.guild_id else {
|
||||
return;
|
||||
};
|
||||
|
||||
if args.len() < 2 {
|
||||
send_embed(
|
||||
ctx,
|
||||
msg,
|
||||
CreateEmbed::new()
|
||||
.title("Create Emoji")
|
||||
.description("Usage: +create <emoji/url> <nom>")
|
||||
.color(0xED4245),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
|
||||
let image_url = emoji_url_from_source(msg, args[0]);
|
||||
if image_url.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let response = match reqwest::get(&image_url).await {
|
||||
Ok(r) => r,
|
||||
Err(_) => return,
|
||||
};
|
||||
let bytes = match response.bytes().await {
|
||||
Ok(b) => b,
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
let data_uri = format!("data:image/png;base64,{}", {
|
||||
use base64::Engine;
|
||||
base64::engine::general_purpose::STANDARD.encode(bytes)
|
||||
});
|
||||
|
||||
let result = guild_id.create_emoji(&ctx.http, args[1], &data_uri).await;
|
||||
|
||||
let embed = if let Ok(emoji) = result {
|
||||
CreateEmbed::new()
|
||||
.title("Emoji")
|
||||
.description(format!("Emoji cree: {}", emoji))
|
||||
.color(theme_color(ctx).await)
|
||||
} else {
|
||||
CreateEmbed::new()
|
||||
.title("Emoji")
|
||||
.description("Impossible de creer l'emoji.")
|
||||
.color(0xED4245)
|
||||
};
|
||||
|
||||
send_embed(ctx, msg, embed).await;
|
||||
}
|
||||
|
||||
pub struct CreateCommand;
|
||||
pub static COMMAND_DESCRIPTOR: CreateCommand = CreateCommand;
|
||||
|
||||
impl crate::commands::command_contract::CommandSpec for CreateCommand {
|
||||
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
|
||||
crate::commands::command_contract::CommandMetadata {
|
||||
name: "create",
|
||||
category: "automation",
|
||||
params: "[emoji/url] [nom]",
|
||||
description: "Cree un emoji custom a partir d'une image, d'un lien ou d'un emoji nitro.",
|
||||
examples: &[
|
||||
"+create <:blob:123456789012345678> blobcopy",
|
||||
"+create https://... logo",
|
||||
],
|
||||
default_aliases: &["mkemoji", "ce"],
|
||||
allow_in_dm: false,
|
||||
default_permission: 6,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
use serenity::builder::CreateEmbed;
|
||||
use serenity::model::prelude::*;
|
||||
use serenity::prelude::*;
|
||||
|
||||
use crate::commands::common::{send_embed, theme_color};
|
||||
|
||||
pub async fn handle_newsticker(ctx: &Context, msg: &Message, args: &[&str]) {
|
||||
let _ = args;
|
||||
send_embed(
|
||||
ctx,
|
||||
msg,
|
||||
CreateEmbed::new()
|
||||
.title("NewSticker")
|
||||
.description("Creation de sticker disponible prochainement (API sticker V2).")
|
||||
.color(theme_color(ctx).await),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub struct NewStickerCommand;
|
||||
pub static COMMAND_DESCRIPTOR: NewStickerCommand = NewStickerCommand;
|
||||
|
||||
impl crate::commands::command_contract::CommandSpec for NewStickerCommand {
|
||||
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
|
||||
crate::commands::command_contract::CommandMetadata {
|
||||
name: "newsticker",
|
||||
category: "automation",
|
||||
params: "[nom]",
|
||||
description: "Cree un nouveau sticker a partir d'un sticker ou fichier repondu.",
|
||||
examples: &["+newsticker cool_pack"],
|
||||
default_aliases: &["stcreate", "nst"],
|
||||
allow_in_dm: false,
|
||||
default_permission: 6,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
use chrono::Utc;
|
||||
use serenity::builder::CreateEmbed;
|
||||
use serenity::model::prelude::*;
|
||||
use serenity::prelude::*;
|
||||
|
||||
use crate::commands::common::{parse_channel_id, send_embed};
|
||||
use crate::db;
|
||||
|
||||
fn is_image_filename(filename: &str) -> bool {
|
||||
let extension = filename
|
||||
.rsplit('.')
|
||||
.next()
|
||||
.unwrap_or("")
|
||||
.to_ascii_lowercase();
|
||||
|
||||
matches!(
|
||||
extension.as_str(),
|
||||
"jpg" | "jpeg" | "png" | "gif" | "webp" | "bmp" | "heic" | "heif"
|
||||
)
|
||||
}
|
||||
|
||||
fn has_only_photo_attachments(msg: &Message) -> bool {
|
||||
!msg.attachments.is_empty()
|
||||
&& msg
|
||||
.attachments
|
||||
.iter()
|
||||
.all(|attachment| is_image_filename(&attachment.filename))
|
||||
}
|
||||
|
||||
fn is_piconly_command_message(content: &str, prefix: &str) -> bool {
|
||||
if !content.starts_with(prefix) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let without_prefix = content.trim_start_matches(prefix).trim();
|
||||
without_prefix
|
||||
.split_whitespace()
|
||||
.next()
|
||||
.map(|command| command.eq_ignore_ascii_case("piconly"))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub async fn enforce_piconly_message(
|
||||
ctx: &Context,
|
||||
msg: &Message,
|
||||
content: &str,
|
||||
prefix: &str,
|
||||
) -> bool {
|
||||
let Some(guild_id) = msg.guild_id else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let Some(pool) = ({
|
||||
let data = ctx.data.read().await;
|
||||
data.get::<db::DbPoolKey>().cloned()
|
||||
}) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let bot_id = ctx.cache.current_user().id.get() as i64;
|
||||
let is_selfie_channel = db::is_piconly_channel(
|
||||
&pool,
|
||||
bot_id,
|
||||
guild_id.get() as i64,
|
||||
msg.channel_id.get() as i64,
|
||||
)
|
||||
.await
|
||||
.unwrap_or(false);
|
||||
|
||||
if !is_selfie_channel || is_piconly_command_message(content, prefix) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if has_only_photo_attachments(msg) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let _ = msg.delete(&ctx.http).await;
|
||||
|
||||
send_embed(
|
||||
ctx,
|
||||
msg,
|
||||
CreateEmbed::new()
|
||||
.title("Salon selfie")
|
||||
.description("Seules les photos sont autorisees dans ce salon.")
|
||||
.color(0xED4245)
|
||||
.timestamp(Utc::now()),
|
||||
)
|
||||
.await;
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub async fn handle_piconly(ctx: &Context, msg: &Message, args: &[&str]) {
|
||||
let Some(guild_id) = msg.guild_id else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(pool) = ({
|
||||
let data = ctx.data.read().await;
|
||||
data.get::<db::DbPoolKey>().cloned()
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let bot_id = ctx.cache.current_user().id.get() as i64;
|
||||
let guild_id_i64 = guild_id.get() as i64;
|
||||
|
||||
if args.is_empty() {
|
||||
let channels = db::get_piconly_channels(&pool, bot_id, guild_id_i64)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
|
||||
let description = if channels.is_empty() {
|
||||
"Aucun salon selfie configure.".to_string()
|
||||
} else {
|
||||
channels
|
||||
.into_iter()
|
||||
.map(|channel| format!("<#{}>", channel.channel_id))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
};
|
||||
|
||||
send_embed(
|
||||
ctx,
|
||||
msg,
|
||||
CreateEmbed::new()
|
||||
.title("PicOnly")
|
||||
.description(description)
|
||||
.timestamp(Utc::now()),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
|
||||
let adding = args[0].eq_ignore_ascii_case("add");
|
||||
let deleting = args[0].eq_ignore_ascii_case("del")
|
||||
|| args[0].eq_ignore_ascii_case("remove")
|
||||
|| args[0].eq_ignore_ascii_case("delete");
|
||||
|
||||
if !adding && !deleting {
|
||||
send_embed(
|
||||
ctx,
|
||||
msg,
|
||||
CreateEmbed::new()
|
||||
.title("PicOnly")
|
||||
.description("Utilisation: +piconly <add/del> [#salon]")
|
||||
.color(0xED4245),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
|
||||
let channel_id = args
|
||||
.get(1)
|
||||
.and_then(|raw| parse_channel_id(raw))
|
||||
.unwrap_or(msg.channel_id);
|
||||
|
||||
let result = if adding {
|
||||
db::add_piconly_channel(&pool, bot_id, guild_id_i64, channel_id.get() as i64).await
|
||||
} else {
|
||||
db::remove_piconly_channel(&pool, bot_id, guild_id_i64, channel_id.get() as i64).await
|
||||
};
|
||||
|
||||
if result.is_err() {
|
||||
send_embed(
|
||||
ctx,
|
||||
msg,
|
||||
CreateEmbed::new()
|
||||
.title("PicOnly")
|
||||
.description("Impossible de mettre a jour le salon selfie.")
|
||||
.color(0xED4245),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
|
||||
let embed = if adding {
|
||||
CreateEmbed::new()
|
||||
.title("Salon selfie ajoute")
|
||||
.description(format!("Salon: <#{}>", channel_id.get()))
|
||||
.timestamp(Utc::now())
|
||||
} else {
|
||||
CreateEmbed::new()
|
||||
.title("Salon selfie retire")
|
||||
.description(format!("Salon: <#{}>", channel_id.get()))
|
||||
.timestamp(Utc::now())
|
||||
};
|
||||
|
||||
send_embed(ctx, msg, embed).await;
|
||||
}
|
||||
|
||||
pub struct PiconlyCommand;
|
||||
pub static COMMAND_DESCRIPTOR: PiconlyCommand = PiconlyCommand;
|
||||
|
||||
impl crate::commands::command_contract::CommandSpec for PiconlyCommand {
|
||||
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
|
||||
crate::commands::command_contract::CommandMetadata {
|
||||
name: "piconly",
|
||||
category: "automation",
|
||||
params: "<add/del> [salon]",
|
||||
description: "Definit ou supprime un salon selfie, ou les membres ne peuvent envoyer que des photos.",
|
||||
examples: &["+piconly", "+piconly add #selfie", "+piconly del #selfie"],
|
||||
default_aliases: &["selfieonly"],
|
||||
allow_in_dm: false,
|
||||
default_permission: 6,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user