Bit of refactoring and cleanup
+ Split command groups into their own files + Upped version + Clippy + fmtmain
parent
275f5c9305
commit
66442efae6
|
@ -2302,7 +2302,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "woxlf"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"chrono",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "woxlf"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
|
|
@ -1,21 +1,6 @@
|
|||
use chrono::Duration;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use rand::prelude::SliceRandom;
|
||||
use rand::thread_rng;
|
||||
use serenity::framework::standard::macros::{command, group, help, hook};
|
||||
use serenity::framework::standard::{
|
||||
help_commands, Args, CommandGroup, CommandResult, HelpOptions,
|
||||
};
|
||||
use serenity::framework::StandardFramework;
|
||||
use serenity::model::guild::Member;
|
||||
use serenity::model::id::ChannelId;
|
||||
use serenity::model::prelude::{Message, UserId};
|
||||
use serenity::prelude::Context;
|
||||
use serenity::utils::MessageBuilder;
|
||||
|
||||
use crate::discord::helper::{add_user_to_game, parse_duration_arg};
|
||||
use crate::error::{Result, WoxlfError};
|
||||
use crate::error;
|
||||
use crate::error::WoxlfError;
|
||||
use crate::game::global_data::GlobalData;
|
||||
use crate::game::message_router::{
|
||||
dispatch_message, Median, MessageDest, MessageSource, WoxlfMessage,
|
||||
|
@ -24,11 +9,19 @@ use crate::game::player_data::PlayerData;
|
|||
use crate::game::role::Role;
|
||||
use crate::game::Phase;
|
||||
use crate::messages::DiscordUser;
|
||||
use chrono::Duration;
|
||||
use rand::prelude::SliceRandom;
|
||||
use rand::thread_rng;
|
||||
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::guild::Member;
|
||||
use serenity::model::id::{ChannelId, UserId};
|
||||
use serenity::utils::MessageBuilder;
|
||||
|
||||
#[group]
|
||||
#[commands(
|
||||
start, say, end, broadcast, next_phase, kill, add_time, test_theme, whisper
|
||||
)]
|
||||
#[commands(start, say, end, broadcast, next_phase, kill, add_time, test_theme)]
|
||||
struct Host;
|
||||
|
||||
#[command]
|
||||
|
@ -47,18 +40,18 @@ async fn start(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
|||
|
||||
global_data.start_game(&game_name, Phase::Night, duration.into())?;
|
||||
|
||||
let players: Result<Vec<&Member>> = args
|
||||
let players: error::Result<Vec<&Member>> = args
|
||||
.iter::<String>()
|
||||
.flatten()
|
||||
.map(|discord_id| {
|
||||
let discord_id = match discord_id.parse::<u64>() {
|
||||
let discord_id = match discord_id.parse::<UserId>() {
|
||||
Ok(discord_id) => discord_id,
|
||||
Err(_) => {
|
||||
return Err(WoxlfError::DiscordIdParseError(discord_id));
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(discord_user) = guild.members.get(&UserId::from(discord_id)) {
|
||||
if let Some(discord_user) = guild.members.get(&discord_id) {
|
||||
Ok(discord_user)
|
||||
} else {
|
||||
Err(WoxlfError::DiscordIdParseError(discord_id.to_string()))
|
||||
|
@ -306,52 +299,6 @@ async fn kill(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[command]
|
||||
#[only_in(guilds)]
|
||||
#[allowed_roles("wolfx host")]
|
||||
async fn add_time(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||
let data = ctx.data.read().await;
|
||||
let global_data = data.get::<GlobalData>().unwrap();
|
||||
|
||||
let mut global_data = global_data.lock().await;
|
||||
|
||||
let duration = parse_duration_arg(&mut args).await?;
|
||||
|
||||
global_data
|
||||
.game_state_mut()?
|
||||
.add_time_to_phase(duration.into());
|
||||
|
||||
let broadcast = MessageBuilder::new()
|
||||
.push(
|
||||
global_data
|
||||
.templates()?
|
||||
.build_phase_extend_message(&global_data)?,
|
||||
)
|
||||
.push_line("")
|
||||
.push(global_data.templates()?.build_satus_message(&global_data)?)
|
||||
.build();
|
||||
|
||||
let broadcast = global_data
|
||||
.templates()?
|
||||
.build_announcement(&global_data, &broadcast)?;
|
||||
|
||||
let woxlf_msg = WoxlfMessage::default()
|
||||
.source(MessageSource::Automated)
|
||||
.dest(MessageDest::Broadcast)
|
||||
.median(Median::Webhook)
|
||||
.content(&broadcast)
|
||||
.clone();
|
||||
|
||||
dispatch_message(ctx, &mut global_data, woxlf_msg).await?;
|
||||
|
||||
msg.reply(&ctx.http, "Phase has been updated")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
global_data.save_game_state().unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command]
|
||||
#[only_in(guilds)]
|
||||
#[allowed_roles("wolfx host")]
|
||||
|
@ -494,260 +441,48 @@ async fn test_theme(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[group]
|
||||
#[commands(vote, status, players)]
|
||||
struct Player;
|
||||
|
||||
#[command]
|
||||
#[only_in(guilds)]
|
||||
#[description = "vote another subject for termination. $vote <code_name>"]
|
||||
async fn vote(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||
let mut data = ctx.data.write().await;
|
||||
let global_data = data.get_mut::<GlobalData>().unwrap();
|
||||
let guild = msg.guild(&ctx.cache).unwrap();
|
||||
#[allowed_roles("wolfx host")]
|
||||
async fn add_time(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||
let data = ctx.data.read().await;
|
||||
let global_data = data.get::<GlobalData>().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
|
||||
),
|
||||
let duration = parse_duration_arg(&mut args).await?;
|
||||
|
||||
global_data
|
||||
.game_state_mut()?
|
||||
.add_time_to_phase(duration.into());
|
||||
|
||||
let broadcast = MessageBuilder::new()
|
||||
.push(
|
||||
global_data
|
||||
.templates()?
|
||||
.build_phase_extend_message(&global_data)?,
|
||||
)
|
||||
.push_line("")
|
||||
.push(global_data.templates()?.build_satus_message(&global_data)?)
|
||||
.build();
|
||||
|
||||
let broadcast = global_data
|
||||
.templates()?
|
||||
.build_announcement(&global_data, &broadcast)?;
|
||||
|
||||
let woxlf_msg = WoxlfMessage::default()
|
||||
.source(MessageSource::Automated)
|
||||
.dest(MessageDest::Broadcast)
|
||||
.median(Median::Webhook)
|
||||
.content(&broadcast)
|
||||
.clone();
|
||||
|
||||
dispatch_message(ctx, &mut global_data, woxlf_msg).await?;
|
||||
|
||||
msg.reply(&ctx.http, "Phase has been updated")
|
||||
.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::<GlobalData>().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::<GlobalData>().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::<GlobalData>().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::<String>()?;
|
||||
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(())
|
||||
}
|
||||
|
||||
#[help]
|
||||
#[individual_command_tip = "If you want more information about a specific command, just pass the command as argument."]
|
||||
#[command_not_found_text = "Could not find: `{}`."]
|
||||
#[max_levenshtein_distance(3)]
|
||||
#[indention_prefix = "+"]
|
||||
#[lacking_role = "Strike"]
|
||||
#[wrong_channel = "Strike"]
|
||||
async fn help(
|
||||
context: &Context,
|
||||
msg: &Message,
|
||||
args: Args,
|
||||
help_options: &'static HelpOptions,
|
||||
groups: &[&'static CommandGroup],
|
||||
owners: HashSet<UserId>,
|
||||
) -> CommandResult {
|
||||
let _ = help_commands::with_embeds(context, msg, args, help_options, groups, owners).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[hook]
|
||||
async fn handle_errors(
|
||||
ctx: &Context,
|
||||
msg: &Message,
|
||||
command_name: &str,
|
||||
command_result: CommandResult,
|
||||
) {
|
||||
match command_result {
|
||||
Ok(()) => println!("Successfully processed command '{}'", command_name),
|
||||
Err(err) => {
|
||||
let reply_msg = format!("Command '{}' returned an error. {}", command_name, err,);
|
||||
println!("{}", reply_msg);
|
||||
msg.reply(&ctx.http, reply_msg).await.unwrap();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn command_framework() -> StandardFramework {
|
||||
StandardFramework::new()
|
||||
.configure(|c| c.prefix('$'))
|
||||
.group(&HOST_GROUP)
|
||||
.group(&PLAYER_GROUP)
|
||||
.help(&HELP)
|
||||
.after(handle_errors)
|
||||
}
|
|
@ -1,3 +1,59 @@
|
|||
pub mod commands;
|
||||
use serenity::client::Context;
|
||||
use serenity::framework::standard::macros::{help, hook};
|
||||
use serenity::framework::standard::{
|
||||
help_commands, Args, CommandGroup, CommandResult, HelpOptions,
|
||||
};
|
||||
use serenity::framework::StandardFramework;
|
||||
use serenity::model::channel::Message;
|
||||
use serenity::model::id::UserId;
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub mod event_handler;
|
||||
pub mod helper;
|
||||
mod host;
|
||||
mod players;
|
||||
|
||||
#[help]
|
||||
#[individual_command_tip = "If you want more information about a specific command, just pass the command as argument."]
|
||||
#[command_not_found_text = "Could not find: `{}`."]
|
||||
#[max_levenshtein_distance(3)]
|
||||
#[indention_prefix = "+"]
|
||||
#[lacking_role = "Strike"]
|
||||
#[wrong_channel = "Strike"]
|
||||
async fn help(
|
||||
context: &Context,
|
||||
msg: &Message,
|
||||
args: Args,
|
||||
help_options: &'static HelpOptions,
|
||||
groups: &[&'static CommandGroup],
|
||||
owners: HashSet<UserId>,
|
||||
) -> CommandResult {
|
||||
let _ = help_commands::with_embeds(context, msg, args, help_options, groups, owners).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[hook]
|
||||
async fn handle_errors(
|
||||
ctx: &Context,
|
||||
msg: &Message,
|
||||
command_name: &str,
|
||||
command_result: CommandResult,
|
||||
) {
|
||||
match command_result {
|
||||
Ok(()) => println!("Successfully processed command '{}'", command_name),
|
||||
Err(err) => {
|
||||
let reply_msg = format!("Command '{}' returned an error. {}", command_name, err,);
|
||||
println!("{}", reply_msg);
|
||||
msg.reply(&ctx.http, reply_msg).await.unwrap();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn command_framework() -> StandardFramework {
|
||||
StandardFramework::new()
|
||||
.configure(|c| c.prefix('$'))
|
||||
.group(&host::HOST_GROUP)
|
||||
.group(&players::PLAYER_GROUP)
|
||||
.help(&HELP)
|
||||
.after(handle_errors)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,224 @@
|
|||
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 <code_name>"]
|
||||
async fn vote(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||
let mut data = ctx.data.write().await;
|
||||
let global_data = data.get_mut::<GlobalData>().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::<GlobalData>().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::<GlobalData>().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::<GlobalData>().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::<String>()?;
|
||||
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(())
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use chrono::Duration;
|
||||
use serenity::prelude::{Mutex, TypeMapKey};
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
|
@ -10,7 +11,6 @@ use crate::game::game_state::GameState;
|
|||
use crate::game::Phase;
|
||||
use crate::imgur::{get_album_images, Image};
|
||||
use crate::messages::MessageTemplates;
|
||||
use chrono::Duration;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GlobalData {
|
||||
|
|
|
@ -260,7 +260,10 @@ pub async fn send_message(
|
|||
}
|
||||
Median::DirectMessage => {
|
||||
let dm_msg = MessageBuilder::new()
|
||||
.push_bold_line_safe(format!("{} has sent you a private message:", &msg.get_message_username(global_data)?))
|
||||
.push_bold_line_safe(format!(
|
||||
"{} has sent you a private message:",
|
||||
&msg.get_message_username(global_data)?
|
||||
))
|
||||
.push(&msg.content)
|
||||
.build();
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::{Display, Formatter};
|
||||
use crate::game::listener::Listeners;
|
||||
use crate::game::role::spy::SpyListener;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
mod spy;
|
||||
|
||||
|
@ -120,6 +120,7 @@ impl Role {
|
|||
}
|
||||
|
||||
pub fn register_role_listener(&self, listeners: &mut Listeners) {
|
||||
#[allow(clippy::single_match)]
|
||||
match self {
|
||||
Role::Spy => listeners.add_listener(Box::new(SpyListener {})),
|
||||
_ => {}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
use crate::game::listener::{EventStatus, Listener, ListenerContext, Priority};
|
||||
use crate::game::message_router::{Median, MessageDest, MessageSource, send_private_message, WoxlfMessage};
|
||||
use crate::game::message_router::{
|
||||
send_private_message, Median, MessageDest, MessageSource, WoxlfMessage,
|
||||
};
|
||||
use crate::game::role::Role;
|
||||
use rand::{thread_rng, Rng};
|
||||
use serenity::async_trait;
|
||||
use serenity::model::prelude::UserId;
|
||||
use serenity::utils::MessageBuilder;
|
||||
|
@ -35,7 +38,6 @@ impl Listener for SpyListener {
|
|||
return Ok(EventStatus::Okay);
|
||||
};
|
||||
|
||||
|
||||
let spy_player = ctx
|
||||
.data
|
||||
.game_state()?
|
||||
|
@ -44,19 +46,30 @@ impl Listener for SpyListener {
|
|||
.find(|p| p.alive && p.role == Role::Spy);
|
||||
|
||||
if let Some(spy_player) = spy_player {
|
||||
if spy_player.discord_id == dest_player.discord_id || spy_player.discord_id == src_player.discord_id {
|
||||
if spy_player.discord_id == dest_player.discord_id
|
||||
|| spy_player.discord_id == src_player.discord_id
|
||||
{
|
||||
return Ok(EventStatus::Okay);
|
||||
}
|
||||
|
||||
let msg_content = MessageBuilder::default()
|
||||
.push_bold_line_safe(format!(
|
||||
"{} Sent {} a private message:",
|
||||
src_player.codename, dest_player.codename
|
||||
))
|
||||
.push(msg.content.clone())
|
||||
.build();
|
||||
// 1/4 chance to intercept message
|
||||
if thread_rng().gen_bool(0.25) {
|
||||
let msg_content = MessageBuilder::default()
|
||||
.push_bold_line_safe(format!(
|
||||
"{} Sent {} a private message:",
|
||||
src_player.codename, dest_player.codename
|
||||
))
|
||||
.push(msg.content.clone())
|
||||
.build();
|
||||
|
||||
send_private_message(&ctx.ctx.http, UserId::from(spy_player.discord_id), &msg_content, &None).await?
|
||||
send_private_message(
|
||||
&ctx.ctx.http,
|
||||
UserId::from(spy_player.discord_id),
|
||||
&msg_content,
|
||||
&None,
|
||||
)
|
||||
.await?
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(EventStatus::Okay);
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::sync::Arc;
|
|||
use serenity::prelude::*;
|
||||
use structopt::StructOpt;
|
||||
|
||||
use discord::commands::command_framework;
|
||||
use discord::command_framework;
|
||||
use discord::event_handler::Handler;
|
||||
use game::global_data::GlobalData;
|
||||
|
||||
|
|
Loading…
Reference in New Issue