Checkpoint
This commit is contained in:
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>>,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(|| {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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,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();
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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!(
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user