wOxlf/src/discord/helper.rs

274 lines
7.3 KiB
Rust

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<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,
guild: &Guild,
global_data: &mut GlobalData,
discord_user: &Member,
first_name: Option<String>,
last_name: Option<String>,
) -> error::Result<PlayerData> {
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<PhaseDuration> {
match args.single::<PhaseDuration>() {
Ok(d) => Ok(d),
Err(_) => Err(WoxlfError::DurationParseError),
}
}