Compare commits

..

No commits in common. "4b7570b24ca110dc4f09377face6aa59194e980e" and "2aec0847120c38ed2c5982bf0a40efa6ced3af3f" have entirely different histories.

11 changed files with 548 additions and 764 deletions

563
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
config = "0.13.3"
config = "0.12.0"
structopt = "0.3.26"
chrono = {version="0.4.19", features=["serde"]}
serde = "1.0.136"
@ -16,10 +16,9 @@ regex = "1.5.5"
futures = "0.3.21"
reqwest = "0.11.10"
tera = "1.15.0"
bitflags = "1.3.2"
[dependencies.serenity]
version = "0.11.5"
version = "0.10.10"
features = ["framework", "standard_framework"]
[dependencies.tokio]

View File

@ -15,12 +15,10 @@ pub struct Args {
pub struct GameConfig {
pub game_name: String,
pub bot_name: String,
pub bot_profile_pic: String,
pub vote_phase_name: String,
pub enemy_phase_name: String,
pub player_group_name: String,
pub profile_album_hash: String,
pub whispers_allowed: bool,
pub first_name: Vec<String>,
pub last_name: Vec<String>,
pub messages: MessageConfig,
@ -45,7 +43,6 @@ pub struct DiscordConfig {
pub host_webhook_id: u64,
pub vote_channel: u64,
pub category: u64,
pub guild_id: u64,
}
#[derive(Debug, Deserialize, Serialize, Clone)]

View File

@ -14,20 +14,16 @@ use serenity::model::prelude::{Message, UserId};
use serenity::prelude::Context;
use serenity::utils::MessageBuilder;
use crate::discord::helper::{add_user_to_game, parse_duration_arg};
use crate::discord::helper::{add_user_to_game, parse_duration_arg, send_msg_to_player_channels};
use crate::error::{Result, WoxlfError};
use crate::game::global_data::GlobalData;
use crate::game::message_router::{
dispatch_message, Median, MessageDest, MessageSource, WoxlfMessage,
};
use crate::game::player_data::PlayerData;
use crate::game::MessageSource;
use crate::game::Phase;
use crate::messages::DiscordUser;
#[group]
#[commands(
start, say, end, broadcast, next_phase, kill, add_time, test_theme, whisper
)]
#[commands(start, say, end, broadcast, next_phase, kill, add_time, test_theme)]
struct Host;
#[command]
@ -38,7 +34,7 @@ async fn start(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
let mut data = ctx.data.write().await;
let global_data = data.get_mut::<GlobalData>().unwrap();
let guild = msg.guild(&ctx.cache).unwrap();
let guild = msg.guild(&ctx.cache).await.unwrap();
let mut global_data = global_data.lock().await;
let game_name = args.single::<String>()?;
@ -140,7 +136,7 @@ async fn start(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
async fn end(ctx: &Context, msg: &Message, mut _args: Args) -> CommandResult {
let mut data = ctx.data.write().await;
let global_data = data.get_mut::<GlobalData>().unwrap();
let guild = msg.guild(&ctx.cache).unwrap();
let guild = msg.guild(&ctx.cache).await.unwrap();
let mut global_data = global_data.lock().await;
@ -163,20 +159,23 @@ async fn end(ctx: &Context, msg: &Message, mut _args: Args) -> CommandResult {
#[command]
#[only_in(guilds)]
#[allowed_roles("wolfx host")]
async fn say(ctx: &Context, _msg: &Message, args: Args) -> CommandResult {
let data = ctx.data.read().await;
let global_data = data.get::<GlobalData>().unwrap();
async fn say(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
let mut data = ctx.data.write().await;
let global_data = data.get_mut::<GlobalData>().unwrap();
let guild = msg.guild(&ctx.cache).await.unwrap();
let global_data = global_data.lock().await;
let mut global_data = global_data.lock().await;
let msg = WoxlfMessage::default()
.source(MessageSource::Host)
.dest(MessageDest::Broadcast)
.median(Median::Webhook)
.content(args.rest())
.clone();
dispatch_message(ctx, &global_data, msg).await?;
send_msg_to_player_channels(
ctx,
&guild,
&mut global_data,
MessageSource::Host,
args.rest(),
None,
false,
)
.await?;
Ok(())
}
@ -184,24 +183,27 @@ async fn say(ctx: &Context, _msg: &Message, args: Args) -> CommandResult {
#[command]
#[only_in(guilds)]
#[allowed_roles("wolfx host")]
async fn broadcast(ctx: &Context, _msg: &Message, args: Args) -> CommandResult {
let data = ctx.data.read().await;
let global_data = data.get::<GlobalData>().unwrap();
async fn broadcast(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
let mut data = ctx.data.write().await;
let global_data = data.get_mut::<GlobalData>().unwrap();
let guild = msg.guild(&ctx.cache).await.unwrap();
let global_data = global_data.lock().await;
let mut global_data = global_data.lock().await;
let broadcast = global_data
let msg = global_data
.templates()?
.build_announcement(&global_data, args.rest())?;
let woxlf_msg = WoxlfMessage::default()
.source(MessageSource::Automated)
.dest(MessageDest::Broadcast)
.content(&broadcast)
.median(Median::Webhook)
.clone();
dispatch_message(ctx, &global_data, woxlf_msg).await?;
send_msg_to_player_channels(
ctx,
&guild,
&mut global_data,
MessageSource::Automated,
&msg,
None,
true,
)
.await?;
Ok(())
}
@ -212,7 +214,7 @@ async fn broadcast(ctx: &Context, _msg: &Message, args: Args) -> CommandResult {
async fn next_phase(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
let mut data = ctx.data.write().await;
let global_data = data.get_mut::<GlobalData>().unwrap();
let guild = msg.guild(&ctx.cache).unwrap();
let guild = msg.guild(&ctx.cache).await.unwrap();
let mut global_data = global_data.lock().await;
@ -230,14 +232,16 @@ async fn next_phase(ctx: &Context, msg: &Message, mut args: Args) -> CommandResu
.templates()?
.build_announcement(&global_data, &broadcast)?;
let woxlf_msg = WoxlfMessage::default()
.source(MessageSource::Automated)
.dest(MessageDest::Broadcast)
.median(Median::Webhook)
.content(&broadcast)
.clone();
dispatch_message(ctx, &global_data, woxlf_msg).await?;
send_msg_to_player_channels(
ctx,
&guild,
&mut global_data,
MessageSource::Automated,
&broadcast,
None,
true,
)
.await?;
if global_data.game_state_mut()?.current_phase == Phase::Day {
let vote_channel = guild
@ -247,7 +251,6 @@ async fn next_phase(ctx: &Context, msg: &Message, mut args: Args) -> CommandResu
))
.unwrap();
vote_channel
.id()
.send_message(&ctx.http, |m| {
m.content(format!(
"**{} {} Votes:**",
@ -305,6 +308,7 @@ async fn kill(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
async fn add_time(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
let mut data = ctx.data.write().await;
let global_data = data.get_mut::<GlobalData>().unwrap();
let guild = msg.guild(&ctx.cache).await.unwrap();
let mut global_data = global_data.lock().await;
@ -328,14 +332,16 @@ async fn add_time(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult
.templates()?
.build_announcement(&global_data, &broadcast)?;
let woxlf_msg = WoxlfMessage::default()
.source(MessageSource::Automated)
.dest(MessageDest::Broadcast)
.median(Median::Webhook)
.content(&broadcast)
.clone();
dispatch_message(ctx, &global_data, woxlf_msg).await?;
send_msg_to_player_channels(
ctx,
&guild,
&mut global_data,
MessageSource::Automated,
&broadcast,
None,
true,
)
.await?;
msg.reply(&ctx.http, "Phase has been updated")
.await
@ -495,7 +501,7 @@ struct Player;
async fn vote(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
let mut data = ctx.data.write().await;
let global_data = data.get_mut::<GlobalData>().unwrap();
let guild = msg.guild(&ctx.cache).unwrap();
let guild = msg.guild(&ctx.cache).await.unwrap();
let mut global_data = global_data.lock().await;
@ -549,7 +555,6 @@ async fn vote(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
)?;
vote_channel
.id()
.send_message(&ctx.http, |m| m.content(vote_msg))
.await?;
} else {
@ -621,7 +626,7 @@ async fn players(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
.push(alive_status);
if msg.channel_id.0 == global_data.cfg.discord_config.host_channel {
let guild = msg.guild(&ctx.cache).unwrap();
let guild = msg.guild(&ctx.cache).await.unwrap();
let member = guild.members.get(&UserId::from(player.discord_id)).unwrap();
msg_builder.push_line(format!(" ({})", member.display_name()));
} else {
@ -634,59 +639,6 @@ async fn players(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
Ok(())
}
#[command]
#[aliases("pm", "w")]
#[description = "Send a private message to another player."]
async fn whisper(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
if args.len() < 2 {
msg.reply(&ctx.http, "Need a recipient and message!")
.await?;
} else {
let data = ctx.data.read().await;
let global_data = data.get::<GlobalData>().unwrap();
let global_data = global_data.lock().await;
let target = args.single::<String>()?;
let pm = args.rest();
let src_player = match global_data
.game_state()?
.get_player_from_discord_id(msg.author.id.0)
{
None => {
msg.reply(&ctx.http, "You are not in the game!").await?;
return Ok(());
}
Some(player) => player,
};
if let Some(target_player) = global_data.game_state()?.get_player_by_codename(&target) {
if src_player.discord_id == target_player.discord_id {
msg.reply(&ctx.http, "You can't send messages to yourself!")
.await?;
return Ok(());
}
let woxlf_msg = WoxlfMessage::default()
.source(MessageSource::Player(Box::new(src_player.clone())))
.dest(MessageDest::Player(Box::new(target_player.clone())))
.median(Median::DirectMessage)
.content(pm)
.clone();
dispatch_message(ctx, &global_data, woxlf_msg).await?;
} else {
msg.reply(
&ctx.http,
format!("Could not find a player with codename {}.", target),
)
.await?;
}
}
Ok(())
}
#[help]
#[individual_command_tip = "If you want more information about a specific command, just pass the command as argument."]
#[command_not_found_text = "Could not find: `{}`."]

View File

@ -1,13 +1,13 @@
use serenity::async_trait;
use serenity::client::{Context, EventHandler};
use serenity::http::AttachmentType;
use serenity::model::channel::Message;
use serenity::model::gateway::Ready;
use serenity::model::prelude::AttachmentType;
use serenity::utils::parse_emoji;
use crate::discord::helper::send_webhook_msg_to_player_channels;
use crate::game::global_data::GlobalData;
use crate::game::message_router::{dispatch_message, MessageDest};
use crate::game::message_router::{Median, MessageSource, WoxlfMessage};
use crate::game::MessageSource;
pub struct Handler {}
@ -26,7 +26,7 @@ impl EventHandler for Handler {
let global_data = data.get::<GlobalData>().unwrap();
let global_data = global_data.lock().await;
let mut global_data = global_data.lock().await;
if global_data.game_state.is_none() {
// no game in progress
@ -43,6 +43,7 @@ impl EventHandler for Handler {
return;
}
let guild = msg.guild(&ctx.cache).await.unwrap();
let user_msg = msg.content.clone();
let re = regex::Regex::new(r"<a?:.+:\d+>").unwrap();
@ -51,6 +52,7 @@ impl EventHandler for Handler {
if let Some(emoji) = parse_emoji(&emoji_cap[0]) {
if !msg
.guild(&ctx.cache)
.await
.unwrap()
.emojis
.contains_key(&emoji.id)
@ -64,18 +66,19 @@ impl EventHandler for Handler {
let attachments: Vec<AttachmentType> = msg
.attachments
.iter()
.map(|a| AttachmentType::Image((a.url).parse().unwrap()))
.map(|a| AttachmentType::Image(&a.url))
.collect();
let woxlf_msg = WoxlfMessage::default()
.source(MessageSource::Player(Box::new(player_data.clone())))
.dest(MessageDest::Broadcast)
.median(Median::Webhook)
.content(&user_msg)
.attachments(attachments)
.clone();
let msg_source = MessageSource::Player(Box::new(player_data.clone()));
dispatch_message(&ctx, &global_data, woxlf_msg)
send_webhook_msg_to_player_channels(
&ctx,
&guild,
&mut global_data,
msg_source,
&user_msg,
Some(attachments),
)
.await
.expect("Unable to send message to players");
}

View File

@ -1,8 +1,9 @@
use serenity::client::Context;
use serenity::framework::standard::Args;
use serenity::model::channel::{PermissionOverwrite, PermissionOverwriteType};
use serenity::http::{AttachmentType, Http};
use serenity::model::channel::{Message, PermissionOverwrite, PermissionOverwriteType};
use serenity::model::guild::{Guild, Member};
use serenity::model::id::ChannelId;
use serenity::model::id::{ChannelId, UserId};
use serenity::model::Permissions;
use crate::error;
@ -10,7 +11,201 @@ use crate::error::WoxlfError;
use crate::game::game_state::PhaseDuration;
use crate::game::global_data::GlobalData;
use crate::game::player_data::PlayerData;
use crate::game::MessageSource;
use crate::imgur::Image;
use serenity::prelude::SerenityError;
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
}
pub async fn send_msg_to_player_channels(
ctx: &Context,
guild: &Guild,
global_data: &mut GlobalData,
msg_source: MessageSource,
msg: &str,
attachment: Option<Vec<AttachmentType<'_>>>,
pin: bool,
) -> error::Result<()> {
let msg_tasks = global_data
.game_state_mut()?
.player_data
.iter()
.filter(|player| filter_source_channel(player, &msg_source))
.map(|player_data| {
let channel = guild
.channels
.get(&ChannelId::from(player_data.channel))
.unwrap();
channel.send_message(&ctx.http, |m| {
m.content(&msg);
if let Some(attachment) = attachment.clone() {
m.add_files(attachment);
}
m
})
});
let msgs: Result<Vec<Message>, SerenityError> = futures::future::join_all(msg_tasks)
.await
.into_iter()
.collect();
let msgs = msgs?;
if pin {
let pin_tasks = msgs.iter().map(|msg| msg.pin(&ctx.http));
let pins: Result<(), SerenityError> = futures::future::join_all(pin_tasks)
.await
.into_iter()
.collect();
pins?;
}
let host_channel = guild
.channels
.get(&ChannelId::from(
global_data.cfg.discord_config.host_channel,
))
.unwrap();
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(),
};
host_channel
.send_message(&ctx.http, |m| {
m.content(format!("({}): {}", source, msg));
if let Some(attachment) = attachment {
m.add_files(attachment);
}
m
})
.await?;
Ok(())
}
pub async fn send_webhook_msg(
http: &Http,
webhook_id: u64,
username: &str,
profile_pic_url: Option<String>,
msg: &str,
attachment: Option<Vec<AttachmentType<'_>>>,
) -> error::Result<()> {
let webhook = http.get_webhook(webhook_id).await?;
webhook
.execute(http, false, |w| {
w.content(&msg).username(username);
if let Some(profile_pic_url) = profile_pic_url {
w.avatar_url(profile_pic_url);
}
if let Some(attachment) = attachment.clone() {
w.add_files(attachment);
}
w
})
.await?;
Ok(())
}
pub async fn send_webhook_msg_to_player_channels(
ctx: &Context,
guild: &Guild,
global_data: &mut GlobalData,
msg_source: MessageSource,
msg: &str,
attachment: Option<Vec<AttachmentType<'_>>>,
) -> error::Result<()> {
let msg_username = match &msg_source {
MessageSource::Player(p) => p.codename.clone(),
MessageSource::Host => "Woxlf".to_string(),
MessageSource::Automated => "Woxlf System Message".to_string(),
};
let profile_pic = match &msg_source {
MessageSource::Player(p) => Some(p.profile_pic_url.clone()),
MessageSource::Host | MessageSource::Automated => None,
};
let msg_tasks = global_data
.game_state_mut()?
.player_data
.iter()
.filter(|player| filter_source_channel(player, &msg_source))
.map(|player_data| {
send_webhook_msg(
&ctx.http,
player_data.channel_webhook_id,
&msg_username,
profile_pic.clone(),
msg,
attachment.clone(),
)
});
let msgs: Result<(), WoxlfError> = futures::future::join_all(msg_tasks)
.await
.into_iter()
.collect();
msgs?;
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 host_channel_username = format!("{} ({})", msg_username, source);
send_webhook_msg(
&ctx.http,
global_data.cfg.discord_config.host_webhook_id,
&host_channel_username,
profile_pic,
msg,
attachment,
)
.await?;
Ok(())
}
pub async fn add_user_to_game(
ctx: &Context,
@ -42,7 +237,7 @@ pub async fn add_user_to_game(
.await?;
let allow =
Permissions::SEND_MESSAGES | Permissions::READ_MESSAGE_HISTORY | Permissions::VIEW_CHANNEL;
Permissions::SEND_MESSAGES | Permissions::READ_MESSAGE_HISTORY | Permissions::READ_MESSAGES;
let overwrite = PermissionOverwrite {
allow,

View File

@ -19,7 +19,6 @@ pub enum WoxlfError {
RanOutOfCodenames,
TemplateError(tera::Error),
RanOutOfProfilePics,
UnsupportedMsgMedium,
}
impl std::error::Error for WoxlfError {}
@ -42,9 +41,6 @@ impl Display for WoxlfError {
}
WoxlfError::TemplateError(e) => format!("Template error: {}", e),
WoxlfError::RanOutOfProfilePics => "Ran out of user profile pics".to_string(),
WoxlfError::UnsupportedMsgMedium => {
"Tried to send a message over an unsupported medium".to_string()
}
};
write!(f, "Woxlf Error: {}", msg)

View File

@ -1,315 +0,0 @@
use crate::error;
use crate::error::WoxlfError;
use crate::game::global_data::GlobalData;
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, GuildId, 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(())
}
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: &GlobalData,
msg: WoxlfMessage<'_>,
) -> error::Result<()> {
let guild_id = global_data.cfg.discord_config.guild_id;
let guild = GuildId::from(guild_id).to_guild_cached(&ctx.cache).unwrap();
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?;
send_to_host_channel(&ctx.http, &guild, global_data, msg).await?;
Ok(())
}

View File

@ -1,8 +1,9 @@
use serde::{Deserialize, Serialize};
use crate::game::player_data::PlayerData;
pub mod game_state;
pub mod global_data;
pub mod message_router;
pub mod player_data;
#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq, Copy)]
@ -16,3 +17,10 @@ impl Default for Phase {
Self::Night
}
}
#[derive(Debug, Clone)]
pub enum MessageSource {
Player(Box<PlayerData>),
Host,
Automated,
}

View File

@ -1,5 +1,6 @@
use std::sync::Arc;
use serenity::client::bridge::gateway::GatewayIntents;
use serenity::prelude::*;
use structopt::StructOpt;
@ -31,9 +32,10 @@ async fn main() {
.expect("Unable to open saved game state.");
}
let mut client = Client::builder(&bot_cfg.discord_config.token, GatewayIntents::all())
let mut client = Client::builder(&bot_cfg.discord_config.token)
.event_handler(Handler {})
.framework(command_framework())
.intents(GatewayIntents::all())
.type_map_insert::<GlobalData>(Arc::new(Mutex::new(global_data)))
.await
.expect("Err creating client");

View File

@ -8,9 +8,9 @@ use serenity::prelude::Mentionable;
use std::collections::HashMap;
use tera::{Tera, Value};
type TeraFnRet = Box<dyn Fn(&HashMap<String, Value>) -> tera::Result<Value> + Send + Sync>;
fn time_to_discord_time(time_flag: &str) -> TeraFnRet {
fn time_to_discord_time(
time_flag: &str,
) -> Box<dyn Fn(&HashMap<String, Value>) -> tera::Result<Value> + Send + Sync> {
let time_flag = time_flag.to_string();
Box::new(