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
+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 {