chore(fmt): change to default rustfmt config (#12)

This commit is contained in:
Oskar 2022-07-09 23:53:23 +02:00 committed by GitHub
parent ecafabde54
commit 607638b99f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 288 additions and 280 deletions

View File

@ -1,16 +0,0 @@
edition = "2018"
match_block_trailing_comma = true
newline_style = "Unix"
use_field_init_shorthand = true
use_small_heuristics = "Max"
use_try_shorthand = true
hard_tabs = true
format_code_in_doc_comments = true
group_imports = "StdExternalCrate"
imports_granularity = "Module"
imports_layout = "HorizontalVertical"
match_arm_blocks = true
normalize_comments = true
overflow_delimited_expr = true
struct_lit_single_line = false

View File

@ -1,4 +1,4 @@
pub fn init() { pub fn init() {
// TODO: log to file // TODO: log to file
tracing_subscriber::fmt::init(); tracing_subscriber::fmt::init();
} }

View File

@ -19,240 +19,259 @@ mod model;
struct BotConfiguration; struct BotConfiguration;
impl TypeMapKey for BotConfiguration { impl TypeMapKey for BotConfiguration {
type Value = Arc<RwLock<Configuration>>; type Value = Arc<RwLock<Configuration>>;
} }
pub struct Handler; pub struct Handler;
async fn get_configuration_lock(ctx: &Context) -> Arc<RwLock<Configuration>> { async fn get_configuration_lock(ctx: &Context) -> Arc<RwLock<Configuration>> {
ctx.data ctx.data
.read() .read()
.await .await
.get::<BotConfiguration>() .get::<BotConfiguration>()
.expect("Expected Configuration in TypeMap.") .expect("Expected Configuration in TypeMap.")
.clone() .clone()
} }
fn contains_match(regex: &[Regex], text: &str) -> bool { fn contains_match(regex: &[Regex], text: &str) -> bool {
regex.iter().any(|r| r.is_match(text)) regex.iter().any(|r| r.is_match(text))
} }
fn load_configuration() -> Configuration { fn load_configuration() -> Configuration {
Configuration::load().expect("Failed to load configuration") Configuration::load().expect("Failed to load configuration")
} }
#[async_trait] #[async_trait]
impl EventHandler for Handler { impl EventHandler for Handler {
async fn interaction_create(&self, ctx: Context, interaction: Interaction) { async fn interaction_create(&self, ctx: Context, interaction: Interaction) {
debug!("Created an interaction: {:?}", interaction); debug!("Created an interaction: {:?}", interaction);
if let Interaction::ApplicationCommand(command) = interaction { if let Interaction::ApplicationCommand(command) = interaction {
let configuration_lock = get_configuration_lock(&ctx).await; let configuration_lock = get_configuration_lock(&ctx).await;
let mut configuration = configuration_lock.write().await; let mut configuration = configuration_lock.write().await;
let administrators = &configuration.administrators; let administrators = &configuration.administrators;
let member = command.member.as_ref().unwrap(); let member = command.member.as_ref().unwrap();
let user_id = member.user.id.0; let user_id = member.user.id.0;
let mut stop_command = false; let mut stop_command = false;
let mut permission_granted = false; let mut permission_granted = false;
// check if the user is an administrator // check if the user is an administrator
if administrators.users.iter().any(|&id| user_id == id) { if administrators.users.iter().any(|&id| user_id == id) {
permission_granted = true permission_granted = true
} }
// check if the user has an administrating role // check if the user has an administrating role
if !permission_granted if !permission_granted
&& administrators && administrators.roles.iter().any(|role_id| {
.roles member
.iter() .roles
.any(|role_id| member.roles.iter().any(|member_role| member_role == role_id)) .iter()
{ .any(|member_role| member_role == role_id)
permission_granted = true })
} {
permission_granted = true
}
let content = if permission_granted { let content = if permission_granted {
match command.data.name.as_str() { match command.data.name.as_str() {
"reload" => { "reload" => {
debug!("{:?} reloaded the configuration.", command.user); debug!("{:?} reloaded the configuration.", command.user);
let new_config = load_configuration(); let new_config = load_configuration();
configuration.administrators = new_config.administrators; configuration.administrators = new_config.administrators;
configuration.message_responses = new_config.message_responses; configuration.message_responses = new_config.message_responses;
configuration.thread_introductions = new_config.thread_introductions; configuration.thread_introductions = new_config.thread_introductions;
"Successfully reloaded configuration.".to_string() "Successfully reloaded configuration.".to_string()
}, }
"stop" => { "stop" => {
debug!("{:?} stopped the bot.", command.user); debug!("{:?} stopped the bot.", command.user);
stop_command = true; stop_command = true;
"Stopped the bot.".to_string() "Stopped the bot.".to_string()
}, }
_ => "Unknown command.".to_string(), _ => "Unknown command.".to_string(),
} }
} else { } else {
"You do not have permission to use this command.".to_string() "You do not have permission to use this command.".to_string()
}; };
// send the response // send the response
if let Err(why) = command if let Err(why) = command
.create_interaction_response(&ctx.http, |response| { .create_interaction_response(&ctx.http, |response| {
response response
.kind(InteractionResponseType::ChannelMessageWithSource) .kind(InteractionResponseType::ChannelMessageWithSource)
.interaction_response_data(|message| { .interaction_response_data(|message| {
message.content(content).flags(MessageFlags::EPHEMERAL) message.content(content).flags(MessageFlags::EPHEMERAL)
}) })
}) })
.await .await
{ {
error!("Cannot respond to slash command: {}", why); error!("Cannot respond to slash command: {}", why);
} }
if stop_command { if stop_command {
process::exit(0); process::exit(0);
} }
} }
} }
async fn message(&self, ctx: Context, msg: Message) { async fn message(&self, ctx: Context, msg: Message) {
debug!("Received message: {}", msg.content); debug!("Received message: {}", msg.content);
if msg.guild_id.is_none() || msg.author.bot { if msg.guild_id.is_none() || msg.author.bot {
return; return;
} }
if let Some(message_response) = if let Some(message_response) = get_configuration_lock(&ctx)
get_configuration_lock(&ctx).await.read().await.message_responses.iter().find( .await
|&response| { .read()
// check if the message was sent in a channel that is included in the responder .await
response.includes.channels.iter().any(|&channel_id| channel_id == msg.channel_id.0) .message_responses
// check if the message was sent by a user that is not excluded from the responder .iter()
&& !response.excludes.roles.iter().any(|&role_id| role_id == msg.author.id.0) .find(|&response| {
// check if the message does not match any of the excludes // check if the message was sent in a channel that is included in the responder
&& !contains_match(&response.excludes.match_field, &msg.content) response.includes.channels.iter().any(|&channel_id| channel_id == msg.channel_id.0)
// check if the message matches any of the includes // check if the message was sent by a user that is not excluded from the responder
&& contains_match(&response.includes.match_field, &msg.content) && !response.excludes.roles.iter().any(|&role_id| role_id == msg.author.id.0)
}, // check if the message does not match any of the excludes
) { && !contains_match(&response.excludes.match_field, &msg.content)
let min_age = message_response.condition.user.server_age; // check if the message matches any of the includes
&& contains_match(&response.includes.match_field, &msg.content)
})
{
let min_age = message_response.condition.user.server_age;
if min_age != 0 { if min_age != 0 {
let joined_at = ctx let joined_at = ctx
.http .http
.get_member(msg.guild_id.unwrap().0, msg.author.id.0) .get_member(msg.guild_id.unwrap().0, msg.author.id.0)
.await .await
.unwrap() .unwrap()
.joined_at .joined_at
.unwrap() .unwrap()
.unix_timestamp(); .unix_timestamp();
let must_joined_at = let must_joined_at =
DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(joined_at, 0), Utc); DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(joined_at, 0), Utc);
let but_joined_at = Utc::now() - Duration::days(min_age); let but_joined_at = Utc::now() - Duration::days(min_age);
if must_joined_at <= but_joined_at { if must_joined_at <= but_joined_at {
return; return;
} }
msg.channel_id msg.channel_id
.send_message(&ctx.http, |m| { .send_message(&ctx.http, |m| {
m.reference_message(&msg); m.reference_message(&msg);
match &message_response.response.embed { match &message_response.response.embed {
Some(embed) => m.embed(|e| { Some(embed) => m.embed(|e| {
e.title(&embed.title) e.title(&embed.title)
.description(&embed.description) .description(&embed.description)
.color(embed.color) .color(embed.color)
.fields(embed.fields.iter().map(|field| { .fields(embed.fields.iter().map(|field| {
(field.name.clone(), field.value.clone(), field.inline) (field.name.clone(), field.value.clone(), field.inline)
})) }))
.footer(|f| { .footer(|f| {
f.text(&embed.footer.text); f.text(&embed.footer.text);
f.icon_url(&embed.footer.icon_url) f.icon_url(&embed.footer.icon_url)
}) })
.thumbnail(&embed.thumbnail.url) .thumbnail(&embed.thumbnail.url)
.image(&embed.image.url) .image(&embed.image.url)
.author(|a| { .author(|a| {
a.name(&embed.author.name).icon_url(&embed.author.icon_url) a.name(&embed.author.name).icon_url(&embed.author.icon_url)
}) })
}), }),
None => m.content(message_response.response.message.as_ref().unwrap()), None => m.content(message_response.response.message.as_ref().unwrap()),
} }
}) })
.await .await
.expect("Could not reply to message author."); .expect("Could not reply to message author.");
} }
} }
} }
async fn thread_create(&self, ctx: Context, thread: GuildChannel) { async fn thread_create(&self, ctx: Context, thread: GuildChannel) {
if thread.member.is_some() { if thread.member.is_some() {
debug!("Thread was joined. Block dispatch."); debug!("Thread was joined. Block dispatch.");
return; return;
} }
debug!("Thread created: {:?}", thread); debug!("Thread created: {:?}", thread);
let configuration_lock = get_configuration_lock(&ctx).await; let configuration_lock = get_configuration_lock(&ctx).await;
let configuration = configuration_lock.read().await; let configuration = configuration_lock.read().await;
if let Some(introducer) = &configuration.thread_introductions.iter().find(|introducer| { if let Some(introducer) = &configuration
introducer.channels.iter().any(|channel_id| *channel_id == thread.parent_id.unwrap().0) .thread_introductions
}) { .iter()
if let Err(why) = .find(|introducer| {
thread.say(&ctx.http, &introducer.response.message.as_ref().unwrap()).await introducer
{ .channels
error!("Error sending message: {:?}", why); .iter()
} .any(|channel_id| *channel_id == thread.parent_id.unwrap().0)
} })
} {
if let Err(why) = thread
.say(&ctx.http, &introducer.response.message.as_ref().unwrap())
.await
{
error!("Error sending message: {:?}", why);
}
}
}
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);
for (cmd, description) in for (cmd, description) in [
[("repload", "Reloads the configuration."), ("stop", "Stop the Discord bot.")] ("repload", "Reloads the configuration."),
{ ("stop", "Stop the Discord bot."),
Command::create_global_application_command(&ctx.http, |command| { ] {
command.name(cmd).description(description) Command::create_global_application_command(&ctx.http, |command| {
}) command.name(cmd).description(description)
.await })
.expect("Could not create command."); .await
} .expect("Could not create command.");
} }
}
} }
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
// Initialize the logging framework. // Initialize the logging framework.
logger::init(); logger::init();
// Set up the configuration. // Set up the configuration.
let configuration = load_configuration(); let configuration = load_configuration();
// Load environment variables from .env file // Load environment variables from .env file
dotenv::dotenv().ok(); dotenv::dotenv().ok();
// Get the Discord authorization token. // Get the Discord authorization token.
let token = env::var("DISCORD_AUTHORIZATION_TOKEN") let token = env::var("DISCORD_AUTHORIZATION_TOKEN")
.expect("Could not load Discord authorization token"); .expect("Could not load Discord authorization token");
if token.len() != 70 { if token.len() != 70 {
error!("Invalid Discord authorization token."); error!("Invalid Discord authorization token.");
process::exit(1); process::exit(1);
} }
// Create the Discord bot client. // Create the Discord bot client.
let mut client = Client::builder( let mut client = Client::builder(
&token, &token,
GatewayIntents::GUILDS | GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT, GatewayIntents::GUILDS | GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT,
) )
.event_handler(Handler) .event_handler(Handler)
.await .await
.expect("Failed to create client"); .expect("Failed to create client");
// Save the configuration. // Save the configuration.
client.data.write().await.insert::<BotConfiguration>(Arc::new(RwLock::new(configuration))); client
.data
.write()
.await
.insert::<BotConfiguration>(Arc::new(RwLock::new(configuration)));
// Start the Discord bot. // Start the Discord bot.
client.start().await.expect("failed to start discord bot"); client.start().await.expect("failed to start discord bot");
info!("Client started."); info!("Client started.");
} }

View File

@ -1,7 +1,7 @@
use std::{ use std::{
fs::{self, File}, fs::{self, File},
io::{Read, Result, Write}, io::{Read, Result, Write},
path::Path, path::Path,
}; };
use dirs::config_dir; use dirs::config_dir;
@ -10,142 +10,147 @@ use serde::{Deserialize, Serialize};
#[derive(Default, Serialize, Deserialize)] #[derive(Default, Serialize, Deserialize)]
pub struct Configuration { pub struct Configuration {
pub administrators: Administrators, pub administrators: Administrators,
pub thread_introductions: Vec<Introduction>, pub thread_introductions: Vec<Introduction>,
pub message_responses: Vec<MessageResponse>, pub message_responses: Vec<MessageResponse>,
} }
const CONFIG_PATH: &str = "configuration.json"; const CONFIG_PATH: &str = "configuration.json";
impl Configuration { impl Configuration {
fn save(&self) -> Result<()> { fn save(&self) -> Result<()> {
let sys_config_dir = config_dir().expect("find config dir"); let sys_config_dir = config_dir().expect("find config dir");
fs::create_dir_all(format!("{}/revanced-discord-bot", sys_config_dir.to_string_lossy())) fs::create_dir_all(format!(
.expect("create config dir"); "{}/revanced-discord-bot",
sys_config_dir.to_string_lossy()
))
.expect("create config dir");
let mut file = File::create(CONFIG_PATH)?; let mut file = File::create(CONFIG_PATH)?;
let json = serde_json::to_string_pretty(&self)?; let json = serde_json::to_string_pretty(&self)?;
file.write_all(json.as_bytes())?; file.write_all(json.as_bytes())?;
Ok(()) Ok(())
} }
pub fn load() -> Result<Configuration> { pub fn load() -> Result<Configuration> {
let sys_config_dir = config_dir().expect("Can not find the configuration directory."); let sys_config_dir = config_dir().expect("Can not find the configuration directory.");
let sys_config = let sys_config = format!(
format!("{}/revanced-discord-bot/{CONFIG_PATH}", sys_config_dir.to_string_lossy()); "{}/revanced-discord-bot/{CONFIG_PATH}",
sys_config_dir.to_string_lossy()
);
// config file in current dir // config file in current dir
let mut file = if Path::new(CONFIG_PATH).exists() { let mut file = if Path::new(CONFIG_PATH).exists() {
File::open(CONFIG_PATH)? File::open(CONFIG_PATH)?
} }
// config file in system dir (on *nix: `~/.config/revanced-discord-bot/`) // config file in system dir (on *nix: `~/.config/revanced-discord-bot/`)
else if Path::new(&sys_config).exists() { else if Path::new(&sys_config).exists() {
File::open(sys_config)? File::open(sys_config)?
} }
// create defalt config // create defalt config
else { else {
let default_config = Configuration::default(); let default_config = Configuration::default();
default_config.save()?; default_config.save()?;
File::open(sys_config)? File::open(sys_config)?
}; };
let mut buf = String::new(); let mut buf = String::new();
file.read_to_string(&mut buf)?; file.read_to_string(&mut buf)?;
Ok(serde_json::from_str(&buf)?) Ok(serde_json::from_str(&buf)?)
} }
} }
#[derive(Default, Serialize, Deserialize)] #[derive(Default, Serialize, Deserialize)]
pub struct Administrators { pub struct Administrators {
pub roles: Vec<u64>, pub roles: Vec<u64>,
pub users: Vec<u64>, pub users: Vec<u64>,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Introduction { pub struct Introduction {
pub channels: Vec<u64>, pub channels: Vec<u64>,
pub response: Response, pub response: Response,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct MessageResponse { pub struct MessageResponse {
pub includes: Includes, pub includes: Includes,
pub excludes: Excludes, pub excludes: Excludes,
pub condition: Condition, pub condition: Condition,
pub response: Response, pub response: Response,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Response { pub struct Response {
pub message: Option<String>, pub message: Option<String>,
pub embed: Option<Embed>, pub embed: Option<Embed>,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Embed { pub struct Embed {
pub title: String, pub title: String,
pub description: String, pub description: String,
pub color: i32, pub color: i32,
pub fields: Vec<Field>, pub fields: Vec<Field>,
pub footer: Footer, pub footer: Footer,
pub image: Image, pub image: Image,
pub thumbnail: Thumbnail, pub thumbnail: Thumbnail,
pub author: Author, pub author: Author,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Field { pub struct Field {
pub name: String, pub name: String,
pub value: String, pub value: String,
pub inline: bool, pub inline: bool,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Footer { pub struct Footer {
pub text: String, pub text: String,
pub icon_url: String, pub icon_url: String,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Image { pub struct Image {
pub url: String, pub url: String,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Thumbnail { pub struct Thumbnail {
pub url: String, pub url: String,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Author { pub struct Author {
pub name: String, pub name: String,
pub icon_url: String, pub icon_url: String,
pub url: String, pub url: String,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Includes { pub struct Includes {
pub channels: Vec<u64>, pub channels: Vec<u64>,
#[serde(rename = "match", with = "serde_regex")] #[serde(rename = "match", with = "serde_regex")]
pub match_field: Vec<Regex>, pub match_field: Vec<Regex>,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Excludes { pub struct Excludes {
pub roles: Vec<u64>, pub roles: Vec<u64>,
#[serde(rename = "match", with = "serde_regex")] #[serde(rename = "match", with = "serde_regex")]
pub match_field: Vec<Regex>, pub match_field: Vec<Regex>,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Condition { pub struct Condition {
pub user: User, pub user: User,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct User { pub struct User {
pub server_age: i64, pub server_age: i64,
} }