Added basic role handling
+ Games now have a config for which roles they have + Roles are assigned at game start + Roles don't function within the bot, except the spy + Added a spy listener + Clippy + fmtmain
parent
7af3bab486
commit
275f5c9305
|
@ -1,6 +1,7 @@
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use crate::game::role::Role;
|
||||||
use config::{Config, File};
|
use config::{Config, File};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
@ -21,6 +22,7 @@ pub struct GameConfig {
|
||||||
pub player_group_name: String,
|
pub player_group_name: String,
|
||||||
pub profile_album_hash: String,
|
pub profile_album_hash: String,
|
||||||
pub whispers_allowed: bool,
|
pub whispers_allowed: bool,
|
||||||
|
pub roles: Vec<Role>,
|
||||||
pub first_name: Vec<String>,
|
pub first_name: Vec<String>,
|
||||||
pub last_name: Vec<String>,
|
pub last_name: Vec<String>,
|
||||||
pub messages: MessageConfig,
|
pub messages: MessageConfig,
|
||||||
|
|
|
@ -21,6 +21,7 @@ use crate::game::message_router::{
|
||||||
dispatch_message, Median, MessageDest, MessageSource, WoxlfMessage,
|
dispatch_message, Median, MessageDest, MessageSource, WoxlfMessage,
|
||||||
};
|
};
|
||||||
use crate::game::player_data::PlayerData;
|
use crate::game::player_data::PlayerData;
|
||||||
|
use crate::game::role::Role;
|
||||||
use crate::game::Phase;
|
use crate::game::Phase;
|
||||||
use crate::messages::DiscordUser;
|
use crate::messages::DiscordUser;
|
||||||
|
|
||||||
|
@ -93,10 +94,15 @@ async fn start(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||||
|
|
||||||
profile_pics.shuffle(&mut thread_rng());
|
profile_pics.shuffle(&mut thread_rng());
|
||||||
|
|
||||||
|
let mut roles = global_data.game_cfg()?.roles.clone();
|
||||||
|
|
||||||
|
roles.shuffle(&mut thread_rng());
|
||||||
|
|
||||||
for player in players {
|
for player in players {
|
||||||
let first_name = first_names.pop();
|
let first_name = first_names.pop();
|
||||||
let last_name = last_names.pop();
|
let last_name = last_names.pop();
|
||||||
let profile_pic_url = profile_pics.pop();
|
let profile_pic_url = profile_pics.pop();
|
||||||
|
let role = roles.pop();
|
||||||
add_user_to_game(
|
add_user_to_game(
|
||||||
ctx,
|
ctx,
|
||||||
&guild,
|
&guild,
|
||||||
|
@ -105,6 +111,7 @@ async fn start(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||||
first_name,
|
first_name,
|
||||||
last_name,
|
last_name,
|
||||||
profile_pic_url,
|
profile_pic_url,
|
||||||
|
role,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
@ -369,6 +376,7 @@ async fn test_theme(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||||
profile_pic_url: "".to_string(),
|
profile_pic_url: "".to_string(),
|
||||||
channel_webhook_id: 0,
|
channel_webhook_id: 0,
|
||||||
alive: true,
|
alive: true,
|
||||||
|
role: Role::Villager,
|
||||||
};
|
};
|
||||||
|
|
||||||
let player_1_discord = DiscordUser {
|
let player_1_discord = DiscordUser {
|
||||||
|
@ -384,6 +392,7 @@ async fn test_theme(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||||
profile_pic_url: "".to_string(),
|
profile_pic_url: "".to_string(),
|
||||||
channel_webhook_id: 0,
|
channel_webhook_id: 0,
|
||||||
alive: false,
|
alive: false,
|
||||||
|
role: Role::Villager,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut players = vec![test_player_1.clone(), test_player_2.clone()];
|
let mut players = vec![test_player_1.clone(), test_player_2.clone()];
|
||||||
|
@ -623,7 +632,12 @@ async fn players(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
|
||||||
if msg.channel_id.0 == global_data.cfg.discord_config.host_channel {
|
if msg.channel_id.0 == global_data.cfg.discord_config.host_channel {
|
||||||
let guild = msg.guild(&ctx.cache).unwrap();
|
let guild = msg.guild(&ctx.cache).unwrap();
|
||||||
let member = guild.members.get(&UserId::from(player.discord_id)).unwrap();
|
let member = guild.members.get(&UserId::from(player.discord_id)).unwrap();
|
||||||
msg_builder.push_line(format!(" ({})", member.display_name()));
|
msg_builder.push_line(format!(
|
||||||
|
" ({}) [{} {}]",
|
||||||
|
member.display_name(),
|
||||||
|
player.role,
|
||||||
|
player.role.seer_color()
|
||||||
|
));
|
||||||
} else {
|
} else {
|
||||||
msg_builder.push_line("");
|
msg_builder.push_line("");
|
||||||
}
|
}
|
||||||
|
@ -638,14 +652,20 @@ async fn players(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
|
||||||
#[aliases("pm", "w")]
|
#[aliases("pm", "w")]
|
||||||
#[description = "Send a private message to another player."]
|
#[description = "Send a private message to another player."]
|
||||||
async fn whisper(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
async fn whisper(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||||
if args.len() < 2 {
|
|
||||||
msg.reply(&ctx.http, "Need a recipient and message!")
|
|
||||||
.await?;
|
|
||||||
} else {
|
|
||||||
let data = ctx.data.read().await;
|
let data = ctx.data.read().await;
|
||||||
let global_data = data.get::<GlobalData>().unwrap();
|
let global_data = data.get::<GlobalData>().unwrap();
|
||||||
let mut global_data = global_data.lock().await;
|
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 target = args.single::<String>()?;
|
||||||
let pm = args.rest();
|
let pm = args.rest();
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,10 @@ use crate::error::WoxlfError;
|
||||||
use crate::game::game_state::PhaseDuration;
|
use crate::game::game_state::PhaseDuration;
|
||||||
use crate::game::global_data::GlobalData;
|
use crate::game::global_data::GlobalData;
|
||||||
use crate::game::player_data::PlayerData;
|
use crate::game::player_data::PlayerData;
|
||||||
|
use crate::game::role::Role;
|
||||||
use crate::imgur::Image;
|
use crate::imgur::Image;
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub async fn add_user_to_game(
|
pub async fn add_user_to_game(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
guild: &Guild,
|
guild: &Guild,
|
||||||
|
@ -20,6 +22,7 @@ pub async fn add_user_to_game(
|
||||||
first_name: Option<String>,
|
first_name: Option<String>,
|
||||||
last_name: Option<String>,
|
last_name: Option<String>,
|
||||||
profile_pic: Option<Image>,
|
profile_pic: Option<Image>,
|
||||||
|
role: Option<Role>,
|
||||||
) -> error::Result<PlayerData> {
|
) -> error::Result<PlayerData> {
|
||||||
if first_name.is_none() && last_name.is_none() {
|
if first_name.is_none() && last_name.is_none() {
|
||||||
return Err(WoxlfError::RanOutOfCodenames);
|
return Err(WoxlfError::RanOutOfCodenames);
|
||||||
|
@ -29,6 +32,10 @@ pub async fn add_user_to_game(
|
||||||
return Err(WoxlfError::RanOutOfProfilePics);
|
return Err(WoxlfError::RanOutOfProfilePics);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if role.is_none() {
|
||||||
|
return Err(WoxlfError::RanOutOfRoles);
|
||||||
|
}
|
||||||
|
|
||||||
let codename = global_data
|
let codename = global_data
|
||||||
.templates()?
|
.templates()?
|
||||||
.build_name(global_data, first_name, last_name)
|
.build_name(global_data, first_name, last_name)
|
||||||
|
@ -67,6 +74,7 @@ pub async fn add_user_to_game(
|
||||||
channel_webhook_id: webhook.id.0,
|
channel_webhook_id: webhook.id.0,
|
||||||
profile_pic_url: profile_pic.unwrap().link,
|
profile_pic_url: profile_pic.unwrap().link,
|
||||||
alive: true,
|
alive: true,
|
||||||
|
role: role.unwrap(),
|
||||||
};
|
};
|
||||||
|
|
||||||
global_data
|
global_data
|
||||||
|
|
|
@ -20,6 +20,8 @@ pub enum WoxlfError {
|
||||||
TemplateError(tera::Error),
|
TemplateError(tera::Error),
|
||||||
RanOutOfProfilePics,
|
RanOutOfProfilePics,
|
||||||
UnsupportedMsgMedium,
|
UnsupportedMsgMedium,
|
||||||
|
RanOutOfRoles,
|
||||||
|
ConfigNotfound,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for WoxlfError {}
|
impl std::error::Error for WoxlfError {}
|
||||||
|
@ -45,6 +47,8 @@ impl Display for WoxlfError {
|
||||||
WoxlfError::UnsupportedMsgMedium => {
|
WoxlfError::UnsupportedMsgMedium => {
|
||||||
"Tried to send a message over an unsupported medium".to_string()
|
"Tried to send a message over an unsupported medium".to_string()
|
||||||
}
|
}
|
||||||
|
WoxlfError::RanOutOfRoles => "Ran out of user roles".to_string(),
|
||||||
|
WoxlfError::ConfigNotfound => "Config not found".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
write!(f, "Woxlf Error: {}", msg)
|
write!(f, "Woxlf Error: {}", msg)
|
||||||
|
|
|
@ -36,7 +36,10 @@ impl GlobalData {
|
||||||
starting_phase: Phase,
|
starting_phase: Phase,
|
||||||
starting_phase_duration: Duration,
|
starting_phase_duration: Duration,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let game_config = self.cfg.get_game_config(game_name).unwrap();
|
let game_config = self
|
||||||
|
.cfg
|
||||||
|
.get_game_config(game_name)
|
||||||
|
.ok_or(WoxlfError::ConfigNotfound)?;
|
||||||
|
|
||||||
self.templates = Some(MessageTemplates::try_from(game_config.messages.clone())?);
|
self.templates = Some(MessageTemplates::try_from(game_config.messages.clone())?);
|
||||||
self.game_cfg = Some(game_config);
|
self.game_cfg = Some(game_config);
|
||||||
|
|
|
@ -83,8 +83,8 @@ pub enum Priority {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ListenerContext<'a> {
|
pub struct ListenerContext<'a> {
|
||||||
data: &'a GlobalData,
|
pub data: &'a mut GlobalData,
|
||||||
ctx: &'a Context,
|
pub ctx: &'a Context,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|
|
@ -38,7 +38,7 @@ impl Default for MessageDest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub enum Median {
|
pub enum Median {
|
||||||
DirectMessage,
|
DirectMessage,
|
||||||
|
@ -133,7 +133,7 @@ fn filter_source_channel(player_data: &&PlayerData, msg_source: &MessageSource)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_webhook_msg(
|
pub async fn send_webhook_msg(
|
||||||
http: &Http,
|
http: &Http,
|
||||||
webhook_id: WebhookId,
|
webhook_id: WebhookId,
|
||||||
username: &str,
|
username: &str,
|
||||||
|
@ -162,23 +162,17 @@ async fn send_webhook_msg(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_private_message(
|
pub async fn send_private_message(
|
||||||
http: &Http,
|
http: &Http,
|
||||||
src_username: &str,
|
|
||||||
dest: UserId,
|
dest: UserId,
|
||||||
msg: &str,
|
msg_content: &str,
|
||||||
attachments: &Option<Vec<AttachmentType<'_>>>,
|
attachments: &Option<Vec<AttachmentType<'_>>>,
|
||||||
) -> error::Result<()> {
|
) -> error::Result<()> {
|
||||||
let dest_user = dest.to_user(http).await?;
|
let dest_user = dest.to_user(http).await?;
|
||||||
|
|
||||||
let mut dm_message = MessageBuilder::new();
|
|
||||||
|
|
||||||
dm_message.push_bold_line_safe(format!("{} has sent you a DM:", src_username));
|
|
||||||
dm_message.push(msg);
|
|
||||||
|
|
||||||
dest_user
|
dest_user
|
||||||
.dm(http, |msg| {
|
.dm(http, |msg| {
|
||||||
msg.content(dm_message);
|
msg.content(msg_content);
|
||||||
|
|
||||||
if let Some(attachments) = attachments {
|
if let Some(attachments) = attachments {
|
||||||
msg.add_files(attachments.clone());
|
msg.add_files(attachments.clone());
|
||||||
|
@ -265,11 +259,15 @@ pub async fn send_message(
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
Median::DirectMessage => {
|
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(&msg.content)
|
||||||
|
.build();
|
||||||
|
|
||||||
send_private_message(
|
send_private_message(
|
||||||
&ctx.http,
|
&ctx.http,
|
||||||
&msg.get_message_username(global_data)?,
|
|
||||||
UserId(dest_player.discord_id),
|
UserId(dest_player.discord_id),
|
||||||
&msg.content,
|
&dm_msg,
|
||||||
&msg.attachments,
|
&msg.attachments,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -283,6 +281,8 @@ pub async fn send_message(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Send a message to the proper channels
|
||||||
|
/// Note safe to use in an event handler
|
||||||
pub async fn dispatch_message(
|
pub async fn dispatch_message(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
global_data: &mut GlobalData,
|
global_data: &mut GlobalData,
|
||||||
|
|
|
@ -5,6 +5,7 @@ pub mod global_data;
|
||||||
pub mod listener;
|
pub mod listener;
|
||||||
pub mod message_router;
|
pub mod message_router;
|
||||||
pub mod player_data;
|
pub mod player_data;
|
||||||
|
pub mod role;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq, Copy)]
|
#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq, Copy)]
|
||||||
pub enum Phase {
|
pub enum Phase {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::game::role::Role;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
@ -9,6 +10,7 @@ pub struct PlayerData {
|
||||||
pub profile_pic_url: String,
|
pub profile_pic_url: String,
|
||||||
pub channel_webhook_id: u64,
|
pub channel_webhook_id: u64,
|
||||||
pub alive: bool,
|
pub alive: bool,
|
||||||
|
pub role: Role,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlayerData {
|
impl PlayerData {
|
||||||
|
|
|
@ -0,0 +1,128 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
use crate::game::listener::Listeners;
|
||||||
|
use crate::game::role::spy::SpyListener;
|
||||||
|
|
||||||
|
mod spy;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq)]
|
||||||
|
pub enum RoleColor {
|
||||||
|
Green,
|
||||||
|
Red,
|
||||||
|
Blue,
|
||||||
|
Purple,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for RoleColor {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let s = match self {
|
||||||
|
RoleColor::Green => "Green",
|
||||||
|
RoleColor::Red => "Red",
|
||||||
|
RoleColor::Blue => "Blue",
|
||||||
|
RoleColor::Purple => "Purple",
|
||||||
|
};
|
||||||
|
|
||||||
|
write!(f, "{}", s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq)]
|
||||||
|
pub enum Role {
|
||||||
|
// Human Roles
|
||||||
|
Villager,
|
||||||
|
Seer,
|
||||||
|
Guardian,
|
||||||
|
Jailer,
|
||||||
|
Coroner,
|
||||||
|
Vigilante,
|
||||||
|
Hunter,
|
||||||
|
Psychic,
|
||||||
|
Miller,
|
||||||
|
Herring,
|
||||||
|
Spy,
|
||||||
|
Jester,
|
||||||
|
Mason,
|
||||||
|
Shepard,
|
||||||
|
// Wolf Roles
|
||||||
|
MasterWolf,
|
||||||
|
WolfShaman,
|
||||||
|
Wolf,
|
||||||
|
// Custom Role
|
||||||
|
Custom(String, RoleColor),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Role {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let s = match self {
|
||||||
|
Role::Villager => "Villager",
|
||||||
|
Role::Seer => "Seer",
|
||||||
|
Role::Guardian => "Guardian",
|
||||||
|
Role::Jailer => "Jailer",
|
||||||
|
Role::Coroner => "Coroner",
|
||||||
|
Role::Vigilante => "Vigilante",
|
||||||
|
Role::Hunter => "Hunter",
|
||||||
|
Role::Psychic => "Psychic",
|
||||||
|
Role::Miller => "Miller",
|
||||||
|
Role::Herring => "Herring",
|
||||||
|
Role::Spy => "Spy",
|
||||||
|
Role::Jester => "Jester",
|
||||||
|
Role::Mason => "Mason",
|
||||||
|
Role::Shepard => "Shepard",
|
||||||
|
Role::MasterWolf => "Master Wolf",
|
||||||
|
Role::WolfShaman => "Wolf Shaman",
|
||||||
|
Role::Wolf => "Wolf",
|
||||||
|
Role::Custom(role_name, _) => role_name,
|
||||||
|
};
|
||||||
|
|
||||||
|
write!(f, "{}", s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Role {
|
||||||
|
/// Color as seen by host or seer
|
||||||
|
pub fn seer_color(&self) -> RoleColor {
|
||||||
|
match self {
|
||||||
|
Role::Villager => RoleColor::Green,
|
||||||
|
Role::Seer => RoleColor::Blue,
|
||||||
|
Role::Guardian => RoleColor::Blue,
|
||||||
|
Role::Jailer => RoleColor::Blue,
|
||||||
|
Role::Coroner => RoleColor::Blue,
|
||||||
|
Role::Vigilante => RoleColor::Blue,
|
||||||
|
Role::Hunter => RoleColor::Blue,
|
||||||
|
Role::Psychic => RoleColor::Blue,
|
||||||
|
Role::Miller => RoleColor::Red,
|
||||||
|
Role::Herring => RoleColor::Blue,
|
||||||
|
Role::Spy => RoleColor::Blue,
|
||||||
|
Role::Jester => RoleColor::Purple,
|
||||||
|
Role::Mason => RoleColor::Green,
|
||||||
|
Role::Shepard => RoleColor::Green,
|
||||||
|
Role::MasterWolf => RoleColor::Green,
|
||||||
|
Role::WolfShaman => RoleColor::Red,
|
||||||
|
Role::Wolf => RoleColor::Red,
|
||||||
|
Role::Custom(_, color) => color.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Color as seen by player
|
||||||
|
pub fn player_color(&self) -> RoleColor {
|
||||||
|
match self {
|
||||||
|
Role::Miller => RoleColor::Green,
|
||||||
|
_ => self.seer_color(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Role name as seen by the player
|
||||||
|
pub fn player_role_name(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Role::Miller => Role::Villager.to_string(),
|
||||||
|
_ => self.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_role_listener(&self, listeners: &mut Listeners) {
|
||||||
|
match self {
|
||||||
|
Role::Spy => listeners.add_listener(Box::new(SpyListener {})),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
use crate::game::listener::{EventStatus, Listener, ListenerContext, Priority};
|
||||||
|
use crate::game::message_router::{Median, MessageDest, MessageSource, send_private_message, WoxlfMessage};
|
||||||
|
use crate::game::role::Role;
|
||||||
|
use serenity::async_trait;
|
||||||
|
use serenity::model::prelude::UserId;
|
||||||
|
use serenity::utils::MessageBuilder;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SpyListener {}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Listener for SpyListener {
|
||||||
|
fn get_priority(&self) -> Priority {
|
||||||
|
Priority::Logging
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn on_chat(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut ListenerContext,
|
||||||
|
msg: &WoxlfMessage,
|
||||||
|
) -> crate::error::Result<EventStatus> {
|
||||||
|
if msg.median != Median::DirectMessage {
|
||||||
|
return Ok(EventStatus::Okay);
|
||||||
|
}
|
||||||
|
|
||||||
|
let src_player = if let MessageSource::Player(p) = &msg.source {
|
||||||
|
p
|
||||||
|
} else {
|
||||||
|
return Ok(EventStatus::Okay);
|
||||||
|
};
|
||||||
|
|
||||||
|
let dest_player = if let MessageDest::Player(p) = &msg.dest {
|
||||||
|
p
|
||||||
|
} else {
|
||||||
|
return Ok(EventStatus::Okay);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
let spy_player = ctx
|
||||||
|
.data
|
||||||
|
.game_state()?
|
||||||
|
.player_data
|
||||||
|
.iter()
|
||||||
|
.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 {
|
||||||
|
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();
|
||||||
|
|
||||||
|
send_private_message(&ctx.ctx.http, UserId::from(spy_player.discord_id), &msg_content, &None).await?
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(EventStatus::Okay);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ use game::global_data::GlobalData;
|
||||||
|
|
||||||
use crate::config::{Args, BotConfig};
|
use crate::config::{Args, BotConfig};
|
||||||
use crate::game::listener::Listeners;
|
use crate::game::listener::Listeners;
|
||||||
|
use crate::game::role::Role;
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
mod discord;
|
mod discord;
|
||||||
|
@ -32,11 +33,14 @@ async fn main() {
|
||||||
.expect("Unable to open saved game state.");
|
.expect("Unable to open saved game state.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut listeners = Listeners::default();
|
||||||
|
Role::Spy.register_role_listener(&mut listeners);
|
||||||
|
|
||||||
let mut client = Client::builder(&bot_cfg.discord_config.token, GatewayIntents::all())
|
let mut client = Client::builder(&bot_cfg.discord_config.token, GatewayIntents::all())
|
||||||
.event_handler(Handler {})
|
.event_handler(Handler {})
|
||||||
.framework(command_framework())
|
.framework(command_framework())
|
||||||
.type_map_insert::<GlobalData>(Arc::new(Mutex::new(global_data)))
|
.type_map_insert::<GlobalData>(Arc::new(Mutex::new(global_data)))
|
||||||
.type_map_insert::<Listeners>(Arc::new(Mutex::new(Listeners::default())))
|
.type_map_insert::<Listeners>(Arc::new(Mutex::new(listeners)))
|
||||||
.await
|
.await
|
||||||
.expect("Err creating client");
|
.expect("Err creating client");
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::config::MessageConfig;
|
use crate::config::MessageConfig;
|
||||||
use crate::game::player_data::PlayerData;
|
use crate::game::player_data::PlayerData;
|
||||||
|
use crate::game::role::Role;
|
||||||
use crate::GlobalData;
|
use crate::GlobalData;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -33,6 +34,34 @@ fn time_to_discord_time(time_flag: &str) -> TeraFnRet {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn role_name() -> TeraFnRet {
|
||||||
|
Box::new(
|
||||||
|
move |args: &HashMap<String, Value>| -> tera::Result<Value> {
|
||||||
|
match args.get("role") {
|
||||||
|
Some(val) => match tera::from_value::<Role>(val.clone()) {
|
||||||
|
Ok(v) => Ok(tera::to_value(v.player_role_name()).unwrap()),
|
||||||
|
Err(_) => Err("Failed to parse value as role".into()),
|
||||||
|
},
|
||||||
|
None => Err("Missing parameter".into()),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn role_color() -> TeraFnRet {
|
||||||
|
Box::new(
|
||||||
|
move |args: &HashMap<String, Value>| -> tera::Result<Value> {
|
||||||
|
match args.get("role") {
|
||||||
|
Some(val) => match tera::from_value::<Role>(val.clone()) {
|
||||||
|
Ok(v) => Ok(tera::to_value(v.player_color().to_string()).unwrap()),
|
||||||
|
Err(_) => Err("Failed to parse value as role".into()),
|
||||||
|
},
|
||||||
|
None => Err("Missing parameter".into()),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct DiscordUser {
|
pub struct DiscordUser {
|
||||||
pub(crate) display_name: String,
|
pub(crate) display_name: String,
|
||||||
|
@ -157,6 +186,8 @@ impl TryFrom<MessageConfig> for MessageTemplates {
|
||||||
|
|
||||||
templates.register_function("to_countdown", time_to_discord_time("R"));
|
templates.register_function("to_countdown", time_to_discord_time("R"));
|
||||||
templates.register_function("to_local_time", time_to_discord_time("f"));
|
templates.register_function("to_local_time", time_to_discord_time("f"));
|
||||||
|
templates.register_function("player_color", role_color());
|
||||||
|
templates.register_function("player_role", role_name());
|
||||||
|
|
||||||
templates.add_raw_template("welcome_message", &config.welcome_message)?;
|
templates.add_raw_template("welcome_message", &config.welcome_message)?;
|
||||||
templates.add_raw_template("status_message", &config.status_message)?;
|
templates.add_raw_template("status_message", &config.status_message)?;
|
||||||
|
|
Loading…
Reference in New Issue