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
+58 -27
View File
@@ -1,29 +1,18 @@
use async_trait::async_trait;
use crate::{
extras::Extras,
framework::{
commands::{
Command, CommandError,
arguments::{Arg, Argument, NeedyString, ParseArguments},
context::CommandContext,
},
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::extras::Extras;
use crate::framework::commands::arguments::{Arg, Argument, NeedyString, ParseArguments};
use crate::framework::commands::context::CommandContext;
use crate::framework::commands::{Command, CommandError};
use crate::framework::runtime::Runtime;
use crate::groups::about::components::{
HelpDonateLinkButton, HelpGoBackButton, HelpSupportLinkButton, HelpWebsiteLinkButton,
};
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)]
pub struct HelpCommand;
@@ -83,12 +72,20 @@ impl Command<State, Extras> for HelpCommand {
}
#[derive(Debug)]
pub struct TetoCommand;
pub struct CodeOfConductCommand;
#[async_trait]
impl Command<State, Extras> for TetoCommand {
impl Command<State, Extras> for CodeOfConductCommand {
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 {
@@ -100,7 +97,41 @@ impl Command<State, Extras> for TetoCommand {
ctx: CommandContext<'_, State, Extras>,
_args: &str,
) -> 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(())
}
+7 -9
View File
@@ -1,14 +1,12 @@
use async_trait::async_trait;
use crate::{
extras::Extras,
framework::{
interactions::{InteractionError, components::Button, context::InteractionContext},
messages::MessageEditBuilder,
},
groups::about::embeds::get_all_command_help_embed,
state::State,
};
use crate::extras::Extras;
use crate::framework::interactions::InteractionError;
use crate::framework::interactions::components::Button;
use crate::framework::interactions::context::InteractionContext;
use crate::framework::messages::MessageEditBuilder;
use crate::groups::about::embeds::get_all_command_help_embed;
use crate::state::State;
#[derive(Debug)]
pub struct HelpGoBackButton;
+9 -11
View File
@@ -5,17 +5,15 @@ use twilight_util::builder::embed::{
EmbedAuthorBuilder, EmbedBuilder, EmbedFieldBuilder, EmbedFooterBuilder, ImageSource,
};
use crate::{
constants::{COLOR_RED, COLOR_WHITE, VERSION},
discord::{images, sanitizing::SanitizeDiscordMarkdown},
extras::Extras,
framework::{
commands::{Command, context::CommandContext},
runtime::Runtime,
},
state::State,
utils::ToTitleCase,
};
use crate::constants::{COLOR_RED, COLOR_WHITE, VERSION};
use crate::discord::images;
use crate::discord::sanitizing::SanitizeDiscordMarkdown;
use crate::extras::Extras;
use crate::framework::commands::Command;
use crate::framework::commands::context::CommandContext;
use crate::framework::runtime::Runtime;
use crate::state::State;
use crate::utils::ToTitleCase;
pub(super) fn get_command_help_embed(
command: &Arc<dyn Command<State, Extras>>,
+7 -9
View File
@@ -1,16 +1,14 @@
use async_trait::async_trait;
use rand::{Rng, SeedableRng};
use rand_chacha::ChaCha20Rng;
use twilight_model::gateway::{
payload::outgoing::{UpdatePresence, update_presence::UpdatePresencePayload},
presence::{Activity, ActivityType, Status},
};
use twilight_model::gateway::payload::outgoing::UpdatePresence;
use twilight_model::gateway::payload::outgoing::update_presence::UpdatePresencePayload;
use twilight_model::gateway::presence::{Activity, ActivityType, Status};
use crate::{
extras::Extras,
framework::{runtime::Runtime, tasks::Task},
state::State,
};
use crate::extras::Extras;
use crate::framework::runtime::Runtime;
use crate::framework::tasks::Task;
use crate::state::State;
#[derive(Debug)]
pub struct UpdatePresenceTask;
+44 -46
View File
@@ -4,28 +4,24 @@ use scc::HashSet;
use time::UtcDateTime;
use twilight_model::id::Id;
use crate::{
database::models::{Call, Channel, Participant},
discord::sanitizing::SanitizeDiscordMarkdown,
extras::Extras,
framework::commands::{
Command, CommandError,
arguments::{Arg, Argument, ParseArguments},
context::CommandContext,
permissions::Permission,
},
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::database::models::{Call, Channel, Participant};
use crate::discord::sanitizing::SanitizeDiscordMarkdown;
use crate::extras::Extras;
use crate::framework::commands::arguments::{Arg, Argument, ParseArguments};
use crate::framework::commands::context::CommandContext;
use crate::framework::commands::permissions::Permission;
use crate::framework::commands::{Command, CommandError};
use crate::groups::calls::masks::{get_mask_display_name, get_mask_emoji};
use crate::groups::calls::permissions::{
IsCallParticipantPermission, NonNewAccountPermission, NotBannedPermission,
};
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)]
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>> {
vec![
Box::new(NotBannedPermission),
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>> {
vec![
Box::new(NotBannedPermission),
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>> {
vec![
Box::new(NotBannedPermission),
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
);
if let Some(channel) = channel {
if let Some(call_id) = channel.call_id {
if let Some(mut participant) = Participant::get_for_user_and_channel_in_call(
ctx.extras.user.id,
ctx.event.channel_id.get(),
call_id,
&ctx.rt.state.db,
)
.await?
{
participant.is_masked = ctx.extras.user.is_masked;
participant.upsert(&ctx.rt.state.db).await?;
if let Some(channel) = channel
&& let Some(call_id) = channel.call_id
{
if let Some(mut participant) = Participant::get_for_user_and_channel_in_call(
ctx.extras.user.id,
ctx.event.channel_id.get(),
call_id,
&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 {
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`.",
get_mask_emoji(&participant.user_mask)
.expect("User's mask did not exist"),
get_mask_display_name(&participant.user_mask),
ctx.prefix,
ctx.prefix
);
}
if ctx.extras.user.is_masked {
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`.",
get_mask_emoji(&participant.user_mask).expect("User's mask did not exist"),
get_mask_display_name(&participant.user_mask),
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)]
pub struct CodeOfConductButton;
+7 -8
View File
@@ -1,17 +1,16 @@
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::{
EmbedAuthorBuilder, EmbedBuilder, EmbedFieldBuilder, EmbedFooterBuilder, ImageSource,
};
use crate::{
constants::{COLOR_RED, COLOR_YELLOW},
database::models::Message,
discord::images,
groups::calls::moderation::Rule,
utils::ellipse,
};
use crate::constants::{COLOR_RED, COLOR_YELLOW};
use crate::database::models::Message;
use crate::discord::images;
use crate::groups::calls::moderation::Rule;
use crate::utils::ellipse;
pub fn first_warning_embed(
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 deadpool_redis::redis::AsyncTypedCommands;
use itertools::Itertools;
use time::{Duration, UtcDateTime};
use twilight_http::{api_error::ApiError, error::ErrorType};
use twilight_model::{
gateway::payload::incoming::{MessageCreate, MessageDelete, MessageUpdate},
id::Id,
};
use twilight_util::{
builder::embed::{EmbedAuthorBuilder, EmbedBuilder, EmbedFooterBuilder, ImageSource},
snowflake::Snowflake,
use twilight_http::api_error::ApiError;
use twilight_http::error::ErrorType;
use twilight_model::gateway::payload::incoming::{MessageCreate, MessageDelete, MessageUpdate};
use twilight_model::id::Id;
use twilight_util::builder::embed::{
EmbedAuthorBuilder, EmbedBuilder, EmbedFooterBuilder, ImageSource,
};
use twilight_util::snowflake::Snowflake;
use crate::{
constants::{COLOR_WHITE, COLOR_YELLOW},
database::models::{Call, Channel, Message, Participant, User, UserIncident},
discord::{images, sanitizing::SanitizeDiscordMarkdown},
extras::Extras,
framework::{
commands::extras::ExtrasGetter,
events::traits::{
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::constants::{COLOR_WHITE, COLOR_YELLOW};
use crate::database::models::{Call, Channel, Message, Participant, User, UserIncident};
use crate::discord::images;
use crate::discord::sanitizing::SanitizeDiscordMarkdown;
use crate::extras::Extras;
use crate::framework::commands::extras::ExtrasGetter;
use crate::framework::events::traits::{
EventResult, OnMessageDeleteHandler, OnMessageEditHandler, OnMessageHandler,
};
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(
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 {
// 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!(
"https://discord.com/channels/{}/{}/{}",
opposite_channel.server_id, opposite_channel.id, replying_to.message_id
)
} 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!(
"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))
.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(),
);
}
@@ -440,12 +458,13 @@ impl OnMessageHandler<State, Extras> for CallMessageHandler {
error: ApiError::General(error),
..
} = e.kind()
&& error.code == 10015 {
log::debug!("Webhook not found, updating...");
opposite_channel
.update_webhook(&rt.state.db, &rt.http)
.await?;
}
&& error.code == 10015
{
log::debug!("Webhook not found, updating...");
opposite_channel
.update_webhook(&rt.state.db, &rt.http)
.await?;
}
return Ok(());
}
}
@@ -626,19 +645,19 @@ impl OnMessageDeleteHandler<State, Extras> for CallMessageDeleteHandler {
&& let Some((webhook_id, webhook_token)) = opposite_channel
.webhook_id
.zip(opposite_channel.webhook_token)
{
rt.http
.delete_webhook_message(
Id::new(webhook_id),
&webhook_token,
Id::new(i64_to_u64(propagation_message_id)),
)
.await?;
{
rt.http
.delete_webhook_message(
Id::new(webhook_id),
&webhook_token,
Id::new(i64_to_u64(propagation_message_id)),
)
.await?;
message.data.is_deleted = true;
message.data.is_deleted = true;
message.insert(&rt.state.db).await?;
}
message.insert(&rt.state.db).await?;
}
Ok(())
}
@@ -79,6 +79,9 @@ goonbud
goonbuds
goonete
goonetes
gooner
goonr
goner
boty hole
suck my pp
ass cracker
@@ -95,6 +98,10 @@ masturbating
masterbating
masterbaiting
masturbatigng
masterbaiter
mastrbaiter
mastrbait
mastrbate
horny
hrny
whore / who re
@@ -116,6 +123,8 @@ eat my bum
eat mi bum
ima touch you
ima touch u
i ma touch you
i ma touch u
il touch you
il touch u
im going to touch you
@@ -125,4 +134,6 @@ im gonna touch u
nude
nudes
nde
ndes
ndes
nood
noods
+3 -1
View File
@@ -83,7 +83,9 @@
//!
//! 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 linkify::LinkFinder;
+54 -7
View File
@@ -2,13 +2,13 @@ use async_trait::async_trait;
use time::{Duration, UtcDateTime};
use twilight_util::snowflake::Snowflake;
use crate::{
database::models::UserIncident,
extras::Extras,
framework::commands::{CommandError, context::CommandContext, permissions::Permission},
groups::calls::components::{CodeOfConductButton, NeedHelpButton},
state::State,
};
use crate::database::models::{Call, Channel, Participant, UserIncident};
use crate::extras::Extras;
use crate::framework::commands::CommandError;
use crate::framework::commands::context::CommandContext;
use crate::framework::commands::permissions::Permission;
use crate::groups::calls::components::{CodeOfConductButton, NeedHelpButton};
use crate::state::State;
#[derive(Debug)]
pub struct NotBannedPermission;
@@ -72,3 +72,50 @@ impl Permission<State, Extras> for NonNewAccountPermission {
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 rand::{SeedableRng, seq::IndexedRandom};
use rand::SeedableRng;
use rand::seq::IndexedRandom;
use rand_chacha::ChaCha20Rng;
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 twilight_model::id::Id;
use crate::{
database::models::Call,
extras::Extras,
framework::{runtime::Runtime, tasks::Task},
state::State,
};
use crate::database::models::Call;
use crate::extras::Extras;
use crate::framework::runtime::Runtime;
use crate::framework::tasks::Task;
use crate::state::State;
#[derive(Debug)]
pub struct EndInactiveCallsTask {
+4 -8
View File
@@ -5,14 +5,10 @@ use std::sync::Arc;
use async_trait::async_trait;
use twilight_model::gateway::payload::incoming::Ready;
use crate::{
extras::Extras,
framework::{
events::traits::{EventResult, OnReadyHandler, OnReconnectHandler},
runtime::Runtime,
},
state::State,
};
use crate::extras::Extras;
use crate::framework::events::traits::{EventResult, OnReadyHandler, OnReconnectHandler};
use crate::framework::runtime::Runtime;
use crate::state::State;
/// Logs a ready message when the bot is ready.
#[derive(Debug)]
+37 -21
View File
@@ -5,15 +5,16 @@ use twilight_util::builder::embed::{
EmbedAuthorBuilder, EmbedBuilder, EmbedFooterBuilder, ImageSource,
};
use crate::{
constants::COLOR_WHITE,
database::models::User,
discord::{images, sanitizing::SanitizeDiscordMarkdown},
extras::Extras,
framework::commands::{Command, CommandError, context::CommandContext},
groups::leveling::constants::KEY_LEADERBOARD_MONTHLY,
state::State,
};
use crate::constants::COLOR_WHITE;
use crate::database::models::User;
use crate::discord::images;
use crate::discord::sanitizing::SanitizeDiscordMarkdown;
use crate::extras::Extras;
use crate::framework::commands::context::CommandContext;
use crate::framework::commands::{Command, CommandError};
use crate::groups::leveling::constants::KEY_LEADERBOARD_MONTHLY;
use crate::state::State;
use crate::utils::format_thousands;
#[derive(Debug)]
pub struct LeaderboardCommand;
@@ -48,6 +49,12 @@ impl Command<State, Extras> for LeaderboardCommand {
let current_user_rank: Option<usize> = redis
.zrevrank(KEY_LEADERBOARD_MONTHLY, ctx.event.author.id.get())
.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();
@@ -57,15 +64,15 @@ impl Command<State, Extras> for LeaderboardCommand {
continue;
};
if let Some(is_banned_from_leaderboard) = user.is_banned_from_leaderboard {
if is_banned_from_leaderboard {
log::trace!(
"Skipping user {} (ID: {}) because they are banned from the leaderboard.",
user.name,
user.id
);
continue;
}
if let Some(is_banned_from_leaderboard) = user.is_banned_from_leaderboard
&& is_banned_from_leaderboard
{
log::trace!(
"Skipping user {} (ID: {}) because they are banned from the leaderboard.",
user.name,
user.id
);
continue;
}
users.push((user, score as u32));
@@ -76,15 +83,24 @@ impl Command<State, Extras> for LeaderboardCommand {
.iter()
.enumerate()
.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")
} else {
"*Nobody earned XP this month yet.*".to_string()
};
let footer = if let Some(rank) = current_user_rank {
format!("Your current rank: #{}", rank + 1)
let footer = if let Some((rank, xp)) = current_user_rank_and_xp {
format!(
"Your current rank: #{} - {} XP",
rank + 1,
format_thousands(xp)
)
} else {
"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 twilight_model::gateway::payload::incoming::MessageCreate;
use crate::{
database::models::Channel,
extras::Extras,
framework::{
commands::extras::ExtrasGetter,
events::traits::{EventResult, OnMessageHandler},
runtime::Runtime,
},
groups::leveling::constants::KEY_LEADERBOARD_MONTHLY,
state::State,
};
use crate::database::models::Channel;
use crate::extras::Extras;
use crate::framework::commands::extras::ExtrasGetter;
use crate::framework::events::traits::{EventResult, OnMessageHandler};
use crate::framework::runtime::Runtime;
use crate::groups::leveling::constants::KEY_LEADERBOARD_MONTHLY;
use crate::state::State;
fn seconds_until_end_of_month() -> i64 {
let now = OffsetDateTime::now_utc();
+26 -15
View File
@@ -1,20 +1,14 @@
use std::{
ops::{Deref, DerefMut},
time::Duration,
};
use std::ops::{Deref, DerefMut};
use std::time::Duration;
use scylla::client::session::Session;
use crate::{
database::models::{Message, User},
extras::Extras,
framework::commands::{
arguments::{ArgumentParsingError, FromArguments, NeedyString},
context::CommandContext,
},
state::State,
utils::split_at_nth_from_end,
};
use crate::database::models::{Message, User};
use crate::extras::Extras;
use crate::framework::commands::arguments::{ArgumentParsingError, FromArguments, NeedyString};
use crate::framework::commands::context::CommandContext;
use crate::state::State;
use crate::utils::split_at_nth_from_end;
/// Fetch a user by their ID from the database.
///
@@ -110,7 +104,24 @@ impl FromArguments<State, Extras> for UserArgument {
.parse()
.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 {
// The argument is (probably) a username.
+13 -19
View File
@@ -1,26 +1,20 @@
use async_trait::async_trait;
use time::UtcDateTime;
use crate::{
database::models::{UserIncident, UserIncidentType},
extras::Extras,
framework::{
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::database::models::{UserIncident, UserIncidentType};
use crate::extras::Extras;
use crate::framework::commands::arguments::{
Arg, Argument, ArgumentParsingError, NeedyGreedyString, ParseArguments,
};
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)]
pub struct ModeratorCommand;
+8 -8
View File
@@ -1,14 +1,14 @@
use async_trait::async_trait;
use twilight_model::channel::message::component::ButtonStyle;
use crate::{
database::models::UserIncident,
extras::Extras,
framework::interactions::{InteractionError, components::Button, context::InteractionContext},
groups::moderation::get_incident_page,
state::State,
utils::get_random_u64,
};
use crate::database::models::UserIncident;
use crate::extras::Extras;
use crate::framework::interactions::InteractionError;
use crate::framework::interactions::components::Button;
use crate::framework::interactions::context::InteractionContext;
use crate::groups::moderation::get_incident_page;
use crate::state::State;
use crate::utils::get_random_u64;
#[derive(Debug)]
pub struct ArrowIncidentButton {
+3 -5
View File
@@ -4,11 +4,9 @@ use twilight_util::builder::embed::{
EmbedAuthorBuilder, EmbedBuilder, EmbedFieldBuilder, ImageSource,
};
use crate::{
constants::{COLOR_RED, COLOR_YELLOW},
database::models::{UserIncident, UserIncidentType},
utils::ellipse,
};
use crate::constants::{COLOR_RED, COLOR_YELLOW};
use crate::database::models::{UserIncident, UserIncidentType};
use crate::utils::ellipse;
fn format_timestamp(timestamp: UtcDateTime) -> String {
format!(
+7 -11
View File
@@ -1,17 +1,13 @@
use scylla::client::session::Session;
use crate::{
database::models::{DatabaseError, UserIncident, UserIncidentType},
extras::Extras,
framework::messages::MessageBuilder,
groups::moderation::{
components::{
ArrowIncidentButton, CounterButton, DeleteIncidentButton, ExpireIncidentButton,
},
embeds::incident_embed,
},
state::State,
use crate::database::models::{DatabaseError, UserIncident, UserIncidentType};
use crate::extras::Extras;
use crate::framework::messages::MessageBuilder;
use crate::groups::moderation::components::{
ArrowIncidentButton, CounterButton, DeleteIncidentButton, ExpireIncidentButton,
};
use crate::groups::moderation::embeds::incident_embed;
use crate::state::State;
pub mod arguments;
pub mod commands;
+5 -5
View File
@@ -1,10 +1,10 @@
use async_trait::async_trait;
use crate::{
extras::Extras,
framework::commands::{CommandError, context::CommandContext, permissions::Permission},
state::State,
};
use crate::extras::Extras;
use crate::framework::commands::CommandError;
use crate::framework::commands::context::CommandContext;
use crate::framework::commands::permissions::Permission;
use crate::state::State;
#[derive(Debug)]
pub struct BotOwnerPermission;
+10 -14
View File
@@ -1,19 +1,15 @@
use async_trait::async_trait;
use crate::{
constants::DEFAULT_PREFIX,
database::models::Server,
discord::sanitizing::SanitizeDiscordMarkdown,
extras::Extras,
framework::commands::{
Command, CommandError,
arguments::{Arg, Argument, GreedyString, ParseArguments},
context::CommandContext,
permissions::Permission,
},
groups::servers::permissions::{GuildOnlyPermission, ServerModeratorPermission},
state::State,
};
use crate::constants::DEFAULT_PREFIX;
use crate::database::models::Server;
use crate::discord::sanitizing::SanitizeDiscordMarkdown;
use crate::extras::Extras;
use crate::framework::commands::arguments::{Arg, Argument, GreedyString, ParseArguments};
use crate::framework::commands::context::CommandContext;
use crate::framework::commands::permissions::Permission;
use crate::framework::commands::{Command, CommandError};
use crate::groups::servers::permissions::{GuildOnlyPermission, ServerModeratorPermission};
use crate::state::State;
#[derive(Debug)]
pub struct PrefixCommand;
+6 -10
View File
@@ -5,17 +5,13 @@ use std::sync::Arc;
use async_trait::async_trait;
use twilight_model::gateway::payload::incoming::{GuildCreate, GuildDelete, GuildUpdate};
use crate::{
database::models::Server,
extras::Extras,
framework::{
events::traits::{
EventResult, OnServerCreateHandler, OnServerDeleteHandler, OnServerUpdateHandler,
},
runtime::Runtime,
},
state::State,
use crate::database::models::Server;
use crate::extras::Extras;
use crate::framework::events::traits::{
EventResult, OnServerCreateHandler, OnServerDeleteHandler, OnServerUpdateHandler,
};
use crate::framework::runtime::Runtime;
use crate::state::State;
/// Adds servers to the DB when they get created.
#[derive(Debug)]
+8 -9
View File
@@ -1,15 +1,14 @@
use async_trait::async_trait;
use twilight_model::{
guild::Permissions,
id::{Id, marker::RoleMarker},
};
use twilight_model::guild::Permissions;
use twilight_model::id::Id;
use twilight_model::id::marker::RoleMarker;
use twilight_util::permission_calculator::PermissionCalculator;
use crate::{
extras::Extras,
framework::commands::{CommandError, context::CommandContext, permissions::Permission},
state::State,
};
use crate::extras::Extras;
use crate::framework::commands::CommandError;
use crate::framework::commands::context::CommandContext;
use crate::framework::commands::permissions::Permission;
use crate::state::State;
#[derive(Debug)]
pub struct GuildOnlyPermission;