+ Only support chat events now + Created a listener for host message snooping + Clippy + fmt
324 lines
8.0 KiB
Rust
324 lines
8.0 KiB
Rust
use crate::error;
|
|
use crate::error::WoxlfError;
|
|
use crate::game::global_data::GlobalData;
|
|
use crate::game::listener::{EventStatus, Listeners, WoxlfEvent};
|
|
use crate::game::player_data::PlayerData;
|
|
use bitflags::bitflags;
|
|
use serenity::client::Context;
|
|
use serenity::http::{CacheHttp, Http};
|
|
use serenity::model::guild::Guild;
|
|
use serenity::model::id::{ChannelId, UserId};
|
|
use serenity::model::prelude::{AttachmentType, WebhookId};
|
|
use serenity::utils::MessageBuilder;
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum MessageSource {
|
|
Player(Box<PlayerData>),
|
|
Host,
|
|
Automated,
|
|
}
|
|
|
|
impl Default for MessageSource {
|
|
fn default() -> Self {
|
|
Self::Automated
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
#[allow(dead_code)]
|
|
pub enum MessageDest {
|
|
Player(Box<PlayerData>),
|
|
Host,
|
|
Broadcast,
|
|
}
|
|
|
|
impl Default for MessageDest {
|
|
fn default() -> Self {
|
|
Self::Broadcast
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
#[allow(dead_code)]
|
|
pub enum Median {
|
|
DirectMessage,
|
|
Webhook,
|
|
StandardMessage,
|
|
}
|
|
|
|
impl Default for Median {
|
|
fn default() -> Self {
|
|
Self::Webhook
|
|
}
|
|
}
|
|
|
|
bitflags! {
|
|
#[derive(Default)]
|
|
pub struct MsgFlags: u32 {
|
|
const PIN_MSG = 0b00000001;
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Default)]
|
|
pub struct WoxlfMessage<'a> {
|
|
pub source: MessageSource,
|
|
pub dest: MessageDest,
|
|
pub median: Median,
|
|
pub content: String,
|
|
pub attachments: Option<Vec<AttachmentType<'a>>>,
|
|
pub flags: MsgFlags,
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
impl<'a> WoxlfMessage<'a> {
|
|
pub fn get_profile_pic(&self, global_data: &GlobalData) -> Result<String, WoxlfError> {
|
|
Ok(match &self.source {
|
|
MessageSource::Player(p) => p.profile_pic_url.clone(),
|
|
MessageSource::Host | MessageSource::Automated => {
|
|
global_data.game_cfg()?.bot_profile_pic.clone()
|
|
}
|
|
})
|
|
}
|
|
|
|
pub fn get_message_username(&self, global_data: &GlobalData) -> Result<String, WoxlfError> {
|
|
Ok(match &self.source {
|
|
MessageSource::Player(p) => p.codename.clone(),
|
|
MessageSource::Host => global_data.game_cfg()?.bot_name.clone(),
|
|
MessageSource::Automated => "Woxlf System Message".to_string(),
|
|
})
|
|
}
|
|
|
|
pub fn source(mut self, source: MessageSource) -> Self {
|
|
self.source = source;
|
|
self
|
|
}
|
|
|
|
pub fn dest(mut self, dest: MessageDest) -> Self {
|
|
self.dest = dest;
|
|
self
|
|
}
|
|
|
|
pub fn median(mut self, median: Median) -> Self {
|
|
self.median = median;
|
|
self
|
|
}
|
|
|
|
pub fn content(mut self, content: &str) -> Self {
|
|
self.content = content.to_string();
|
|
self
|
|
}
|
|
|
|
pub fn attachments(mut self, attachments: Vec<AttachmentType<'a>>) -> Self {
|
|
self.attachments = Some(attachments);
|
|
self
|
|
}
|
|
|
|
pub fn flags(mut self, flags: MsgFlags) -> Self {
|
|
self.flags = flags;
|
|
self
|
|
}
|
|
|
|
pub fn pin(mut self) -> Self {
|
|
self.flags |= MsgFlags::PIN_MSG;
|
|
self
|
|
}
|
|
}
|
|
|
|
fn filter_source_channel(player_data: &&PlayerData, msg_source: &MessageSource) -> bool {
|
|
if let MessageSource::Player(source_player) = &msg_source {
|
|
if source_player.channel == player_data.channel {
|
|
return false;
|
|
}
|
|
}
|
|
true
|
|
}
|
|
|
|
async fn send_webhook_msg(
|
|
http: &Http,
|
|
webhook_id: WebhookId,
|
|
username: &str,
|
|
profile_pic_url: Option<String>,
|
|
msg: &str,
|
|
attachments: &Option<Vec<AttachmentType<'_>>>,
|
|
) -> error::Result<()> {
|
|
let webhook = http.get_webhook(webhook_id.0).await?;
|
|
|
|
webhook
|
|
.execute(http, false, move |w| {
|
|
w.content(&msg).username(username);
|
|
|
|
if let Some(profile_pic_url) = profile_pic_url {
|
|
w.avatar_url(profile_pic_url);
|
|
}
|
|
|
|
if let Some(attachments) = attachments {
|
|
w.add_files(attachments.clone());
|
|
}
|
|
|
|
w
|
|
})
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn send_private_message(
|
|
http: &Http,
|
|
src_username: &str,
|
|
dest: UserId,
|
|
msg: &str,
|
|
attachments: &Option<Vec<AttachmentType<'_>>>,
|
|
) -> error::Result<()> {
|
|
let dest_user = dest.to_user(http).await?;
|
|
|
|
let mut dm_message = MessageBuilder::new();
|
|
|
|
dm_message.push_bold_line_safe(format!("{} has sent you a DM:", src_username));
|
|
dm_message.push(msg);
|
|
|
|
dest_user
|
|
.dm(http, |msg| {
|
|
msg.content(dm_message);
|
|
|
|
if let Some(attachments) = attachments {
|
|
msg.add_files(attachments.clone());
|
|
}
|
|
|
|
msg
|
|
})
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn send_to_host_channel(
|
|
http: &Http,
|
|
guild: &Guild,
|
|
global_data: &GlobalData,
|
|
msg: WoxlfMessage<'_>,
|
|
) -> error::Result<()> {
|
|
let source = match &msg.source {
|
|
MessageSource::Player(player_data) => {
|
|
let name = guild
|
|
.members
|
|
.get(&UserId::from(player_data.discord_id))
|
|
.unwrap()
|
|
.display_name();
|
|
|
|
name.to_string()
|
|
}
|
|
MessageSource::Host => "Host".to_string(),
|
|
MessageSource::Automated => "Automated".to_string(),
|
|
};
|
|
|
|
let dest = match &msg.median {
|
|
Median::DirectMessage => {
|
|
if let MessageDest::Player(dest_player) = &msg.dest {
|
|
let name = guild
|
|
.members
|
|
.get(&UserId::from(dest_player.discord_id))
|
|
.unwrap()
|
|
.display_name();
|
|
format!(" to {} ({})", dest_player.codename, name)
|
|
} else {
|
|
"".to_string()
|
|
}
|
|
}
|
|
_ => "".to_string(),
|
|
};
|
|
|
|
let host_channel_username = format!(
|
|
"{} ({}){}",
|
|
msg.get_message_username(global_data)?,
|
|
source,
|
|
dest
|
|
);
|
|
|
|
send_webhook_msg(
|
|
http,
|
|
WebhookId::from(global_data.cfg.discord_config.host_webhook_id),
|
|
&host_channel_username,
|
|
Some(msg.get_profile_pic(global_data)?),
|
|
&msg.content,
|
|
&msg.attachments,
|
|
)
|
|
.await?;
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn send_message(
|
|
ctx: &Context,
|
|
global_data: &GlobalData,
|
|
msg: &WoxlfMessage<'_>,
|
|
dest_player: &PlayerData,
|
|
) -> Result<(), WoxlfError> {
|
|
match &msg.median {
|
|
Median::Webhook => {
|
|
send_webhook_msg(
|
|
&ctx.http,
|
|
WebhookId::from(dest_player.channel_webhook_id),
|
|
&msg.get_message_username(global_data)?,
|
|
Some(msg.get_profile_pic(global_data)?),
|
|
&msg.content,
|
|
&msg.attachments,
|
|
)
|
|
.await?;
|
|
}
|
|
Median::DirectMessage => {
|
|
send_private_message(
|
|
&ctx.http,
|
|
&msg.get_message_username(global_data)?,
|
|
UserId(dest_player.discord_id),
|
|
&msg.content,
|
|
&msg.attachments,
|
|
)
|
|
.await?;
|
|
}
|
|
Median::StandardMessage => {
|
|
let channel = ChannelId::from(dest_player.channel);
|
|
channel.say(&ctx.http(), &msg.content).await?;
|
|
}
|
|
};
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn dispatch_message(
|
|
ctx: &Context,
|
|
global_data: &mut GlobalData,
|
|
msg: WoxlfMessage<'_>,
|
|
) -> error::Result<()> {
|
|
let data = ctx.data.read().await;
|
|
let listeners = data.get::<Listeners>().unwrap();
|
|
let mut listeners = listeners.lock().await;
|
|
|
|
if listeners
|
|
.process_event(ctx, global_data, WoxlfEvent::Chat(msg.clone()))
|
|
.await?
|
|
== EventStatus::Canceled
|
|
{
|
|
return Ok(());
|
|
};
|
|
|
|
let msg_tasks = global_data
|
|
.game_state()?
|
|
.player_data
|
|
.iter()
|
|
.filter(|player| filter_source_channel(player, &msg.source))
|
|
.filter(|player| match &msg.dest {
|
|
MessageDest::Player(dest_user) => dest_user.discord_id == player.discord_id,
|
|
MessageDest::Host => false,
|
|
MessageDest::Broadcast => true,
|
|
})
|
|
.map(|p| send_message(ctx, global_data, &msg, p));
|
|
|
|
let results: Result<(), WoxlfError> = futures::future::join_all(msg_tasks)
|
|
.await
|
|
.into_iter()
|
|
.collect();
|
|
|
|
results?;
|
|
|
|
Ok(())
|
|
}
|