use crate::game::global_data::GlobalData; use crate::game::message_router::{ dispatch_message, Median, MessageDest, MessageSource, WoxlfMessage, }; use crate::game::Phase; use serenity::client::Context; use serenity::framework::standard::macros::{command, group}; use serenity::framework::standard::{Args, CommandResult}; use serenity::model::channel::Message; use serenity::model::id::{ChannelId, UserId}; use serenity::utils::MessageBuilder; #[group] #[commands(vote, status, players, whisper)] struct Player; #[command] #[only_in(guilds)] #[description = "vote another subject for termination. $vote "] async fn vote(ctx: &Context, msg: &Message, args: Args) -> CommandResult { let mut data = ctx.data.write().await; let global_data = data.get_mut::().unwrap(); let guild = msg.guild(&ctx.cache).unwrap(); let mut global_data = global_data.lock().await; if global_data.game_state_mut()?.current_phase != Phase::Day { msg.reply( &ctx.http, format!( "You can only vote during the {} phase.", global_data.game_cfg()?.vote_phase_name ), ) .await .unwrap(); return Ok(()); } if global_data .game_state_mut()? .get_player_from_channel(msg.channel_id.0) .is_some() { let target_player = global_data .game_state_mut()? .get_player_by_codename(args.rest()); if let Some(target_player) = target_player { let vote_channel = guild .channels .get(&ChannelId::from( global_data.cfg.discord_config.vote_channel, )) .unwrap(); let player_data = global_data .game_state_mut()? .get_player_from_channel_mut(msg.channel_id.0) .unwrap(); player_data.cast_vote(target_player.discord_id); // borrow as immutable let player_data = global_data .game_state()? .get_player_from_channel(msg.channel_id.0) .unwrap(); let vote_msg = global_data.templates()?.build_vote_message( &global_data, player_data, &target_player, )?; vote_channel .id() .send_message(&ctx.http, |m| m.content(vote_msg)) .await?; } else { msg.reply(&ctx.http, "Target not found!").await.unwrap(); } } else { msg.reply( &ctx.http, "This command needs to be run in a game channel, goober", ) .await?; } global_data.save_game_state().unwrap(); Ok(()) } #[command] #[only_in(guilds)] #[description = "Get the game status."] async fn status(ctx: &Context, msg: &Message, _args: Args) -> CommandResult { let mut data = ctx.data.write().await; let global_data = data.get_mut::().unwrap(); let mut global_data = global_data.lock().await; let mut msg_builder = MessageBuilder::new(); msg_builder.push( global_data .templates()? .build_satus_message(&global_data) .unwrap(), ); if global_data.game_state_mut()?.current_phase == Phase::Day { msg_builder.push_line( global_data .templates()? .build_vote_tally(&global_data) .unwrap(), ); } msg.reply(&ctx.http, msg_builder.build()).await.unwrap(); Ok(()) } #[command] #[only_in(guilds)] #[description = "Get the other players in the game."] async fn players(ctx: &Context, msg: &Message, _args: Args) -> CommandResult { let data = ctx.data.read().await; let global_data = data.get::().unwrap(); let global_data = global_data.lock().await; let mut msg_builder = MessageBuilder::new(); msg_builder.push_line(&global_data.game_cfg()?.player_group_name); for player in &global_data.game_state()?.player_data { let alive_status = if !player.alive { " (Dead) " } else { "" }; msg_builder .push("* ") .push(&player.codename) .push(alive_status); if msg.channel_id.0 == global_data.cfg.discord_config.host_channel { let guild = msg.guild(&ctx.cache).unwrap(); let member = guild.members.get(&UserId::from(player.discord_id)).unwrap(); msg_builder.push_line(format!( " ({}) [{} {}]", member.display_name(), player.role, player.role.seer_color() )); } else { msg_builder.push_line(""); } } msg.reply(&ctx.http, msg_builder.build()).await.unwrap(); Ok(()) } #[command] #[aliases("pm", "w")] #[description = "Send a private message to another player."] async fn whisper(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { let data = ctx.data.read().await; let global_data = data.get::().unwrap(); let mut global_data = global_data.lock().await; if !global_data.game_cfg()?.whispers_allowed { msg.reply(&ctx.http, "No private messages are allowed in this game") .await?; return Ok(()); } if args.len() < 2 { msg.reply(&ctx.http, "Need a recipient and message!") .await?; } else { let target = args.single::()?; 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, &mut global_data, woxlf_msg).await?; } else { msg.reply( &ctx.http, format!("Could not find a player with codename {}.", target), ) .await?; } } Ok(()) }