use serenity::client::Context; use serenity::framework::standard::Args; use serenity::http::{AttachmentType, Http}; use serenity::model::channel::{Message, PermissionOverwrite, PermissionOverwriteType}; use serenity::model::guild::{Guild, Member}; use serenity::model::id::{ChannelId, UserId}; use serenity::model::Permissions; use crate::error; 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 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>>, 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, 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, msg: &str, attachment: Option>>, ) -> 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>>, ) -> 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, guild: &Guild, global_data: &mut GlobalData, discord_user: &Member, first_name: Option, last_name: Option, ) -> error::Result { if first_name == None && last_name == None { return Err(WoxlfError::RanOutOfCodenames); } let codename = global_data .templates()? .build_name(global_data, first_name, last_name) .unwrap(); let channel = guild .create_channel(&ctx.http, |c| { c.category(&ChannelId::from(global_data.cfg.discord_config.category)) .name(format!("{}'s Channel", discord_user.display_name())) }) .await?; let allow = Permissions::SEND_MESSAGES | Permissions::READ_MESSAGE_HISTORY | Permissions::READ_MESSAGES; let overwrite = PermissionOverwrite { allow, deny: Default::default(), kind: PermissionOverwriteType::Member(discord_user.user.id), }; channel.create_permission(&ctx.http, &overwrite).await?; let webhook = channel .create_webhook( &ctx.http, format!("{}'s Woxlf Webhook", discord_user.display_name()), ) .await?; let player_data = PlayerData { channel: channel.id.0, discord_id: discord_user.user.id.0, vote_target: None, codename, channel_webhook_id: webhook.id.0, profile_pic_url: global_data.get_profile_pic_url().await?, }; global_data .game_state_mut()? .player_data .push(player_data.clone()); Ok(player_data) } pub async fn parse_duration_arg(args: &mut Args) -> error::Result { match args.single::() { Ok(d) => Ok(d), Err(_) => Err(WoxlfError::DurationParseError), } }