Checkpoint

This commit is contained in:
2026-03-07 19:27:40 -03:00
parent d0b5f86740
commit 21678239b5
50 changed files with 599 additions and 506 deletions
Generated
+1 -1
View File
@@ -2013,7 +2013,7 @@ checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
[[package]] [[package]]
name = "payphone" name = "payphone"
version = "4.4.35" version = "4.4.41"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "payphone" name = "payphone"
version = "4.4.35" version = "4.4.41"
edition = "2024" edition = "2024"
default-run = "payphone" default-run = "payphone"
+2
View File
@@ -0,0 +1,2 @@
group_imports = "StdExternalCrate"
imports_granularity = "Module"
+3 -1
View File
@@ -64,7 +64,9 @@
//! starting the server. You can also use the `--bind` argument to specify the address to bind the //! starting the server. You can also use the `--bind` argument to specify the address to bind the
//! server to, which defaults to `0.0.0.0:3000`. //! server to, which defaults to `0.0.0.0:3000`.
use std::{fmt, net::SocketAddr, str::FromStr}; use std::fmt;
use std::net::SocketAddr;
use std::str::FromStr;
use twilight_gateway::ShardId; use twilight_gateway::ShardId;
+8 -6
View File
@@ -1,15 +1,17 @@
use std::{io::Cursor, sync::Arc}; use std::io::Cursor;
use std::sync::Arc;
use color_thief::ColorFormat; use color_thief::ColorFormat;
use image::{GenericImageView, ImageReader}; use image::{GenericImageView, ImageReader};
use reqwest::header::USER_AGENT; use reqwest::header::USER_AGENT;
use rgb::Rgb; use rgb::Rgb;
use tokio::sync::{Semaphore, mpsc::Receiver}; use tokio::sync::Semaphore;
use tokio::sync::mpsc::Receiver;
use crate::{ use crate::constants::VERSION;
constants::VERSION, database::models::User, use crate::database::models::User;
discord::images::get_user_custom_avatar_url_or_none, state::State, use crate::discord::images::get_user_custom_avatar_url_or_none;
}; use crate::state::State;
pub struct AvatarColorFetchingRequest { pub struct AvatarColorFetchingRequest {
pub hash: String, pub hash: String,
+3 -4
View File
@@ -1,10 +1,9 @@
pub mod models; pub mod models;
use include_dir::{Dir, include_dir}; use include_dir::{Dir, include_dir};
use scylla::{ use scylla::client::session::Session;
client::{session::Session, session_builder::SessionBuilder}, use scylla::client::session_builder::SessionBuilder;
errors::ExecutionError, use scylla::errors::ExecutionError;
};
use thiserror::Error; use thiserror::Error;
use time::OffsetDateTime; use time::OffsetDateTime;
+18 -28
View File
@@ -1,37 +1,29 @@
use std::collections::HashSet; use std::collections::HashSet;
use scylla::{ use scylla::client::session::Session;
DeserializeValue, SerializeValue, use scylla::deserialize::row::DeserializeRow;
client::session::Session, use scylla::errors::{
deserialize::row::DeserializeRow, DeserializationError, ExecutionError, FirstRowError, IntoRowsResultError, RowsError,
errors::{ SingleRowError,
DeserializationError, ExecutionError, FirstRowError, IntoRowsResultError, RowsError,
SingleRowError,
},
response::query_result::QueryRowsResult,
statement::{
Statement,
batch::{Batch, BatchType},
},
}; };
use scylla::response::query_result::QueryRowsResult;
use scylla::statement::Statement;
use scylla::statement::batch::{Batch, BatchType};
use scylla::{DeserializeValue, SerializeValue};
use thiserror::Error; use thiserror::Error;
use time::{Duration, OffsetDateTime, UtcDateTime, UtcOffset}; use time::{Duration, OffsetDateTime, UtcDateTime, UtcOffset};
use tokio::sync::mpsc::Sender; use tokio::sync::mpsc::Sender;
use twilight_http::Client; use twilight_http::Client;
use twilight_model::{ use twilight_model::channel::{Message as TwilightMessage, Webhook};
channel::{Message as TwilightMessage, Webhook}, use twilight_model::guild::Guild as TwilightGuild;
guild::Guild as TwilightGuild, use twilight_model::id::Id;
id::Id, use twilight_model::user::User as TwilightUser;
user::User as TwilightUser,
};
use twilight_validate::request::{ValidationError, ValidationErrorType, webhook_username}; use twilight_validate::request::{ValidationError, ValidationErrorType, webhook_username};
use crate::{ use crate::colors::AvatarColorFetchingRequest;
colors::AvatarColorFetchingRequest, use crate::constants::DEFAULT_PREFIX;
constants::DEFAULT_PREFIX, use crate::groups::calls::masks::{get_mask_avatar_url, get_mask_display_name, get_random_mask};
groups::calls::masks::{get_mask_avatar_url, get_mask_display_name, get_random_mask}, use crate::utils::{Bye, get_random_u64, i64_to_u64, u64_to_i64};
utils::{Bye, get_random_u64, i64_to_u64, u64_to_i64},
};
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum DatabaseOrDiscordError { pub enum DatabaseOrDiscordError {
@@ -1346,9 +1338,7 @@ impl Message {
propagation_message_id = ? propagation_message_id = ?
LIMIT 1; LIMIT 1;
"#, "#,
( (u64_to_i64(propagation_message_id),),
u64_to_i64(propagation_message_id),
),
) )
.await? .await?
.into_rows_result()?; .into_rows_result()?;
+16 -16
View File
@@ -1,4 +1,6 @@
use std::{fmt::Debug, sync::Arc, time::Duration}; use std::fmt::Debug;
use std::sync::Arc;
use std::time::Duration;
use async_trait::async_trait; use async_trait::async_trait;
use thiserror::Error; use thiserror::Error;
@@ -8,22 +10,20 @@ use twilight_gateway::{ConfigBuilder, Event, EventTypeFlags, Intents, Shard, Sha
use twilight_http::Client; use twilight_http::Client;
use twilight_model::gateway::payload::outgoing::update_presence::UpdatePresencePayload; use twilight_model::gateway::payload::outgoing::update_presence::UpdatePresencePayload;
use crate::{ use crate::framework::commands::Command;
framework::{ use crate::framework::commands::extras::ExtrasGetter;
commands::{Command, extras::ExtrasGetter, runner::CommandRunner}, use crate::framework::commands::runner::CommandRunner;
events::traits::{ use crate::framework::events::traits::{
OnInteractionHandler, OnMessageDeleteHandler, OnMessageEditHandler, OnMessageHandler, OnInteractionHandler, OnMessageDeleteHandler, OnMessageEditHandler, OnMessageHandler,
OnReadyHandler, OnReconnectHandler, OnServerCreateHandler, OnServerDeleteHandler, OnReadyHandler, OnReconnectHandler, OnServerCreateHandler, OnServerDeleteHandler,
OnServerUpdateHandler, OnServerUpdateHandler,
},
interactions::{
components::Component, registry::ComponentRegistry, runner::InteractionRunner,
},
runtime::Runtime,
tasks::Task,
},
utils::Bye,
}; };
use crate::framework::interactions::components::Component;
use crate::framework::interactions::registry::ComponentRegistry;
use crate::framework::interactions::runner::InteractionRunner;
use crate::framework::runtime::Runtime;
use crate::framework::tasks::Task;
use crate::utils::Bye;
/// Errors occurred while attempting to run the bot. /// Errors occurred while attempting to run the bot.
#[derive(Debug, Error)] #[derive(Debug, Error)]
+4 -5
View File
@@ -1,11 +1,10 @@
use std::{ use std::fmt;
fmt, use std::ops::{Deref, DerefMut};
ops::{Deref, DerefMut},
};
use thiserror::Error; use thiserror::Error;
use crate::{database::models::DatabaseError, framework::commands::context::CommandContext}; use crate::database::models::DatabaseError;
use crate::framework::commands::context::CommandContext;
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum ArgumentParsingError { pub enum ArgumentParsingError {
+7 -8
View File
@@ -1,15 +1,14 @@
use std::sync::Arc; use std::sync::Arc;
use twilight_http::{Error, Response}; use twilight_http::{Error, Response};
use twilight_model::{ use twilight_model::channel::Message;
channel::{Message, message::Component}, use twilight_model::channel::message::Component;
gateway::payload::incoming::MessageCreate, use twilight_model::gateway::payload::incoming::MessageCreate;
};
use crate::framework::{ use crate::framework::commands::messages::MessageSender;
commands::messages::MessageSender, interactions::components::ActionRow, use crate::framework::interactions::components::ActionRow;
messages::MessageBuilder, runtime::Runtime, use crate::framework::messages::MessageBuilder;
}; use crate::framework::runtime::Runtime;
pub struct CommandContext<'a, T, E> { pub struct CommandContext<'a, T, E> {
pub event: &'a MessageCreate, pub event: &'a MessageCreate,
+12 -21
View File
@@ -1,27 +1,18 @@
use std::{ use std::future::{Future, IntoFuture};
future::{Future, IntoFuture}, use std::pin::Pin;
pin::Pin, use std::sync::Arc;
sync::Arc,
};
use twilight_http::{Error, Response, response::marker::EmptyBody}; use twilight_http::response::marker::EmptyBody;
use twilight_model::{ use twilight_http::{Error, Response};
channel::{ use twilight_model::channel::Message;
Message, use twilight_model::channel::message::{Component as TwilightComponent, Embed};
message::{Component as TwilightComponent, Embed}, use twilight_model::http::interaction::{InteractionResponse, InteractionResponseType};
}, use twilight_model::id::Id;
http::interaction::{InteractionResponse, InteractionResponseType}, use twilight_model::id::marker::{ChannelMarker, InteractionMarker};
id::{
Id,
marker::{ChannelMarker, InteractionMarker},
},
};
use twilight_util::builder::InteractionResponseDataBuilder; use twilight_util::builder::InteractionResponseDataBuilder;
use crate::framework::{ use crate::framework::interactions::components::{ActionRow, Component};
interactions::components::{ActionRow, Component}, use crate::framework::runtime::Runtime;
runtime::Runtime,
};
pub struct MessageSender<T, E> { pub struct MessageSender<T, E> {
channel_id: Id<ChannelMarker>, channel_id: Id<ChannelMarker>,
+7 -8
View File
@@ -1,17 +1,16 @@
use std::fmt::Debug; use std::fmt::Debug;
use async_trait::async_trait; use async_trait::async_trait;
use deadpool_redis::{cluster::PoolError, redis::RedisError}; use deadpool_redis::cluster::PoolError;
use deadpool_redis::redis::RedisError;
use thiserror::Error; use thiserror::Error;
use twilight_http::response::DeserializeBodyError; use twilight_http::response::DeserializeBodyError;
use crate::{ use crate::database::models::{DatabaseError, DatabaseOrDiscordError};
database::models::{DatabaseError, DatabaseOrDiscordError}, use crate::framework::commands::arguments::Argument;
framework::{ use crate::framework::commands::context::CommandContext;
commands::{arguments::Argument, context::CommandContext, permissions::Permission}, use crate::framework::commands::permissions::Permission;
runtime::Runtime, use crate::framework::runtime::Runtime;
},
};
pub mod arguments; pub mod arguments;
pub mod context; pub mod context;
+2 -1
View File
@@ -2,7 +2,8 @@ use std::fmt::Debug;
use async_trait::async_trait; use async_trait::async_trait;
use crate::framework::commands::{CommandError, context::CommandContext}; use crate::framework::commands::CommandError;
use crate::framework::commands::context::CommandContext;
#[async_trait] #[async_trait]
pub trait Permission<S: Send + Sync + 'static, E>: Debug { pub trait Permission<S: Send + Sync + 'static, E>: Debug {
+5 -5
View File
@@ -3,11 +3,11 @@ use std::sync::Arc;
use async_trait::async_trait; use async_trait::async_trait;
use twilight_model::gateway::payload::incoming::MessageCreate; use twilight_model::gateway::payload::incoming::MessageCreate;
use crate::framework::{ use crate::framework::commands::context::CommandContext;
commands::{context::CommandContext, extras::ExtrasGetter, get_command_parts}, use crate::framework::commands::extras::ExtrasGetter;
events::traits::{EventResult, OnMessageHandler}, use crate::framework::commands::get_command_parts;
runtime::Runtime, use crate::framework::events::traits::{EventResult, OnMessageHandler};
}; use crate::framework::runtime::Runtime;
#[derive(Debug)] #[derive(Debug)]
pub struct CommandRunner; pub struct CommandRunner;
+5 -2
View File
@@ -1,4 +1,6 @@
use std::{error::Error, fmt::Debug, sync::Arc}; use std::error::Error;
use std::fmt::Debug;
use std::sync::Arc;
use async_trait::async_trait; use async_trait::async_trait;
use twilight_model::gateway::payload::incoming::{ use twilight_model::gateway::payload::incoming::{
@@ -6,7 +8,8 @@ use twilight_model::gateway::payload::incoming::{
MessageUpdate, Ready, MessageUpdate, Ready,
}; };
use crate::framework::{commands::extras::ExtrasGetter, runtime::Runtime}; use crate::framework::commands::extras::ExtrasGetter;
use crate::framework::runtime::Runtime;
pub type EventResult = Result<(), Box<dyn Error>>; pub type EventResult = Result<(), Box<dyn Error>>;
+7 -5
View File
@@ -1,12 +1,14 @@
use std::{fmt::Debug, sync::Arc}; use std::fmt::Debug;
use std::sync::Arc;
use async_trait::async_trait; use async_trait::async_trait;
use twilight_model::channel::message::{ use twilight_model::channel::message::Component as TwilightComponent;
Component as TwilightComponent, use twilight_model::channel::message::component::{
component::{ActionRow as TwilightActionRow, Button as TwilightButton, ButtonStyle}, ActionRow as TwilightActionRow, Button as TwilightButton, ButtonStyle,
}; };
use crate::framework::interactions::{InteractionError, context::InteractionContext}; use crate::framework::interactions::InteractionError;
use crate::framework::interactions::context::InteractionContext;
type ComponentWithID<T, E> = (Option<String>, Arc<dyn Component<T, E>>); type ComponentWithID<T, E> = (Option<String>, Arc<dyn Component<T, E>>);
+8 -10
View File
@@ -1,17 +1,15 @@
use std::sync::Arc; use std::sync::Arc;
use twilight_http::{Error, Response, response::marker::EmptyBody}; use twilight_http::response::marker::EmptyBody;
use twilight_model::{ use twilight_http::{Error, Response};
gateway::payload::incoming::InteractionCreate, use twilight_model::gateway::payload::incoming::InteractionCreate;
http::interaction::{InteractionResponse, InteractionResponseType}, use twilight_model::http::interaction::{InteractionResponse, InteractionResponseType};
};
use twilight_util::builder::InteractionResponseDataBuilder; use twilight_util::builder::InteractionResponseDataBuilder;
use crate::framework::{ use crate::framework::interactions::InteractionError;
interactions::{InteractionError, components::ActionRow}, use crate::framework::interactions::components::ActionRow;
messages::MessageEditBuilder, use crate::framework::messages::MessageEditBuilder;
runtime::Runtime, use crate::framework::runtime::Runtime;
};
pub struct InteractionContext<T, E> { pub struct InteractionContext<T, E> {
pub event: InteractionCreate, pub event: InteractionCreate,
+6 -10
View File
@@ -1,18 +1,14 @@
use std::sync::Arc; use std::sync::Arc;
use scc::HashMap; use scc::HashMap;
use twilight_model::application::interaction::InteractionData;
use twilight_model::{ use twilight_model::gateway::payload::incoming::InteractionCreate;
application::interaction::InteractionData, gateway::payload::incoming::InteractionCreate,
};
use crate::framework::{
commands::extras::ExtrasGetter,
interactions::{InteractionError, context::InteractionContext},
runtime::Runtime,
};
use super::components::Component; use super::components::Component;
use crate::framework::commands::extras::ExtrasGetter;
use crate::framework::interactions::InteractionError;
use crate::framework::interactions::context::InteractionContext;
use crate::framework::runtime::Runtime;
#[derive(Default)] #[derive(Default)]
pub struct ComponentRegistry<T, E> { pub struct ComponentRegistry<T, E> {
+3 -5
View File
@@ -3,11 +3,9 @@ use std::sync::Arc;
use async_trait::async_trait; use async_trait::async_trait;
use twilight_model::gateway::payload::incoming::InteractionCreate; use twilight_model::gateway::payload::incoming::InteractionCreate;
use crate::framework::{ use crate::framework::commands::extras::ExtrasGetter;
commands::extras::ExtrasGetter, use crate::framework::events::traits::{EventResult, OnInteractionHandler};
events::traits::{EventResult, OnInteractionHandler}, use crate::framework::runtime::Runtime;
runtime::Runtime,
};
#[derive(Debug)] #[derive(Debug)]
pub struct InteractionRunner; pub struct InteractionRunner;
+5 -4
View File
@@ -22,11 +22,12 @@ use std::sync::Arc;
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use twilight_cache_inmemory::InMemoryCache; use twilight_cache_inmemory::InMemoryCache;
use twilight_http::Client; use twilight_http::Client;
use twilight_model::{gateway::payload::outgoing::UpdatePresence, oauth::Application}; use twilight_model::gateway::payload::outgoing::UpdatePresence;
use twilight_model::oauth::Application;
use crate::framework::{ use crate::framework::bot::PrefixGetter;
bot::PrefixGetter, commands::Command, interactions::registry::ComponentRegistry, use crate::framework::commands::Command;
}; use crate::framework::interactions::registry::ComponentRegistry;
/// The application's runtime tools. /// The application's runtime tools.
pub struct Runtime<T, E> { pub struct Runtime<T, E> {
+2 -1
View File
@@ -1,4 +1,5 @@
use std::{fmt::Debug, time::Duration}; use std::fmt::Debug;
use std::time::Duration;
use async_trait::async_trait; use async_trait::async_trait;
+58 -27
View File
@@ -1,29 +1,18 @@
use async_trait::async_trait; use async_trait::async_trait;
use crate::{ use crate::extras::Extras;
extras::Extras, use crate::framework::commands::arguments::{Arg, Argument, NeedyString, ParseArguments};
framework::{ use crate::framework::commands::context::CommandContext;
commands::{ use crate::framework::commands::{Command, CommandError};
Command, CommandError, use crate::framework::runtime::Runtime;
arguments::{Arg, Argument, NeedyString, ParseArguments}, use crate::groups::about::components::{
context::CommandContext, HelpDonateLinkButton, HelpGoBackButton, HelpSupportLinkButton, HelpWebsiteLinkButton,
},
runtime::Runtime,
},
groups::{
about::{
components::{
HelpDonateLinkButton, HelpGoBackButton, HelpSupportLinkButton,
HelpWebsiteLinkButton,
},
embeds::{
get_all_command_help_embed, get_command_help_embed, get_command_not_found_embed,
},
},
calls::components::CodeOfConductButton,
},
state::State,
}; };
use crate::groups::about::embeds::{
get_all_command_help_embed, get_command_help_embed, get_command_not_found_embed,
};
use crate::groups::calls::components::CodeOfConductButton;
use crate::state::State;
#[derive(Debug)] #[derive(Debug)]
pub struct HelpCommand; pub struct HelpCommand;
@@ -83,12 +72,20 @@ impl Command<State, Extras> for HelpCommand {
} }
#[derive(Debug)] #[derive(Debug)]
pub struct TetoCommand; pub struct CodeOfConductCommand;
#[async_trait] #[async_trait]
impl Command<State, Extras> for TetoCommand { impl Command<State, Extras> for CodeOfConductCommand {
fn name(&self) -> &'static str { fn name(&self) -> &'static str {
"teto" "codeofconduct"
}
fn aliases(&self) -> &'static [&'static str] {
&["coc"]
}
fn description(&self) -> &'static str {
"Shows a link to Payphone's Code of Conduct."
} }
fn show_in_help(&self, _rt: &Runtime<State, Extras>, _extras: &Extras) -> bool { fn show_in_help(&self, _rt: &Runtime<State, Extras>, _extras: &Extras) -> bool {
@@ -100,7 +97,41 @@ impl Command<State, Extras> for TetoCommand {
ctx: CommandContext<'_, State, Extras>, ctx: CommandContext<'_, State, Extras>,
_args: &str, _args: &str,
) -> Result<(), CommandError> { ) -> Result<(), CommandError> {
ctx.send().content("GLORY TO TETOSLAVIA").await?; ctx.send()
.content("https://payphone.lat/code-of-conduct")
.await?;
Ok(())
}
}
#[derive(Debug)]
pub struct DonateCommand;
#[async_trait]
impl Command<State, Extras> for DonateCommand {
fn name(&self) -> &'static str {
"donate"
}
fn aliases(&self) -> &'static [&'static str] {
&["d"]
}
fn description(&self) -> &'static str {
"Shows a link to Payphone's Ko-Fi page."
}
fn show_in_help(&self, _rt: &Runtime<State, Extras>, _extras: &Extras) -> bool {
false
}
async fn execute(
&self,
ctx: CommandContext<'_, State, Extras>,
_args: &str,
) -> Result<(), CommandError> {
ctx.send().content("https://ko-fi.com/payphonedev").await?;
Ok(()) Ok(())
} }
+7 -9
View File
@@ -1,14 +1,12 @@
use async_trait::async_trait; use async_trait::async_trait;
use crate::{ use crate::extras::Extras;
extras::Extras, use crate::framework::interactions::InteractionError;
framework::{ use crate::framework::interactions::components::Button;
interactions::{InteractionError, components::Button, context::InteractionContext}, use crate::framework::interactions::context::InteractionContext;
messages::MessageEditBuilder, use crate::framework::messages::MessageEditBuilder;
}, use crate::groups::about::embeds::get_all_command_help_embed;
groups::about::embeds::get_all_command_help_embed, use crate::state::State;
state::State,
};
#[derive(Debug)] #[derive(Debug)]
pub struct HelpGoBackButton; pub struct HelpGoBackButton;
+9 -11
View File
@@ -5,17 +5,15 @@ use twilight_util::builder::embed::{
EmbedAuthorBuilder, EmbedBuilder, EmbedFieldBuilder, EmbedFooterBuilder, ImageSource, EmbedAuthorBuilder, EmbedBuilder, EmbedFieldBuilder, EmbedFooterBuilder, ImageSource,
}; };
use crate::{ use crate::constants::{COLOR_RED, COLOR_WHITE, VERSION};
constants::{COLOR_RED, COLOR_WHITE, VERSION}, use crate::discord::images;
discord::{images, sanitizing::SanitizeDiscordMarkdown}, use crate::discord::sanitizing::SanitizeDiscordMarkdown;
extras::Extras, use crate::extras::Extras;
framework::{ use crate::framework::commands::Command;
commands::{Command, context::CommandContext}, use crate::framework::commands::context::CommandContext;
runtime::Runtime, use crate::framework::runtime::Runtime;
}, use crate::state::State;
state::State, use crate::utils::ToTitleCase;
utils::ToTitleCase,
};
pub(super) fn get_command_help_embed( pub(super) fn get_command_help_embed(
command: &Arc<dyn Command<State, Extras>>, command: &Arc<dyn Command<State, Extras>>,
+7 -9
View File
@@ -1,16 +1,14 @@
use async_trait::async_trait; use async_trait::async_trait;
use rand::{Rng, SeedableRng}; use rand::{Rng, SeedableRng};
use rand_chacha::ChaCha20Rng; use rand_chacha::ChaCha20Rng;
use twilight_model::gateway::{ use twilight_model::gateway::payload::outgoing::UpdatePresence;
payload::outgoing::{UpdatePresence, update_presence::UpdatePresencePayload}, use twilight_model::gateway::payload::outgoing::update_presence::UpdatePresencePayload;
presence::{Activity, ActivityType, Status}, use twilight_model::gateway::presence::{Activity, ActivityType, Status};
};
use crate::{ use crate::extras::Extras;
extras::Extras, use crate::framework::runtime::Runtime;
framework::{runtime::Runtime, tasks::Task}, use crate::framework::tasks::Task;
state::State, use crate::state::State;
};
#[derive(Debug)] #[derive(Debug)]
pub struct UpdatePresenceTask; pub struct UpdatePresenceTask;
+44 -46
View File
@@ -4,28 +4,24 @@ use scc::HashSet;
use time::UtcDateTime; use time::UtcDateTime;
use twilight_model::id::Id; use twilight_model::id::Id;
use crate::{ use crate::database::models::{Call, Channel, Participant};
database::models::{Call, Channel, Participant}, use crate::discord::sanitizing::SanitizeDiscordMarkdown;
discord::sanitizing::SanitizeDiscordMarkdown, use crate::extras::Extras;
extras::Extras, use crate::framework::commands::arguments::{Arg, Argument, ParseArguments};
framework::commands::{ use crate::framework::commands::context::CommandContext;
Command, CommandError, use crate::framework::commands::permissions::Permission;
arguments::{Arg, Argument, ParseArguments}, use crate::framework::commands::{Command, CommandError};
context::CommandContext, use crate::groups::calls::masks::{get_mask_display_name, get_mask_emoji};
permissions::Permission, use crate::groups::calls::permissions::{
}, IsCallParticipantPermission, NonNewAccountPermission, NotBannedPermission,
groups::calls::{
masks::{get_mask_display_name, get_mask_emoji},
permissions::{NonNewAccountPermission, NotBannedPermission},
strings::{
get_string_call_answered, get_string_call_hangup_other_party,
get_string_call_hangup_this_party, get_string_call_no_answer,
get_string_call_searching, get_string_tips,
},
},
state::State,
utils::Bye,
}; };
use crate::groups::calls::strings::{
get_string_call_answered, get_string_call_hangup_other_party,
get_string_call_hangup_this_party, get_string_call_no_answer, get_string_call_searching,
get_string_tips,
};
use crate::state::State;
use crate::utils::Bye;
#[derive(Default, Debug)] #[derive(Default, Debug)]
pub struct CallCommand { pub struct CallCommand {
@@ -172,8 +168,9 @@ impl Command<State, Extras> for SkipCommand {
fn permissions(&self) -> Vec<Box<dyn Permission<State, Extras> + Send + Sync + 'static>> { fn permissions(&self) -> Vec<Box<dyn Permission<State, Extras> + Send + Sync + 'static>> {
vec![ vec![
Box::new(NotBannedPermission),
Box::new(NonNewAccountPermission), Box::new(NonNewAccountPermission),
Box::new(NotBannedPermission),
Box::new(IsCallParticipantPermission),
] ]
} }
@@ -318,8 +315,9 @@ impl Command<State, Extras> for HangupCommand {
fn permissions(&self) -> Vec<Box<dyn Permission<State, Extras> + Send + Sync + 'static>> { fn permissions(&self) -> Vec<Box<dyn Permission<State, Extras> + Send + Sync + 'static>> {
vec![ vec![
Box::new(NotBannedPermission),
Box::new(NonNewAccountPermission), Box::new(NonNewAccountPermission),
Box::new(NotBannedPermission),
Box::new(IsCallParticipantPermission),
] ]
} }
@@ -442,8 +440,9 @@ impl Command<State, Extras> for FriendRequestCommand {
fn permissions(&self) -> Vec<Box<dyn Permission<State, Extras> + Send + Sync + 'static>> { fn permissions(&self) -> Vec<Box<dyn Permission<State, Extras> + Send + Sync + 'static>> {
vec![ vec![
Box::new(NotBannedPermission),
Box::new(NonNewAccountPermission), Box::new(NonNewAccountPermission),
Box::new(NotBannedPermission),
Box::new(IsCallParticipantPermission),
] ]
} }
@@ -598,29 +597,28 @@ impl Command<State, Extras> for MaskCommand {
mask_status, ctx.prefix, ctx.prefix mask_status, ctx.prefix, ctx.prefix
); );
if let Some(channel) = channel { if let Some(channel) = channel
if let Some(call_id) = channel.call_id { && let Some(call_id) = channel.call_id
if let Some(mut participant) = Participant::get_for_user_and_channel_in_call( {
ctx.extras.user.id, if let Some(mut participant) = Participant::get_for_user_and_channel_in_call(
ctx.event.channel_id.get(), ctx.extras.user.id,
call_id, ctx.event.channel_id.get(),
&ctx.rt.state.db, call_id,
) &ctx.rt.state.db,
.await? )
{ .await?
participant.is_masked = ctx.extras.user.is_masked; {
participant.upsert(&ctx.rt.state.db).await?; participant.is_masked = ctx.extras.user.is_masked;
participant.upsert(&ctx.rt.state.db).await?;
if ctx.extras.user.is_masked { if ctx.extras.user.is_masked {
mask_message = format!( mask_message = format!(
"🎭 Your mask {mask_verb} set to `on`. You're currently being displayed as **{} {}**. You can toggle your mask on or off using `{}mask on` and `{}mask off`.", "🎭 Your mask {mask_verb} set to `on`. You're currently being displayed as **{} {}**. You can toggle your mask on or off using `{}mask on` and `{}mask off`.",
get_mask_emoji(&participant.user_mask) get_mask_emoji(&participant.user_mask).expect("User's mask did not exist"),
.expect("User's mask did not exist"), get_mask_display_name(&participant.user_mask),
get_mask_display_name(&participant.user_mask), ctx.prefix,
ctx.prefix, ctx.prefix
ctx.prefix );
);
}
} }
} }
} }
+3 -1
View File
@@ -1,4 +1,6 @@
use crate::{extras::Extras, framework::interactions::components::Button, state::State}; use crate::extras::Extras;
use crate::framework::interactions::components::Button;
use crate::state::State;
#[derive(Debug)] #[derive(Debug)]
pub struct CodeOfConductButton; pub struct CodeOfConductButton;
+7 -8
View File
@@ -1,17 +1,16 @@
use std::sync::Arc; use std::sync::Arc;
use twilight_model::{channel::message::Embed, oauth::Application}; use twilight_model::channel::message::Embed;
use twilight_model::oauth::Application;
use twilight_util::builder::embed::{ use twilight_util::builder::embed::{
EmbedAuthorBuilder, EmbedBuilder, EmbedFieldBuilder, EmbedFooterBuilder, ImageSource, EmbedAuthorBuilder, EmbedBuilder, EmbedFieldBuilder, EmbedFooterBuilder, ImageSource,
}; };
use crate::{ use crate::constants::{COLOR_RED, COLOR_YELLOW};
constants::{COLOR_RED, COLOR_YELLOW}, use crate::database::models::Message;
database::models::Message, use crate::discord::images;
discord::images, use crate::groups::calls::moderation::Rule;
groups::calls::moderation::Rule, use crate::utils::ellipse;
utils::ellipse,
};
pub fn first_warning_embed( pub fn first_warning_embed(
app: &Application, app: &Application,
+68 -49
View File
@@ -1,41 +1,36 @@
use std::{error::Error, sync::Arc}; use std::error::Error;
use std::sync::Arc;
use async_trait::async_trait; use async_trait::async_trait;
use deadpool_redis::redis::AsyncTypedCommands; use deadpool_redis::redis::AsyncTypedCommands;
use itertools::Itertools; use itertools::Itertools;
use time::{Duration, UtcDateTime}; use time::{Duration, UtcDateTime};
use twilight_http::{api_error::ApiError, error::ErrorType}; use twilight_http::api_error::ApiError;
use twilight_model::{ use twilight_http::error::ErrorType;
gateway::payload::incoming::{MessageCreate, MessageDelete, MessageUpdate}, use twilight_model::gateway::payload::incoming::{MessageCreate, MessageDelete, MessageUpdate};
id::Id, use twilight_model::id::Id;
}; use twilight_util::builder::embed::{
use twilight_util::{ EmbedAuthorBuilder, EmbedBuilder, EmbedFooterBuilder, ImageSource,
builder::embed::{EmbedAuthorBuilder, EmbedBuilder, EmbedFooterBuilder, ImageSource},
snowflake::Snowflake,
}; };
use twilight_util::snowflake::Snowflake;
use crate::{ use crate::constants::{COLOR_WHITE, COLOR_YELLOW};
constants::{COLOR_WHITE, COLOR_YELLOW}, use crate::database::models::{Call, Channel, Message, Participant, User, UserIncident};
database::models::{Call, Channel, Message, Participant, User, UserIncident}, use crate::discord::images;
discord::{images, sanitizing::SanitizeDiscordMarkdown}, use crate::discord::sanitizing::SanitizeDiscordMarkdown;
extras::Extras, use crate::extras::Extras;
framework::{ use crate::framework::commands::extras::ExtrasGetter;
commands::extras::ExtrasGetter, use crate::framework::events::traits::{
events::traits::{ EventResult, OnMessageDeleteHandler, OnMessageEditHandler, OnMessageHandler,
EventResult, OnMessageDeleteHandler, OnMessageEditHandler, OnMessageHandler,
},
interactions::components::ActionRow,
runtime::Runtime,
},
groups::calls::{
components::{CodeOfConductButton, NeedHelpButton},
embeds,
masks::{get_mask_avatar_url, get_mask_display_name},
moderation::{Action, Rule, escape_message, moderate_message},
},
state::State,
utils::{ellipse, i64_to_u64},
}; };
use crate::framework::interactions::components::ActionRow;
use crate::framework::runtime::Runtime;
use crate::groups::calls::components::{CodeOfConductButton, NeedHelpButton};
use crate::groups::calls::embeds;
use crate::groups::calls::masks::{get_mask_avatar_url, get_mask_display_name};
use crate::groups::calls::moderation::{Action, Rule, escape_message, moderate_message};
use crate::state::State;
use crate::utils::{ellipse, i64_to_u64};
async fn penalize_user( async fn penalize_user(
user: User, user: User,
@@ -349,15 +344,35 @@ impl OnMessageHandler<State, Extras> for CallMessageHandler {
) )
}; };
// Replied-to links are always
// .../{other_party_server_id}/{other_party_channel_id}/{other_party_message_id}
//
// When the message being replied to was sent by the other party, the
// `other_party_message_id` is the original message ID, given it was sent from
// their channel so the original is in their channel.
//
// When the message being replied to was sent by this party, the
// `other_party_message_id` is the propagation message ID given the other party
// will have seen the propagated message instead.
let message_link = if i64_to_u64(propagation_channel_id) == channel.id { let message_link = if i64_to_u64(propagation_channel_id) == channel.id {
// if the message replied-to was sent by the other party
//
// replied_to.message_id -> other party's channel id
// replied_to.propagation_channel_id -> this channel's ID
// replied_to.propagation_message_id -> this channel's message ID
format!( format!(
"https://discord.com/channels/{}/{}/{}", "https://discord.com/channels/{}/{}/{}",
opposite_channel.server_id, opposite_channel.id, replying_to.message_id opposite_channel.server_id, opposite_channel.id, replying_to.message_id
) )
} else { } else {
// if the message replied-to was sent by this party
//
// replied_to.message_id -> this channel's message ID
// replied_to.propagation_channel_id -> other party's channel id
// replied_to.propagation_message_id -> other party's message ID
format!( format!(
"https://discord.com/channels/{}/{}/{}", "https://discord.com/channels/{}/{}/{}",
channel.server_id, propagation_channel_id, propagation_message_id opposite_channel.server_id, propagation_channel_id, propagation_message_id
) )
}; };
@@ -377,7 +392,10 @@ impl OnMessageHandler<State, Extras> for CallMessageHandler {
.and_then(|i| i.color.map(|c| c as u32)) .and_then(|i| i.color.map(|c| c as u32))
.unwrap_or(COLOR_WHITE) .unwrap_or(COLOR_WHITE)
}) })
.description(ellipse(moderate_message(&replying_to.data.content, false).0, 100)) .description(ellipse(
moderate_message(&replying_to.data.content, false).0,
100,
))
.build(), .build(),
); );
} }
@@ -440,12 +458,13 @@ impl OnMessageHandler<State, Extras> for CallMessageHandler {
error: ApiError::General(error), error: ApiError::General(error),
.. ..
} = e.kind() } = e.kind()
&& error.code == 10015 { && error.code == 10015
log::debug!("Webhook not found, updating..."); {
opposite_channel log::debug!("Webhook not found, updating...");
.update_webhook(&rt.state.db, &rt.http) opposite_channel
.await?; .update_webhook(&rt.state.db, &rt.http)
} .await?;
}
return Ok(()); return Ok(());
} }
} }
@@ -626,19 +645,19 @@ impl OnMessageDeleteHandler<State, Extras> for CallMessageDeleteHandler {
&& let Some((webhook_id, webhook_token)) = opposite_channel && let Some((webhook_id, webhook_token)) = opposite_channel
.webhook_id .webhook_id
.zip(opposite_channel.webhook_token) .zip(opposite_channel.webhook_token)
{ {
rt.http rt.http
.delete_webhook_message( .delete_webhook_message(
Id::new(webhook_id), Id::new(webhook_id),
&webhook_token, &webhook_token,
Id::new(i64_to_u64(propagation_message_id)), Id::new(i64_to_u64(propagation_message_id)),
) )
.await?; .await?;
message.data.is_deleted = true; message.data.is_deleted = true;
message.insert(&rt.state.db).await?; message.insert(&rt.state.db).await?;
} }
Ok(()) Ok(())
} }
@@ -79,6 +79,9 @@ goonbud
goonbuds goonbuds
goonete goonete
goonetes goonetes
gooner
goonr
goner
boty hole boty hole
suck my pp suck my pp
ass cracker ass cracker
@@ -95,6 +98,10 @@ masturbating
masterbating masterbating
masterbaiting masterbaiting
masturbatigng masturbatigng
masterbaiter
mastrbaiter
mastrbait
mastrbate
horny horny
hrny hrny
whore / who re whore / who re
@@ -116,6 +123,8 @@ eat my bum
eat mi bum eat mi bum
ima touch you ima touch you
ima touch u ima touch u
i ma touch you
i ma touch u
il touch you il touch you
il touch u il touch u
im going to touch you im going to touch you
@@ -125,4 +134,6 @@ im gonna touch u
nude nude
nudes nudes
nde nde
ndes ndes
nood
noods
+3 -1
View File
@@ -83,7 +83,9 @@
//! //!
//! All of which would be detected. //! All of which would be detected.
use std::{collections::HashMap, fmt::Debug, sync::Arc}; use std::collections::HashMap;
use std::fmt::Debug;
use std::sync::Arc;
use itertools::Itertools; use itertools::Itertools;
use linkify::LinkFinder; use linkify::LinkFinder;
+54 -7
View File
@@ -2,13 +2,13 @@ use async_trait::async_trait;
use time::{Duration, UtcDateTime}; use time::{Duration, UtcDateTime};
use twilight_util::snowflake::Snowflake; use twilight_util::snowflake::Snowflake;
use crate::{ use crate::database::models::{Call, Channel, Participant, UserIncident};
database::models::UserIncident, use crate::extras::Extras;
extras::Extras, use crate::framework::commands::CommandError;
framework::commands::{CommandError, context::CommandContext, permissions::Permission}, use crate::framework::commands::context::CommandContext;
groups::calls::components::{CodeOfConductButton, NeedHelpButton}, use crate::framework::commands::permissions::Permission;
state::State, use crate::groups::calls::components::{CodeOfConductButton, NeedHelpButton};
}; use crate::state::State;
#[derive(Debug)] #[derive(Debug)]
pub struct NotBannedPermission; pub struct NotBannedPermission;
@@ -72,3 +72,50 @@ impl Permission<State, Extras> for NonNewAccountPermission {
Ok(()) Ok(())
} }
} }
/// Requires users to have sent a message in the call to be able to use the command.
///
/// This is to prevent abuse of commands by lurkers in the call, as well as to prevent
/// non-participants from skipping or ending calls they're not in.
#[derive(Debug)]
pub struct IsCallParticipantPermission;
#[async_trait]
impl Permission<State, Extras> for IsCallParticipantPermission {
async fn check(&self, ctx: &CommandContext<State, Extras>) -> Result<bool, CommandError> {
let channel = match Channel::get_by_id(ctx.event.channel_id.get(), &ctx.rt.state.db).await?
{
Some(channel) => channel,
None => return Ok(true), // The channel isn't in the database, so it can't be in call.
};
let call_id = match channel.call_id {
Some(call_id) => call_id,
None => return Ok(true), // The channel isn't in a call, so the user can use commands in it.
};
let participants = Participant::get_for_call(call_id, &ctx.rt.state.db).await?;
for participant in participants {
if participant.user_id == ctx.event.author.id.get() {
return Ok(true);
}
}
return Ok(false);
}
async fn on_handle_denied(
&self,
ctx: &CommandContext<State, Extras>,
) -> Result<(), CommandError> {
ctx.send()
.content("❌ You haven't sent any messages on this call. You cannot run this command.")
.await
.inspect_err(|e| {
log::error!("Failed to send message: {e}");
})?;
Ok(())
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use rand::{SeedableRng, seq::IndexedRandom}; use rand::SeedableRng;
use rand::seq::IndexedRandom;
use rand_chacha::ChaCha20Rng; use rand_chacha::ChaCha20Rng;
static STRINGS_TIPS: Lazy<Vec<String>> = Lazy::new(|| { static STRINGS_TIPS: Lazy<Vec<String>> = Lazy::new(|| {
+5 -6
View File
@@ -3,12 +3,11 @@ use deadpool_redis::redis::AsyncTypedCommands;
use time::{Duration, UtcDateTime}; use time::{Duration, UtcDateTime};
use twilight_model::id::Id; use twilight_model::id::Id;
use crate::{ use crate::database::models::Call;
database::models::Call, use crate::extras::Extras;
extras::Extras, use crate::framework::runtime::Runtime;
framework::{runtime::Runtime, tasks::Task}, use crate::framework::tasks::Task;
state::State, use crate::state::State;
};
#[derive(Debug)] #[derive(Debug)]
pub struct EndInactiveCallsTask { pub struct EndInactiveCallsTask {
+4 -8
View File
@@ -5,14 +5,10 @@ use std::sync::Arc;
use async_trait::async_trait; use async_trait::async_trait;
use twilight_model::gateway::payload::incoming::Ready; use twilight_model::gateway::payload::incoming::Ready;
use crate::{ use crate::extras::Extras;
extras::Extras, use crate::framework::events::traits::{EventResult, OnReadyHandler, OnReconnectHandler};
framework::{ use crate::framework::runtime::Runtime;
events::traits::{EventResult, OnReadyHandler, OnReconnectHandler}, use crate::state::State;
runtime::Runtime,
},
state::State,
};
/// Logs a ready message when the bot is ready. /// Logs a ready message when the bot is ready.
#[derive(Debug)] #[derive(Debug)]
+37 -21
View File
@@ -5,15 +5,16 @@ use twilight_util::builder::embed::{
EmbedAuthorBuilder, EmbedBuilder, EmbedFooterBuilder, ImageSource, EmbedAuthorBuilder, EmbedBuilder, EmbedFooterBuilder, ImageSource,
}; };
use crate::{ use crate::constants::COLOR_WHITE;
constants::COLOR_WHITE, use crate::database::models::User;
database::models::User, use crate::discord::images;
discord::{images, sanitizing::SanitizeDiscordMarkdown}, use crate::discord::sanitizing::SanitizeDiscordMarkdown;
extras::Extras, use crate::extras::Extras;
framework::commands::{Command, CommandError, context::CommandContext}, use crate::framework::commands::context::CommandContext;
groups::leveling::constants::KEY_LEADERBOARD_MONTHLY, use crate::framework::commands::{Command, CommandError};
state::State, use crate::groups::leveling::constants::KEY_LEADERBOARD_MONTHLY;
}; use crate::state::State;
use crate::utils::format_thousands;
#[derive(Debug)] #[derive(Debug)]
pub struct LeaderboardCommand; pub struct LeaderboardCommand;
@@ -48,6 +49,12 @@ impl Command<State, Extras> for LeaderboardCommand {
let current_user_rank: Option<usize> = redis let current_user_rank: Option<usize> = redis
.zrevrank(KEY_LEADERBOARD_MONTHLY, ctx.event.author.id.get()) .zrevrank(KEY_LEADERBOARD_MONTHLY, ctx.event.author.id.get())
.await?; .await?;
let current_user_xp: Option<f64> = redis
.zscore(KEY_LEADERBOARD_MONTHLY, ctx.event.author.id.get())
.await?;
let current_user_xp = current_user_xp.map(|xp| xp as u64);
let current_user_rank_and_xp = current_user_rank.zip(current_user_xp);
let mut users = Vec::new(); let mut users = Vec::new();
@@ -57,15 +64,15 @@ impl Command<State, Extras> for LeaderboardCommand {
continue; continue;
}; };
if let Some(is_banned_from_leaderboard) = user.is_banned_from_leaderboard { if let Some(is_banned_from_leaderboard) = user.is_banned_from_leaderboard
if is_banned_from_leaderboard { && is_banned_from_leaderboard
log::trace!( {
"Skipping user {} (ID: {}) because they are banned from the leaderboard.", log::trace!(
user.name, "Skipping user {} (ID: {}) because they are banned from the leaderboard.",
user.id user.name,
); user.id
continue; );
} continue;
} }
users.push((user, score as u32)); users.push((user, score as u32));
@@ -76,15 +83,24 @@ impl Command<State, Extras> for LeaderboardCommand {
.iter() .iter()
.enumerate() .enumerate()
.map(|(i, (user, xp))| { .map(|(i, (user, xp))| {
format!("#{}: **{}** - {} XP", i + 1, user.name.sanitize(true), xp) format!(
"#{}: **{}** - {} XP",
i + 1,
user.name.sanitize(true),
format_thousands(*xp as u64)
)
}) })
.join("\n") .join("\n")
} else { } else {
"*Nobody earned XP this month yet.*".to_string() "*Nobody earned XP this month yet.*".to_string()
}; };
let footer = if let Some(rank) = current_user_rank { let footer = if let Some((rank, xp)) = current_user_rank_and_xp {
format!("Your current rank: #{}", rank + 1) format!(
"Your current rank: #{} - {} XP",
rank + 1,
format_thousands(xp)
)
} else { } else {
"You haven't earned XP this month yet.".to_string() "You haven't earned XP this month yet.".to_string()
}; };
+7 -11
View File
@@ -7,17 +7,13 @@ use rand_chacha::ChaCha20Rng;
use time::{Date, Month, OffsetDateTime, Time}; use time::{Date, Month, OffsetDateTime, Time};
use twilight_model::gateway::payload::incoming::MessageCreate; use twilight_model::gateway::payload::incoming::MessageCreate;
use crate::{ use crate::database::models::Channel;
database::models::Channel, use crate::extras::Extras;
extras::Extras, use crate::framework::commands::extras::ExtrasGetter;
framework::{ use crate::framework::events::traits::{EventResult, OnMessageHandler};
commands::extras::ExtrasGetter, use crate::framework::runtime::Runtime;
events::traits::{EventResult, OnMessageHandler}, use crate::groups::leveling::constants::KEY_LEADERBOARD_MONTHLY;
runtime::Runtime, use crate::state::State;
},
groups::leveling::constants::KEY_LEADERBOARD_MONTHLY,
state::State,
};
fn seconds_until_end_of_month() -> i64 { fn seconds_until_end_of_month() -> i64 {
let now = OffsetDateTime::now_utc(); let now = OffsetDateTime::now_utc();
+26 -15
View File
@@ -1,20 +1,14 @@
use std::{ use std::ops::{Deref, DerefMut};
ops::{Deref, DerefMut}, use std::time::Duration;
time::Duration,
};
use scylla::client::session::Session; use scylla::client::session::Session;
use crate::{ use crate::database::models::{Message, User};
database::models::{Message, User}, use crate::extras::Extras;
extras::Extras, use crate::framework::commands::arguments::{ArgumentParsingError, FromArguments, NeedyString};
framework::commands::{ use crate::framework::commands::context::CommandContext;
arguments::{ArgumentParsingError, FromArguments, NeedyString}, use crate::state::State;
context::CommandContext, use crate::utils::split_at_nth_from_end;
},
state::State,
utils::split_at_nth_from_end,
};
/// Fetch a user by their ID from the database. /// Fetch a user by their ID from the database.
/// ///
@@ -110,7 +104,24 @@ impl FromArguments<State, Extras> for UserArgument {
.parse() .parse()
.map_err(|_| ArgumentParsingError::InvalidArgument)?; .map_err(|_| ArgumentParsingError::InvalidArgument)?;
user = get_user_by_id(user_id, &ctx.rt.state.db).await?; match get_user_by_id(user_id, &ctx.rt.state.db).await {
Ok(result) => user = result,
Err(error) => {
if matches!(error, ArgumentParsingError::InvalidArgument) {
let message =
Message::get_by_propagation_message_id(user_id, &ctx.rt.state.db)
.await?;
if let Some(message) = message {
user = get_user_by_id(message.author_id, &ctx.rt.state.db).await?;
} else {
return Err(ArgumentParsingError::InvalidArgument);
}
} else {
return Err(error);
}
}
}
} else { } else {
// The argument is (probably) a username. // The argument is (probably) a username.
+13 -19
View File
@@ -1,26 +1,20 @@
use async_trait::async_trait; use async_trait::async_trait;
use time::UtcDateTime; use time::UtcDateTime;
use crate::{ use crate::database::models::{UserIncident, UserIncidentType};
database::models::{UserIncident, UserIncidentType}, use crate::extras::Extras;
extras::Extras, use crate::framework::commands::arguments::{
framework::{ Arg, Argument, ArgumentParsingError, NeedyGreedyString, ParseArguments,
commands::{
Command, CommandError,
arguments::{Arg, Argument, ArgumentParsingError, NeedyGreedyString, ParseArguments},
context::CommandContext,
permissions::Permission,
},
runtime::Runtime,
},
groups::moderation::{
arguments::{DurationArgument, UserArgument},
get_incident_page,
permissions::{BotModeratorPermission, BotOwnerPermission},
},
state::State,
utils::get_random_u64,
}; };
use crate::framework::commands::context::CommandContext;
use crate::framework::commands::permissions::Permission;
use crate::framework::commands::{Command, CommandError};
use crate::framework::runtime::Runtime;
use crate::groups::moderation::arguments::{DurationArgument, UserArgument};
use crate::groups::moderation::get_incident_page;
use crate::groups::moderation::permissions::{BotModeratorPermission, BotOwnerPermission};
use crate::state::State;
use crate::utils::get_random_u64;
#[derive(Debug)] #[derive(Debug)]
pub struct ModeratorCommand; pub struct ModeratorCommand;
+8 -8
View File
@@ -1,14 +1,14 @@
use async_trait::async_trait; use async_trait::async_trait;
use twilight_model::channel::message::component::ButtonStyle; use twilight_model::channel::message::component::ButtonStyle;
use crate::{ use crate::database::models::UserIncident;
database::models::UserIncident, use crate::extras::Extras;
extras::Extras, use crate::framework::interactions::InteractionError;
framework::interactions::{InteractionError, components::Button, context::InteractionContext}, use crate::framework::interactions::components::Button;
groups::moderation::get_incident_page, use crate::framework::interactions::context::InteractionContext;
state::State, use crate::groups::moderation::get_incident_page;
utils::get_random_u64, use crate::state::State;
}; use crate::utils::get_random_u64;
#[derive(Debug)] #[derive(Debug)]
pub struct ArrowIncidentButton { pub struct ArrowIncidentButton {
+3 -5
View File
@@ -4,11 +4,9 @@ use twilight_util::builder::embed::{
EmbedAuthorBuilder, EmbedBuilder, EmbedFieldBuilder, ImageSource, EmbedAuthorBuilder, EmbedBuilder, EmbedFieldBuilder, ImageSource,
}; };
use crate::{ use crate::constants::{COLOR_RED, COLOR_YELLOW};
constants::{COLOR_RED, COLOR_YELLOW}, use crate::database::models::{UserIncident, UserIncidentType};
database::models::{UserIncident, UserIncidentType}, use crate::utils::ellipse;
utils::ellipse,
};
fn format_timestamp(timestamp: UtcDateTime) -> String { fn format_timestamp(timestamp: UtcDateTime) -> String {
format!( format!(
+7 -11
View File
@@ -1,17 +1,13 @@
use scylla::client::session::Session; use scylla::client::session::Session;
use crate::{ use crate::database::models::{DatabaseError, UserIncident, UserIncidentType};
database::models::{DatabaseError, UserIncident, UserIncidentType}, use crate::extras::Extras;
extras::Extras, use crate::framework::messages::MessageBuilder;
framework::messages::MessageBuilder, use crate::groups::moderation::components::{
groups::moderation::{ ArrowIncidentButton, CounterButton, DeleteIncidentButton, ExpireIncidentButton,
components::{
ArrowIncidentButton, CounterButton, DeleteIncidentButton, ExpireIncidentButton,
},
embeds::incident_embed,
},
state::State,
}; };
use crate::groups::moderation::embeds::incident_embed;
use crate::state::State;
pub mod arguments; pub mod arguments;
pub mod commands; pub mod commands;
+5 -5
View File
@@ -1,10 +1,10 @@
use async_trait::async_trait; use async_trait::async_trait;
use crate::{ use crate::extras::Extras;
extras::Extras, use crate::framework::commands::CommandError;
framework::commands::{CommandError, context::CommandContext, permissions::Permission}, use crate::framework::commands::context::CommandContext;
state::State, use crate::framework::commands::permissions::Permission;
}; use crate::state::State;
#[derive(Debug)] #[derive(Debug)]
pub struct BotOwnerPermission; pub struct BotOwnerPermission;
+10 -14
View File
@@ -1,19 +1,15 @@
use async_trait::async_trait; use async_trait::async_trait;
use crate::{ use crate::constants::DEFAULT_PREFIX;
constants::DEFAULT_PREFIX, use crate::database::models::Server;
database::models::Server, use crate::discord::sanitizing::SanitizeDiscordMarkdown;
discord::sanitizing::SanitizeDiscordMarkdown, use crate::extras::Extras;
extras::Extras, use crate::framework::commands::arguments::{Arg, Argument, GreedyString, ParseArguments};
framework::commands::{ use crate::framework::commands::context::CommandContext;
Command, CommandError, use crate::framework::commands::permissions::Permission;
arguments::{Arg, Argument, GreedyString, ParseArguments}, use crate::framework::commands::{Command, CommandError};
context::CommandContext, use crate::groups::servers::permissions::{GuildOnlyPermission, ServerModeratorPermission};
permissions::Permission, use crate::state::State;
},
groups::servers::permissions::{GuildOnlyPermission, ServerModeratorPermission},
state::State,
};
#[derive(Debug)] #[derive(Debug)]
pub struct PrefixCommand; pub struct PrefixCommand;
+6 -10
View File
@@ -5,17 +5,13 @@ use std::sync::Arc;
use async_trait::async_trait; use async_trait::async_trait;
use twilight_model::gateway::payload::incoming::{GuildCreate, GuildDelete, GuildUpdate}; use twilight_model::gateway::payload::incoming::{GuildCreate, GuildDelete, GuildUpdate};
use crate::{ use crate::database::models::Server;
database::models::Server, use crate::extras::Extras;
extras::Extras, use crate::framework::events::traits::{
framework::{ EventResult, OnServerCreateHandler, OnServerDeleteHandler, OnServerUpdateHandler,
events::traits::{
EventResult, OnServerCreateHandler, OnServerDeleteHandler, OnServerUpdateHandler,
},
runtime::Runtime,
},
state::State,
}; };
use crate::framework::runtime::Runtime;
use crate::state::State;
/// Adds servers to the DB when they get created. /// Adds servers to the DB when they get created.
#[derive(Debug)] #[derive(Debug)]
+8 -9
View File
@@ -1,15 +1,14 @@
use async_trait::async_trait; use async_trait::async_trait;
use twilight_model::{ use twilight_model::guild::Permissions;
guild::Permissions, use twilight_model::id::Id;
id::{Id, marker::RoleMarker}, use twilight_model::id::marker::RoleMarker;
};
use twilight_util::permission_calculator::PermissionCalculator; use twilight_util::permission_calculator::PermissionCalculator;
use crate::{ use crate::extras::Extras;
extras::Extras, use crate::framework::commands::CommandError;
framework::commands::{CommandError, context::CommandContext, permissions::Permission}, use crate::framework::commands::context::CommandContext;
state::State, use crate::framework::commands::permissions::Permission;
}; use crate::state::State;
#[derive(Debug)] #[derive(Debug)]
pub struct GuildOnlyPermission; pub struct GuildOnlyPermission;
+2 -1
View File
@@ -39,7 +39,8 @@
use colored::Colorize; use colored::Colorize;
use log::{Level, LevelFilter}; use log::{Level, LevelFilter};
use time::{OffsetDateTime, macros::format_description}; use time::OffsetDateTime;
use time::macros::format_description;
pub struct Logger; pub struct Logger;
+35 -47
View File
@@ -100,62 +100,49 @@
//! $ cargo run -- docs --open //! $ cargo run -- docs --open
//! ``` //! ```
use std::{ use std::error::Error;
error::Error, use std::net::{IpAddr, Ipv4Addr, SocketAddr};
net::{IpAddr, Ipv4Addr, SocketAddr}, use std::path::Path;
path::Path, use std::process::Command;
process::Command, use std::sync::Arc;
sync::Arc,
};
use async_trait::async_trait; use async_trait::async_trait;
use axum::{Router, response::Redirect, routing}; use axum::response::Redirect;
use axum::{Router, routing};
use clap::Parser; use clap::Parser;
use notify::{Event, EventKind, Watcher}; use notify::{Event, EventKind, Watcher};
use tokio::net::TcpListener; use tokio::net::TcpListener;
use tower_http::services::ServeDir; use tower_http::services::ServeDir;
use twilight_cache_inmemory::ResourceType; use twilight_cache_inmemory::ResourceType;
use twilight_gateway::{Intents, ShardId}; use twilight_gateway::{Intents, ShardId};
use twilight_model::{ use twilight_model::user::User as TwilightUser;
gateway::{
payload::outgoing::update_presence::UpdatePresencePayload,
presence::{Activity, ActivityType, Status},
},
user::User as TwilightUser,
};
use crate::{ use crate::cli::{Args, Commands};
cli::{Args, Commands}, use crate::colors::init_color_fetching_queue_listener;
colors::init_color_fetching_queue_listener, use crate::constants::{DEFAULT_PREFIX, VERSION};
constants::{DEFAULT_PREFIX, VERSION}, use crate::database::models::{Server, User};
database::models::{Server, User}, use crate::extras::Extras;
extras::Extras, use crate::framework::bot::{Bot, PrefixGetter};
framework::{ use crate::framework::commands::extras::ExtrasGetter;
bot::{Bot, PrefixGetter}, use crate::framework::runtime::Runtime;
commands::extras::ExtrasGetter, use crate::groups::about::commands::{CodeOfConductCommand, DonateCommand, HelpCommand};
runtime::Runtime, use crate::groups::about::components::HelpGoBackButton;
}, use crate::groups::about::tasks::UpdatePresenceTask;
groups::{ use crate::groups::calls::commands::{
about::{ CallCommand, FriendRequestCommand, HangupCommand, MaskCommand, SkipCommand,
commands::{HelpCommand, TetoCommand},
components::HelpGoBackButton, tasks::UpdatePresenceTask,
},
calls::{
commands::{
CallCommand, FriendRequestCommand, HangupCommand, MaskCommand, SkipCommand,
},
events::{CallMessageDeleteHandler, CallMessageEditHandler, CallMessageHandler},
},
gateway::events::{ReadyHandler, ReconnectHandler},
leveling::{commands::LeaderboardCommand, events::MessageLevelingHandler},
moderation::commands::{BanCommand, IncidentsCommand, ModeratorCommand},
servers::{
commands::PrefixCommand,
events::{ServerCreateHandler, ServerDeleteHandler, ServerUpdateHandler},
},
},
state::State,
}; };
use crate::groups::calls::events::{
CallMessageDeleteHandler, CallMessageEditHandler, CallMessageHandler,
};
use crate::groups::gateway::events::{ReadyHandler, ReconnectHandler};
use crate::groups::leveling::commands::LeaderboardCommand;
use crate::groups::leveling::events::MessageLevelingHandler;
use crate::groups::moderation::commands::{BanCommand, IncidentsCommand, ModeratorCommand};
use crate::groups::servers::commands::PrefixCommand;
use crate::groups::servers::events::{
ServerCreateHandler, ServerDeleteHandler, ServerUpdateHandler,
};
use crate::state::State;
mod cli; mod cli;
mod colors; mod colors;
@@ -299,7 +286,8 @@ async fn start(
.intent(Intents::GUILD_MESSAGES) .intent(Intents::GUILD_MESSAGES)
.intent(Intents::MESSAGE_CONTENT) .intent(Intents::MESSAGE_CONTENT)
.command(HelpCommand) .command(HelpCommand)
.command(TetoCommand) .command(CodeOfConductCommand)
.command(DonateCommand)
.command(CallCommand::new()) .command(CallCommand::new())
.command(SkipCommand) .command(SkipCommand)
.command(HangupCommand) .command(HangupCommand)
+2 -4
View File
@@ -1,7 +1,5 @@
use deadpool_redis::{ use deadpool_redis::Runtime;
Runtime, use deadpool_redis::cluster::{Config, CreatePoolError, Pool};
cluster::{Config, CreatePoolError, Pool},
};
pub fn init(urls: Vec<String>) -> Result<Pool, CreatePoolError> { pub fn init(urls: Vec<String>) -> Result<Pool, CreatePoolError> {
Config::from_urls(urls).create_pool(Some(Runtime::Tokio1)) Config::from_urls(urls).create_pool(Some(Runtime::Tokio1))
+22
View File
@@ -133,3 +133,25 @@ pub fn split_at_nth_from_end<'a, 'b>(
None None
} }
/// Formats a number with commas as thousands separators.
///
/// E.g. `123456789` becomes `123,456,789`.
///
/// Arguments:
/// * `num` - The number to format.
///
/// Returns:
/// [`String`] - The formatted number as a string.
pub fn format_thousands(num: u64) -> String {
let mut num_str = num.to_string();
let mut result = String::new();
while num_str.len() > 3 {
let chunk = &num_str[num_str.len() - 3..];
result = format!(",{chunk}{result}");
num_str.truncate(num_str.len() - 3);
}
format!("{}{}", num_str, result)
}