Add support for extras, conditionally displaying commands in the help command, and start working on moderation commands

This commit is contained in:
2025-08-22 18:06:52 -03:00
parent 55ae1c8937
commit 8868b18be3
41 changed files with 1075 additions and 368 deletions
+25 -24
View File
@@ -5,6 +5,7 @@ use twilight_model::id::Id;
use crate::{
database::models::{Call, Channel},
discord::sanitizing::SanitizeDiscordMarkdown,
extras::Extras,
framework::commands::{
Command, CommandError, context::CommandContext, permissions::Permission,
},
@@ -39,7 +40,7 @@ impl CallCommand {
}
#[async_trait]
impl Command<State> for CallCommand {
impl Command<State, Extras> for CallCommand {
fn name(&self) -> &'static str {
"call"
}
@@ -48,13 +49,13 @@ impl Command<State> for CallCommand {
&["c"]
}
fn permissions(&self) -> Vec<Box<dyn Permission<State> + Send + Sync + 'static>> {
fn permissions(&self) -> Vec<Box<dyn Permission<State, Extras> + Send + Sync + 'static>> {
vec![Box::new(NotBannedPermission)]
}
async fn execute(
&self,
ctx: CommandContext<'_, State>,
ctx: CommandContext<'_, State, Extras>,
_args: &str,
) -> Result<(), CommandError> {
let channel = Channel::get_or_create_by_id(
@@ -66,7 +67,7 @@ impl Command<State> for CallCommand {
.inspect_err(|e| {
log::error!("Failed to get or create channel: {e:?}");
})
.map_err(|_| CommandError::DatabaseQueryFailed)?;
.map_err(|_| CommandError::DatabaseFailed)?;
if channel.is_searching || self.channel_lock.contains(&ctx.event.channel_id.get()) {
ctx.send()
@@ -100,7 +101,7 @@ impl Command<State> for CallCommand {
log::error!("Failed to start call: {e:?}");
self.channel_lock.remove(&ctx.event.channel_id.get());
})
.map_err(|_| CommandError::DatabaseQueryFailed)?;
.map_err(|_| CommandError::DatabaseFailed)?;
self.channel_lock
.remove_async(&ctx.event.channel_id.get())
@@ -141,7 +142,7 @@ impl Command<State> for CallCommand {
pub struct SkipCommand;
#[async_trait]
impl Command<State> for SkipCommand {
impl Command<State, Extras> for SkipCommand {
fn name(&self) -> &'static str {
"skip"
}
@@ -150,13 +151,13 @@ impl Command<State> for SkipCommand {
&["s", "next"]
}
fn permissions(&self) -> Vec<Box<dyn Permission<State> + Send + Sync + 'static>> {
fn permissions(&self) -> Vec<Box<dyn Permission<State, Extras> + Send + Sync + 'static>> {
vec![Box::new(NotBannedPermission)]
}
async fn execute(
&self,
ctx: CommandContext<'_, State>,
ctx: CommandContext<'_, State, Extras>,
_args: &str,
) -> Result<(), CommandError> {
let channel = Channel::get_or_create_by_id(
@@ -168,7 +169,7 @@ impl Command<State> for SkipCommand {
.inspect_err(|e| {
log::error!("Failed to get or create channel: {e:?}");
})
.map_err(|_| CommandError::DatabaseQueryFailed)?;
.map_err(|_| CommandError::DatabaseFailed)?;
if channel.call_id.is_none() {
ctx.send()
@@ -197,13 +198,13 @@ impl Command<State> for SkipCommand {
let call = Call::get_by_id(channel.call_id.unwrap(), &ctx.rt.state.db)
.await
.inspect_err(|e| log::error!("Failed to get call: {e:?}"))
.map_err(|_| CommandError::DatabaseQueryFailed)?
.map_err(|_| CommandError::DatabaseFailed)?
.ok_or(CommandError::DatabaseInconsistentState)?;
call.hangup(&ctx.rt.state.db)
.await
.inspect_err(|e| log::error!("Failed to hang up call: {e:?}"))
.map_err(|_| CommandError::DatabaseQueryFailed)?;
.map_err(|_| CommandError::DatabaseFailed)?;
let other_channel_id = call.other_party_channel_id(ctx.event.channel_id.get());
@@ -225,7 +226,7 @@ impl Command<State> for SkipCommand {
.inspect_err(|e| {
log::error!("Failed to start (skipped) call: {e:?}");
})
.map_err(|_| CommandError::DatabaseQueryFailed)?;
.map_err(|_| CommandError::DatabaseFailed)?;
if let Some(call) = call {
ctx.send()
@@ -259,7 +260,7 @@ impl Command<State> for SkipCommand {
pub struct HangupCommand;
#[async_trait]
impl Command<State> for HangupCommand {
impl Command<State, Extras> for HangupCommand {
fn name(&self) -> &'static str {
"hangup"
}
@@ -268,19 +269,19 @@ impl Command<State> for HangupCommand {
&["h", "hang"]
}
fn permissions(&self) -> Vec<Box<dyn Permission<State> + Send + Sync + 'static>> {
fn permissions(&self) -> Vec<Box<dyn Permission<State, Extras> + Send + Sync + 'static>> {
vec![Box::new(NotBannedPermission)]
}
async fn execute(
&self,
ctx: CommandContext<'_, State>,
ctx: CommandContext<'_, State, Extras>,
_args: &str,
) -> Result<(), CommandError> {
let Some(channel) = Channel::get_by_id(ctx.event.channel_id.get(), &ctx.rt.state.db)
.await
.inspect_err(|e| log::error!("Failed to get channel: {e:?}"))
.map_err(|_| CommandError::DatabaseQueryFailed)?
.map_err(|_| CommandError::DatabaseFailed)?
else {
ctx.send()
.content(format!(
@@ -300,7 +301,7 @@ impl Command<State> for HangupCommand {
.inspect_err(|e| {
log::error!("Failed to cancel search: {e:?}");
})
.map_err(|_| CommandError::DatabaseQueryFailed)?;
.map_err(|_| CommandError::DatabaseFailed)?;
ctx.send()
.content(format!("☎️ {}", get_string_call_no_answer(ctx.prefix)))
@@ -314,7 +315,7 @@ impl Command<State> for HangupCommand {
let Some(call) = Call::get_by_id(call_id, &ctx.rt.state.db)
.await
.inspect_err(|e| log::error!("Failed to get call: {e:?}"))
.map_err(|_| CommandError::DatabaseQueryFailed)?
.map_err(|_| CommandError::DatabaseFailed)?
else {
ctx.send()
.content("❌ Call not found.")
@@ -327,7 +328,7 @@ impl Command<State> for HangupCommand {
call.hangup(&ctx.rt.state.db)
.await
.inspect_err(|e| log::error!("Failed to hang up call: {e:?}"))
.map_err(|_| CommandError::DatabaseQueryFailed)?;
.map_err(|_| CommandError::DatabaseFailed)?;
ctx.send()
.content(format!(
@@ -366,7 +367,7 @@ impl Command<State> for HangupCommand {
pub struct FriendRequestCommand;
#[async_trait]
impl Command<State> for FriendRequestCommand {
impl Command<State, Extras> for FriendRequestCommand {
fn name(&self) -> &'static str {
"friendreq"
}
@@ -379,19 +380,19 @@ impl Command<State> for FriendRequestCommand {
"Shows your username to the other party and asks them to send you a friend request on Discord."
}
fn permissions(&self) -> Vec<Box<dyn Permission<State> + Send + Sync + 'static>> {
fn permissions(&self) -> Vec<Box<dyn Permission<State, Extras> + Send + Sync + 'static>> {
vec![Box::new(NotBannedPermission)]
}
async fn execute(
&self,
ctx: CommandContext<'_, State>,
ctx: CommandContext<'_, State, Extras>,
_args: &str,
) -> Result<(), CommandError> {
let channel = Channel::get_by_id(ctx.event.channel_id.get(), &ctx.rt.state.db)
.await
.inspect_err(|e| log::error!("Failed to get channel: {e:?}"))
.map_err(|_| CommandError::DatabaseQueryFailed)?;
.map_err(|_| CommandError::DatabaseFailed)?;
let Some(channel) = channel else {
ctx.send()
@@ -409,7 +410,7 @@ impl Command<State> for FriendRequestCommand {
Call::get_by_id(call_id, &ctx.rt.state.db)
.await
.inspect_err(|e| log::error!("Failed to get call: {e:?}"))
.map_err(|_| CommandError::DatabaseQueryFailed)?
.map_err(|_| CommandError::DatabaseFailed)?
} else {
ctx.send()
.content(format!(
+3 -3
View File
@@ -1,9 +1,9 @@
use crate::{framework::interactions::components::Button, state::State};
use crate::{extras::Extras, framework::interactions::components::Button, state::State};
#[derive(Debug)]
pub struct CodeOfConductButton;
impl Button<State> for CodeOfConductButton {
impl Button<State, Extras> for CodeOfConductButton {
fn label(&self) -> Option<&'static str> {
Some("Code of Conduct")
}
@@ -16,7 +16,7 @@ impl Button<State> for CodeOfConductButton {
#[derive(Debug)]
pub struct NeedHelpButton;
impl Button<State> for NeedHelpButton {
impl Button<State, Extras> for NeedHelpButton {
fn label(&self) -> Option<&'static str> {
Some("Need Help?")
}
+26 -17
View File
@@ -11,21 +11,18 @@ use twilight_util::builder::embed::{EmbedAuthorBuilder, EmbedBuilder, ImageSourc
use crate::{
constants::COLOR_WHITE,
database::models::{
Call, Channel, Message, User, UserIncident, get_or_create_user_and_set_last_seen,
},
database::models::{Call, Channel, Message, User, UserIncident},
discord::sanitizing::SanitizeDiscordMarkdown,
extras::Extras,
framework::{
events::traits::{
commands::extras::ExtrasGetter, events::traits::{
EventResult, OnMessageDeleteHandler, OnMessageEditHandler, OnMessageHandler,
},
interactions::components::ActionRow,
runtime::Runtime,
}, interactions::components::ActionRow, runtime::Runtime
},
groups::calls::{
components::{CodeOfConductButton, NeedHelpButton},
embeds, get_display_profile,
moderation::{Action, Rule, escape_message},
moderation::{escape_message, Action, Rule},
},
state::State,
utils::{ellipse, i64_to_u64},
@@ -35,7 +32,7 @@ async fn penalize_user(
user: User,
message: Message,
broken_rules: Vec<Arc<dyn Rule>>,
rt: &Runtime<State>,
rt: &Runtime<State, Extras>,
) -> Result<(), Box<dyn Error>> {
let reason = format!(
"You broke the following rules during a call:\n{}",
@@ -88,8 +85,13 @@ async fn penalize_user(
pub struct CallMessageHandler;
#[async_trait]
impl OnMessageHandler<State> for CallMessageHandler {
async fn on_message(&self, event: &MessageCreate, rt: Arc<Runtime<State>>) -> EventResult {
impl OnMessageHandler<State, Extras> for CallMessageHandler {
async fn on_message(
&self,
event: &MessageCreate,
rt: Arc<Runtime<State, Extras>>,
extras: Arc<dyn ExtrasGetter<State, Extras = Extras>>,
) -> EventResult {
if event.author.bot || event.author.system.unwrap_or(false) {
return Ok(());
}
@@ -144,7 +146,8 @@ impl OnMessageHandler<State> for CallMessageHandler {
return Ok(());
};
let user = get_or_create_user_and_set_last_seen(event.author.clone(), &rt.state.db).await?;
let user =
User::get_or_create_and_set_last_seen(event.author.clone(), &rt.state.db).await?;
let user_incidents = UserIncident::get_for_user(event.author.id.get(), &rt.state.db)
.await?
@@ -314,8 +317,12 @@ impl OnMessageHandler<State> for CallMessageHandler {
pub struct CallMessageEditHandler;
#[async_trait]
impl OnMessageEditHandler<State> for CallMessageEditHandler {
async fn on_message_edit(&self, event: &MessageUpdate, rt: Arc<Runtime<State>>) -> EventResult {
impl OnMessageEditHandler<State, Extras> for CallMessageEditHandler {
async fn on_message_edit(
&self,
event: &MessageUpdate,
rt: Arc<Runtime<State, Extras>>,
) -> EventResult {
log::trace!(
"Edited message was '{}' sent by {} (bot? {:?})",
event.content,
@@ -368,7 +375,9 @@ impl OnMessageEditHandler<State> for CallMessageEditHandler {
.reply(event.id)
.await?;
let user = get_or_create_user_and_set_last_seen(event.author.clone(), &rt.state.db).await?;
let user =
User::get_or_create_and_set_last_seen(event.author.clone(), &rt.state.db)
.await?;
penalize_user(user, message, rules, &rt).await?;
@@ -404,11 +413,11 @@ impl OnMessageEditHandler<State> for CallMessageEditHandler {
pub struct CallMessageDeleteHandler;
#[async_trait]
impl OnMessageDeleteHandler<State> for CallMessageDeleteHandler {
impl OnMessageDeleteHandler<State, Extras> for CallMessageDeleteHandler {
async fn on_message_delete(
&self,
event: &MessageDelete,
rt: Arc<Runtime<State>>,
rt: Arc<Runtime<State, Extras>>,
) -> EventResult {
let Some(channel) = Channel::get_by_id(event.channel_id.get(), &rt.state.db).await? else {
return Ok(());
@@ -80,6 +80,8 @@ gooncream
gooncreaming
goonbud
goonbuds
goonete
goonetes
boty hole
suck my pp
ass cracker
@@ -98,7 +100,7 @@ masterbaiting
masturbatigng
horny
hrny
whore
whore / who re
whre
tickle your pickle
tickle yr pickle
+8 -4
View File
@@ -2,6 +2,7 @@ use async_trait::async_trait;
use crate::{
database::models::UserIncident,
extras::Extras,
framework::commands::{CommandError, context::CommandContext, permissions::Permission},
groups::calls::components::{CodeOfConductButton, NeedHelpButton},
state::State,
@@ -11,12 +12,12 @@ use crate::{
pub struct NotBannedPermission;
#[async_trait]
impl Permission<State> for NotBannedPermission {
async fn check(&self, ctx: &CommandContext<State>) -> Result<bool, CommandError> {
impl Permission<State, Extras> for NotBannedPermission {
async fn check(&self, ctx: &CommandContext<State, Extras>) -> Result<bool, CommandError> {
let incidents = UserIncident::get_for_user(ctx.event.author.id.get(), &ctx.rt.state.db)
.await
.inspect_err(|e| log::error!("Failed to get user incidents: {e}"))
.map_err(|_| CommandError::DatabaseQueryFailed)?;
.map_err(|_| CommandError::DatabaseFailed)?;
if incidents.iter().any(|i| i.is_active()) {
Ok(false)
@@ -25,7 +26,10 @@ impl Permission<State> for NotBannedPermission {
}
}
async fn on_handle_denied(&self, ctx: &CommandContext<State>) -> Result<(), CommandError> {
async fn on_handle_denied(
&self,
ctx: &CommandContext<State, Extras>,
) -> Result<(), CommandError> {
ctx.send()
.content("❌ You've been banned from Payphone.")
.component(CodeOfConductButton, 1)