Files
payphone/src/groups/calls/events.rs
T
2025-08-12 11:24:09 -03:00

155 lines
4.5 KiB
Rust

use std::{collections::HashMap, sync::Arc};
use async_trait::async_trait;
use linkify::LinkFinder;
use twilight_model::{gateway::payload::incoming::MessageCreate, id::Id};
use twilight_validate::request::webhook_username;
use crate::{
database::models::{Call, Channel}, discord::{images, sanitizing::SanitizeDiscordMarkdown}, framework::{
events::traits::{EventResult, OnMessageHandler},
runtime::Runtime,
}, groups::calls::moderation::escape_message, state::State
};
#[derive(Debug)]
pub struct MessageRedirector;
#[async_trait]
impl OnMessageHandler<State> for MessageRedirector {
async fn on_message(&self, event: &MessageCreate, rt: Arc<Runtime<State>>) -> EventResult {
if event.author.bot || event.author.system.unwrap_or(false) {
return Ok(());
}
let prefixes = rt
.get_prefixes(event.guild_id.map(|i| i.get()), Some(event.author.id.get()))
.await;
if prefixes.iter().any(|prefix| {
event
.content
.to_ascii_lowercase()
.starts_with(&prefix.to_ascii_lowercase())
}) {
return Ok(());
}
let Some(channel) = Channel::get_by_id(event.channel_id.get(), &rt.state.db)
.await
.inspect_err(|e| {
log::error!(
"Failed to get channel from DB when processing message create event! {e:?}"
);
})?
else {
return Ok(());
};
let call = if let Some(call_id) = channel.call_id {
if let Some(call) = Call::get_by_id(call_id, &rt.state.db)
.await
.inspect_err(|e| {
log::error!(
"Failed to get call from DB when processing message create event! {e:?}"
);
})?
{
if !call.is_active() {
return Ok(());
}
call
} else {
log::error!(
"Call ID {} in channel {} does not correspond to an active call.",
call_id,
channel.id
);
return Ok(());
}
} else {
return Ok(());
};
let Some(opposite_channel) = Channel::get_by_id(
call.other_party_channel_id(event.channel_id.get()),
&rt.state.db,
)
.await
.inspect_err(|e| {
log::error!(
"Failed to get opposite channel from DB when processing message create event! {e:?}"
);
})?
else {
return Ok(());
};
let Some((webhook_id, webhook_token)) = opposite_channel
.webhook_id
.zip(opposite_channel.webhook_token)
else {
// Handle DM calls in the future.
return Ok(());
};
let profile = get_display_profile(event);
rt.http
.execute_webhook(Id::new(webhook_id), &webhook_token)
.allowed_mentions(None)
.username(&profile.name)
.avatar_url(&profile.avatar_url)
.content(&escape_message(event))
.await
.inspect_err(|e| {
log::error!(
"Failed to send message to webhook for call {} in channel {}: {e:?}",
call.id,
opposite_channel.id
);
})?;
Ok(())
}
}
struct DisplayProfile {
pub name: String,
pub avatar_url: String,
}
fn get_display_profile(event: &MessageCreate) -> DisplayProfile {
let member = event.member.clone();
let name = if let Some(nick) = &member.clone().and_then(|member| member.nick) {
if webhook_username(nick).is_err() {
"Invalid Nickname".to_string()
} else {
nick.clone()
}
} else if let Some(name) = &event.author.global_name {
if webhook_username(name).is_err() {
"Invalid Global Name".to_string()
} else {
name.clone()
}
} else if webhook_username(&event.author.name).is_err() {
"Invalid Username".to_string()
} else {
event.author.name.clone()
};
let avatar_hash = member
.and_then(|inner| inner.avatar)
.or(event.author.avatar);
let avatar_url = images::get_avatar_url(
avatar_hash,
event.author.id.get(),
event.author.discriminator as u64,
);
DisplayProfile { name, avatar_url }
}