use std::fs::File; use std::io::prelude::*; use rand::Rng; use serenity::framework::standard::CommandResult; use serenity::model::channel::{PermissionOverwrite, PermissionOverwriteType}; use serenity::model::guild::Member; use serenity::model::id::UserId; use serenity::model::prelude::ChannelId; use serenity::model::prelude::Guild; use serenity::model::Permissions; use serenity::prelude::Context; use serenity::utils::MessageBuilder; use crate::data::{BotConfig, GameState, GlobalData, MessageSource, PlayerData}; use std::time::UNIX_EPOCH; pub async fn send_msg_to_player_channels( ctx: &Context, guild: &Guild, global_data: &GlobalData, msg_source: MessageSource, msg: &str, pin: bool, ) { for player_data in &global_data.game_state.player_data { if let MessageSource::Player(channel_id) = msg_source { if channel_id == player_data.channel { continue; } } let channel = guild .channels .get(&ChannelId::from(player_data.channel)) .unwrap(); let msg = channel .send_message(&ctx.http, |m| m.content(&msg)) .await .unwrap(); if pin { // pin system messages msg.pin(&ctx.http).await.unwrap(); } } let host_channel = guild .channels .get(&ChannelId::from(global_data.cfg.host_channel)) .unwrap(); let source = match msg_source { MessageSource::Player(channel_id) => { let discord_id = global_data .game_state .get_player_from_channel(channel_id) .unwrap() .discord_id; let name = guild .members .get(&UserId::from(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))) .await .unwrap(); } pub fn save_game_state(global_data: &GlobalData) -> std::io::Result<()> { let s = toml::to_string_pretty(&global_data.game_state).unwrap(); let mut file = File::create(global_data.cfg.get_game_state_path())?; file.write_all(s.as_bytes()) } pub fn get_game_state(global_data: &mut GlobalData) -> std::io::Result<()> { let mut file = File::open(global_data.cfg.get_game_state_path())?; let mut data = String::new(); file.read_to_string(&mut data)?; global_data.game_state = toml::from_str(&data).unwrap(); Ok(()) } pub fn clear_game_state(global_data: &mut GlobalData) -> std::io::Result<()> { global_data.game_state.clear(); let state_path = global_data.cfg.get_game_state_path(); if state_path.exists() { std::fs::remove_file(global_data.cfg.get_game_state_path())?; } Ok(()) } pub fn generate_codename(config: &BotConfig) -> String { let mut rng = rand::thread_rng(); let occupation = &config.occupation[rng.gen_range(0..config.occupation.len())]; let adj = &config.adjective[rng.gen_range(0..config.adjective.len())]; format!("{} {}", adj, occupation) } pub async fn add_user_to_game( ctx: &Context, guild: &Guild, global_data: &mut GlobalData, discord_user: &Member, ) -> CommandResult { let mut codename = generate_codename(&global_data.cfg); while global_data.game_state.codename_exists(&codename) { codename = generate_codename(&global_data.cfg); } let channel = guild .create_channel(&ctx.http, |c| { c.category(&ChannelId::from(global_data.cfg.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 msg = channel.send_message(&ctx.http, |m| { m.content(MessageBuilder::new() .push("Welcome ") .mention(discord_user) .push_line(" to your WOxlf Terminal. You may use this terminal to communicate to other subjects.") .push_line("You will also use this terminal for choosing one of your fellow subjects for termination.") .push_line("Happy testing :)") .push_line("") .push("SUBJECT CODENAME: ") .push_line(&codename) .push(print_game_status(&global_data.game_state)) ) }).await?; channel.pin(&ctx.http, msg.id).await?; let player_data = PlayerData { channel: channel.id.0, discord_id: discord_user.user.id.0, vote_target: None, codename, }; global_data.game_state.player_data.push(player_data); Ok(()) } pub fn build_system_message(msg: &str) -> String { MessageBuilder::new() .push_bold_line("\\*\\*IMPORTANT wOxlf SYSTEM MESSAGE\\*\\*") .push_line("") .push_line(msg) .push_line("") .push_bold_line("\\*\\*END OF SYSTEM MESSAGE\\*\\*") .build() } pub fn print_game_status(game_state: &GameState) -> String { MessageBuilder::new() .push_line(format!( "CURRENT EXPERIMENT PHASE: {} {}", game_state.current_phase, game_state.phase_number )) .push_line(format!( "PHASE END TIME: {}", game_state.get_phase_end_time() )) .push_line(format!("PHASE ENDING {}", game_state.get_phase_countdown())) .build() } pub fn get_phase_end_timestamp(hours: u64) -> u64 { let end_time = std::time::SystemTime::now() + std::time::Duration::from_secs(hours * 60 * 60); end_time.duration_since(UNIX_EPOCH).unwrap().as_secs() }