use serenity::framework::standard::macros::{command, group}; use serenity::framework::standard::{Args, CommandResult}; use serenity::framework::StandardFramework; use serenity::model::id::ChannelId; use serenity::model::prelude::{Message, UserId}; use serenity::prelude::Context; use serenity::utils::MessageBuilder; use crate::data::{GlobalData, MessageSource, Phase}; use crate::helper; use crate::helper::{ build_system_message, clear_game_state, get_phase_end_timestamp, print_game_status, save_game_state, send_msg_to_player_channels, }; #[group] #[commands(start, say, end, broadcast, next_phase, terminate, add_time)] struct Host; #[command] #[only_in(guilds)] #[allowed_roles("wolfx host")] async fn start(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { msg.channel_id.say(&ctx.http, "Starting game").await?; let mut data = ctx.data.write().await; let global_data = data.get_mut::().unwrap(); let guild = msg.guild(&ctx.cache).await.unwrap(); let mut global_data = global_data.lock().await; clear_game_state(&mut global_data).unwrap(); let duration = match args.single::() { Ok(d) => d, Err(_) => { msg.reply(&ctx.http, "Error parsing phase duration!") .await .unwrap(); return Ok(()); } }; global_data.game_state.phase_end_time = get_phase_end_timestamp(duration); for player in args.iter::().flatten() { if let Some(discord_user) = guild.members.get(&UserId::from(player)) { helper::add_user_to_game(ctx, &guild, &mut global_data, discord_user).await?; } else { msg.reply( &ctx.http, format!("User {} is invalid or not in this server!", player), ) .await?; break; } } save_game_state(&global_data).unwrap(); Ok(()) } #[command] #[only_in(guilds)] #[allowed_roles("wolfx host")] async fn end(ctx: &Context, msg: &Message, mut _args: Args) -> CommandResult { let mut data = ctx.data.write().await; let global_data = data.get_mut::().unwrap(); let guild = msg.guild(&ctx.cache).await.unwrap(); let mut global_data = global_data.lock().await; for player_data in &global_data.game_state.player_data { let channel = guild .channels .get(&ChannelId::from(player_data.channel)) .unwrap(); channel.delete(&ctx.http).await?; } clear_game_state(&mut global_data).unwrap(); msg.reply(&ctx.http, "Game ended!").await.unwrap(); Ok(()) } #[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::().unwrap(); let guild = msg.guild(&ctx.cache).await.unwrap(); let global_data = global_data.lock().await; let msg = format!("**wOxlf **> {}", args.rest()); send_msg_to_player_channels(ctx, &guild, &global_data, MessageSource::Host, &msg, false).await; Ok(()) } #[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::().unwrap(); let guild = msg.guild(&ctx.cache).await.unwrap(); let global_data = global_data.lock().await; let msg = build_system_message(args.rest()); send_msg_to_player_channels(ctx, &guild, &global_data, MessageSource::Host, &msg, true).await; Ok(()) } #[command] #[only_in(guilds)] #[allowed_roles("wolfx host")] 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::().unwrap(); let guild = msg.guild(&ctx.cache).await.unwrap(); let mut global_data = global_data.lock().await; let duration = match args.single::() { Ok(d) => d, Err(_) => { msg.reply(&ctx.http, "Error parsing phase duration!") .await .unwrap(); return Ok(()); } }; global_data.game_state.next_phase(); global_data.game_state.phase_end_time = get_phase_end_timestamp(duration); let broadcast = MessageBuilder::new() .push_line(args.rest()) .push_line("") .push(print_game_status(&global_data.game_state)) .build(); let broadcast = build_system_message(&broadcast); send_msg_to_player_channels( ctx, &guild, &global_data, MessageSource::Host, &broadcast, true, ) .await; if global_data.game_state.current_phase == Phase::Day { let vote_channel = guild .channels .get(&ChannelId::from(global_data.cfg.vote_channel)) .unwrap(); vote_channel .send_message(&ctx.http, |m| { m.content(format!( "**DAY {} VOTES:**", global_data.game_state.phase_number )) }) .await .unwrap(); } msg.reply( &ctx.http, format!( "Phase has been cycled to {}.", &global_data.game_state.current_phase ), ) .await .unwrap(); save_game_state(&global_data).unwrap(); Ok(()) } #[command] #[only_in(guilds)] #[allowed_roles("wolfx host")] async fn terminate(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).await.unwrap(); let mut global_data = global_data.lock().await; let target = args.rest().to_lowercase(); let index = global_data .game_state .player_data .iter() .position(|p| p.codename.to_lowercase() == target); if let Some(index) = index { let player = global_data.game_state.player_data.remove(index); let player_channel = guild .channels .get(&ChannelId::from(player.channel)) .unwrap(); player_channel.delete(&ctx.http).await.unwrap(); msg.reply( &ctx.http, format!("{} has been terminated.", player.codename), ) .await .unwrap(); } else { msg.reply(&ctx.http, "No subject found with that codename.") .await .unwrap(); } save_game_state(&global_data).unwrap(); Ok(()) } #[command] #[only_in(guilds)] #[allowed_roles("wolfx host")] 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::().unwrap(); let guild = msg.guild(&ctx.cache).await.unwrap(); let mut global_data = global_data.lock().await; global_data.game_state.next_phase(); let duration = match args.single::() { Ok(d) => d, Err(_) => { msg.reply(&ctx.http, "Error parsing phase duration!") .await .unwrap(); return Ok(()); } }; global_data.game_state.add_time_to_phase(duration); let broadcast = MessageBuilder::new() .push_line("EXPERIMENT PHASE HAS BEEN EXTENDED!!!") .push_line("") .push(print_game_status(&global_data.game_state)) .build(); let broadcast = build_system_message(&broadcast); send_msg_to_player_channels( ctx, &guild, &global_data, MessageSource::Host, &broadcast, true, ) .await; msg.reply(&ctx.http, "Phase has been updated") .await .unwrap(); save_game_state(&global_data).unwrap(); Ok(()) } #[group] #[commands(vote, status)] struct Player; #[command] #[only_in(guilds)] 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).await.unwrap(); let mut global_data = global_data.lock().await; if global_data.game_state.current_phase != Phase::Day { msg.reply( &ctx.http, "You can only select subject for termination during the day!", ) .await .unwrap(); return Ok(()); } if global_data .game_state .get_player_from_channel(msg.channel_id.0) .is_some() { let target_player = global_data.game_state.get_player_by_codename(args.rest()); if let Some(target_player) = target_player { let vote_channel = guild .channels .get(&ChannelId::from(global_data.cfg.vote_channel)) .unwrap(); let player_data = global_data .game_state .get_player_from_channel_mut(msg.channel_id.0) .unwrap(); player_data.vote_target = Some(target_player.channel); vote_channel .send_message(&ctx.http, |m| { m.content(format!( "{} has selected {} for termination", &player_data.codename, target_player.codename )) }) .await .unwrap(); } else { msg.reply(&ctx.http, "Subject not found!").await.unwrap(); } } else { msg.reply( &ctx.http, "This command needs to be run in a game channel, goober", ) .await .unwrap(); } save_game_state(&global_data).unwrap(); Ok(()) } #[command] #[only_in(guilds)] async fn status(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(print_game_status(&global_data.game_state)); if global_data.game_state.current_phase == Phase::Day { msg_builder.push_line(""); let vote_tallies = global_data.game_state.get_vote_tallies(); if vote_tallies.is_empty() { msg_builder.push_line("NO TERMINATION VOTES HAVE BEEN CAST"); } else { msg_builder.push_line("TERMINATION VOTE TALLIES:"); for (player, tally) in global_data.game_state.get_vote_tallies() { msg_builder.push_line(format!("{}: {}", player, tally)); } } } msg.reply(&ctx.http, msg_builder.build()).await.unwrap(); Ok(()) } pub fn command_framework() -> StandardFramework { StandardFramework::new() .configure(|c| c.prefix("!")) .group(&HOST_GROUP) .group(&PLAYER_GROUP) }