mirror of
https://github.com/revanced/revanced-discord-bot.git
synced 2025-05-03 07:44:26 +02:00
feat: embeds
This commit is contained in:
parent
57693d39ed
commit
4293dd5518
@ -38,9 +38,9 @@
|
|||||||
"$ref": "#/$defs/channels",
|
"$ref": "#/$defs/channels",
|
||||||
"description": "A list of channel ids. The bot will only introduce in threads under these channels."
|
"description": "A list of channel ids. The bot will only introduce in threads under these channels."
|
||||||
},
|
},
|
||||||
"message": {
|
"response": {
|
||||||
"$ref": "#/$defs/message",
|
"$ref": "#/$defs/response",
|
||||||
"description": "The message to send when the thread has been created."
|
"description": "The response to send when the thread has been created."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -91,9 +91,9 @@
|
|||||||
},
|
},
|
||||||
"description": "The conditions to respond to the message."
|
"description": "The conditions to respond to the message."
|
||||||
},
|
},
|
||||||
"message": {
|
"response": {
|
||||||
"$ref": "#/$defs/message",
|
"$ref": "#/$defs/response",
|
||||||
"description": "The message to send when the message is responded to."
|
"description": "The response to send when the message is responded to."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"description": "The conditions to respond to a message."
|
"description": "The conditions to respond to a message."
|
||||||
@ -128,8 +128,110 @@
|
|||||||
"uniqueItems": true,
|
"uniqueItems": true,
|
||||||
"minItems": 1
|
"minItems": 1
|
||||||
},
|
},
|
||||||
"message": {
|
"embed": {
|
||||||
"type": "string"
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"title": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The title of the embed."
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The description of the embed."
|
||||||
|
},
|
||||||
|
"color": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "The color of the embed."
|
||||||
|
},
|
||||||
|
"fields": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the field."
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The value of the field."
|
||||||
|
},
|
||||||
|
"inline": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether the field is inline."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": "The field to add to the embed."
|
||||||
|
},
|
||||||
|
"description": "The fields to add to the embed."
|
||||||
|
},
|
||||||
|
"image": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"url": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The url of the image."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": "The image to add to the embed."
|
||||||
|
},
|
||||||
|
"thumbnail": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"url": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The url of the thumbnail."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": "The thumbnail to add to the embed."
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the author."
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The url of the author."
|
||||||
|
},
|
||||||
|
"icon_url": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The url of the author's icon."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": "The author to add to the embed."
|
||||||
|
},
|
||||||
|
"footer": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"text": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The text of the footer."
|
||||||
|
},
|
||||||
|
"icon_url": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The url of the footer's icon."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": "The footer to add to the embed."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"message": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The message. Can be empty if the embed is not empty"
|
||||||
|
},
|
||||||
|
"embed": {
|
||||||
|
"$ref": "#/$defs/embed",
|
||||||
|
"description": "The embed to send."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ pub struct Administrators {
|
|||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Introduction {
|
pub struct Introduction {
|
||||||
pub channels: Vec<u64>,
|
pub channels: Vec<u64>,
|
||||||
pub message: String,
|
pub response: Response,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
@ -35,7 +35,64 @@ pub struct MessageResponder {
|
|||||||
pub includes: Includes,
|
pub includes: Includes,
|
||||||
pub excludes: Excludes,
|
pub excludes: Excludes,
|
||||||
pub condition: Condition,
|
pub condition: Condition,
|
||||||
pub message: String,
|
pub response: Response,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Response {
|
||||||
|
pub message: Option<String>,
|
||||||
|
pub embed: Option<Embed>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Embed {
|
||||||
|
pub title: String,
|
||||||
|
pub description: String,
|
||||||
|
pub color: i32,
|
||||||
|
pub fields: Vec<Field>,
|
||||||
|
pub footer: Footer,
|
||||||
|
pub image: Image,
|
||||||
|
pub thumbnail: Thumbnail,
|
||||||
|
pub author: Author,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Field {
|
||||||
|
pub name: String,
|
||||||
|
pub value: String,
|
||||||
|
pub inline: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Footer {
|
||||||
|
pub text: String,
|
||||||
|
#[serde(rename = "icon_url")]
|
||||||
|
pub icon_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Image {
|
||||||
|
pub url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Thumbnail {
|
||||||
|
pub url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Author {
|
||||||
|
pub name: String,
|
||||||
|
#[serde(rename = "icon_url")]
|
||||||
|
pub icon_url: String,
|
||||||
|
pub url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
|
119
src/main.rs
119
src/main.rs
@ -2,13 +2,13 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use chrono::{DateTime, Duration, NaiveDateTime, Utc};
|
use chrono::{DateTime, Duration, NaiveDateTime, Utc};
|
||||||
use configuration::BotConfiguration;
|
use configuration::BotConfiguration;
|
||||||
use log::{error, info, trace, warn, LevelFilter};
|
use log::{error, info, trace, LevelFilter};
|
||||||
use logger::logging::SimpleLogger;
|
use logger::logging::SimpleLogger;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serenity::client::{Context, EventHandler};
|
use serenity::client::{Context, EventHandler};
|
||||||
use serenity::model::application::command::Command;
|
|
||||||
use serenity::model::channel::{GuildChannel, Message};
|
use serenity::model::channel::{GuildChannel, Message};
|
||||||
use serenity::model::gateway::Ready;
|
use serenity::model::gateway::Ready;
|
||||||
|
use serenity::model::prelude::command::Command;
|
||||||
use serenity::model::prelude::interaction::{Interaction, InteractionResponseType, MessageFlags};
|
use serenity::model::prelude::interaction::{Interaction, InteractionResponseType, MessageFlags};
|
||||||
use serenity::prelude::{GatewayIntents, RwLock, TypeMapKey};
|
use serenity::prelude::{GatewayIntents, RwLock, TypeMapKey};
|
||||||
use serenity::{async_trait, Client};
|
use serenity::{async_trait, Client};
|
||||||
@ -47,34 +47,34 @@ impl EventHandler for Handler {
|
|||||||
async fn interaction_create(&self, ctx: Context, interaction: Interaction) {
|
async fn interaction_create(&self, ctx: Context, interaction: Interaction) {
|
||||||
trace!("Created an interaction: {:?}", interaction);
|
trace!("Created an interaction: {:?}", interaction);
|
||||||
|
|
||||||
let configuration_lock = get_configuration_lock(&ctx).await;
|
|
||||||
let mut configuration = configuration_lock.write().await;
|
|
||||||
|
|
||||||
if let Interaction::ApplicationCommand(command) = interaction {
|
if let Interaction::ApplicationCommand(command) = interaction {
|
||||||
let content = match command.data.name.as_str() {
|
let configuration_lock = get_configuration_lock(&ctx).await;
|
||||||
"reload" => {
|
let mut configuration = configuration_lock.write().await;
|
||||||
let member = command.member.as_ref().unwrap();
|
|
||||||
let user_id = member.user.id.0;
|
|
||||||
|
|
||||||
let administrators = &configuration.administrators;
|
let administrators = &configuration.administrators;
|
||||||
|
let member = command.member.as_ref().unwrap();
|
||||||
|
let user_id = member.user.id.0;
|
||||||
|
let mut stop_command = false;
|
||||||
|
let mut permission_granted = false;
|
||||||
|
|
||||||
let mut permission_granted = false;
|
// check if the user is an administrator
|
||||||
|
if administrators.users.iter().any(|&id| user_id == id) {
|
||||||
|
permission_granted = true
|
||||||
|
}
|
||||||
|
// check if the user has an administrating role
|
||||||
|
if !permission_granted
|
||||||
|
&& administrators
|
||||||
|
.roles
|
||||||
|
.iter()
|
||||||
|
.any(|role_id| member.roles.iter().any(|member_role| member_role == role_id))
|
||||||
|
{
|
||||||
|
permission_granted = true
|
||||||
|
}
|
||||||
|
|
||||||
// check if the user is an administrator
|
let content = if permission_granted {
|
||||||
if administrators.users.iter().any(|&id| user_id == id) {
|
match command.data.name.as_str() {
|
||||||
permission_granted = true
|
"reload" => {
|
||||||
}
|
trace!("{:?} reloaded the configuration.", command.user);
|
||||||
// check if the user has an administrating role
|
|
||||||
if !permission_granted
|
|
||||||
&& administrators.roles.iter().any(|role_id| {
|
|
||||||
member.roles.iter().any(|member_role| member_role == role_id)
|
|
||||||
}) {
|
|
||||||
permission_granted = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// if permission is granted, reload the configuration
|
|
||||||
if permission_granted {
|
|
||||||
trace!("{:?} reloading configuration.", command.user);
|
|
||||||
|
|
||||||
let new_config = load_configuration();
|
let new_config = load_configuration();
|
||||||
|
|
||||||
@ -83,12 +83,16 @@ impl EventHandler for Handler {
|
|||||||
configuration.thread_introductions = new_config.thread_introductions;
|
configuration.thread_introductions = new_config.thread_introductions;
|
||||||
|
|
||||||
"Successfully reloaded configuration.".to_string()
|
"Successfully reloaded configuration.".to_string()
|
||||||
} else {
|
},
|
||||||
// else return an error message
|
"stop" => {
|
||||||
"You do not have permission to use this command.".to_string()
|
trace!("{:?} stopped the bot.", command.user);
|
||||||
}
|
stop_command = true;
|
||||||
},
|
"Stopped the bot.".to_string()
|
||||||
_ => "Unknown command.".to_string(),
|
},
|
||||||
|
_ => "Unknown command.".to_string(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
"You do not have permission to use this command.".to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
// send the response
|
// send the response
|
||||||
@ -104,6 +108,10 @@ impl EventHandler for Handler {
|
|||||||
{
|
{
|
||||||
error!("Cannot respond to slash command: {}", why);
|
error!("Cannot respond to slash command: {}", why);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if stop_command {
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,7 +121,7 @@ impl EventHandler for Handler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(response) =
|
if let Some(responder) =
|
||||||
get_configuration_lock(&ctx).await.read().await.message_responders.iter().find(
|
get_configuration_lock(&ctx).await.read().await.message_responders.iter().find(
|
||||||
|&responder| {
|
|&responder| {
|
||||||
// check if the message was sent in a channel that is included in the responder
|
// check if the message was sent in a channel that is included in the responder
|
||||||
@ -126,7 +134,7 @@ impl EventHandler for Handler {
|
|||||||
&& contains_match(&responder.includes.match_field, &msg.content)
|
&& contains_match(&responder.includes.match_field, &msg.content)
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
let min_age = response.condition.user.server_age;
|
let min_age = responder.condition.user.server_age;
|
||||||
|
|
||||||
if min_age != 0 {
|
if min_age != 0 {
|
||||||
let joined_at = ctx
|
let joined_at = ctx
|
||||||
@ -146,7 +154,30 @@ impl EventHandler for Handler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.reply_ping(&ctx.http, &response.message)
|
msg.channel_id
|
||||||
|
.send_message(&ctx.http, |m| {
|
||||||
|
m.reference_message(&msg);
|
||||||
|
match &responder.response.embed {
|
||||||
|
Some(embed) => m.embed(|e| {
|
||||||
|
e.title(&embed.title)
|
||||||
|
.description(&embed.description)
|
||||||
|
.color(embed.color)
|
||||||
|
.fields(embed.fields.iter().map(|field| {
|
||||||
|
(field.name.clone(), field.value.clone(), field.inline)
|
||||||
|
}))
|
||||||
|
.footer(|f| {
|
||||||
|
f.text(&embed.footer.text);
|
||||||
|
f.icon_url(&embed.footer.icon_url)
|
||||||
|
})
|
||||||
|
.thumbnail(&embed.thumbnail.url)
|
||||||
|
.image(&embed.image.url)
|
||||||
|
.author(|a| {
|
||||||
|
a.name(&embed.author.name).icon_url(&embed.author.icon_url)
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
None => m.content(responder.response.message.as_ref().unwrap()),
|
||||||
|
}
|
||||||
|
})
|
||||||
.await
|
.await
|
||||||
.expect("Could not reply to message author.");
|
.expect("Could not reply to message author.");
|
||||||
}
|
}
|
||||||
@ -162,7 +193,9 @@ impl EventHandler for Handler {
|
|||||||
if let Some(introducer) = &configuration.thread_introductions.iter().find(|introducer| {
|
if let Some(introducer) = &configuration.thread_introductions.iter().find(|introducer| {
|
||||||
introducer.channels.iter().any(|channel_id| *channel_id == thread.parent_id.unwrap().0)
|
introducer.channels.iter().any(|channel_id| *channel_id == thread.parent_id.unwrap().0)
|
||||||
}) {
|
}) {
|
||||||
if let Err(why) = thread.say(&ctx.http, &introducer.message).await {
|
if let Err(why) =
|
||||||
|
thread.say(&ctx.http, &introducer.response.message.as_ref().unwrap()).await
|
||||||
|
{
|
||||||
error!("Error sending message: {:?}", why);
|
error!("Error sending message: {:?}", why);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -171,11 +204,15 @@ impl EventHandler for Handler {
|
|||||||
async fn ready(&self, ctx: Context, ready: Ready) {
|
async fn ready(&self, ctx: Context, ready: Ready) {
|
||||||
info!("Connected as {}", ready.user.name);
|
info!("Connected as {}", ready.user.name);
|
||||||
|
|
||||||
Command::create_global_application_command(&ctx.http, |command| {
|
for (cmd, description) in
|
||||||
command.name("reload").description("Reloads the configuration.")
|
[("repload", "Reloads the configuration."), ("stop", "Stop the Discord bot.")]
|
||||||
})
|
{
|
||||||
.await
|
Command::create_global_application_command(&ctx.http, |command| {
|
||||||
.expect("Could not create command.");
|
command.name(cmd).description(description)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.expect("Could not create command.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user