wOxlf/src/data.rs

184 lines
4.6 KiB
Rust

use config::{Config, File};
use serde::{Deserialize, Serialize};
use serenity::prelude::TypeMapKey;
use std::collections::HashMap;
use std::fmt::{Display, Formatter};
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
use structopt::StructOpt;
use tokio::sync::Mutex;
#[derive(Debug, StructOpt)]
#[structopt(name = "WOXlf", about = "WOXlf discord bot")]
pub struct Args {
pub cfg_path: PathBuf,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct BotConfig {
pub token: String,
pub app_id: u64,
pub host_channel: u64,
pub vote_channel: u64,
pub category: u64,
pub game_state_dir: PathBuf,
pub occupation: Vec<String>,
pub adjective: Vec<String>,
}
impl BotConfig {
pub fn new(config_path: &Path) -> Result<Self, config::ConfigError> {
let cfg = Config::builder()
.add_source(File::from(config_path))
.build()?;
cfg.try_deserialize()
}
pub fn get_game_state_path(&self) -> PathBuf {
self.game_state_dir.join("wOxlf_data.toml")
}
}
#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
pub enum Phase {
Day,
Night,
}
impl Default for Phase {
fn default() -> Self {
Self::Night
}
}
impl Display for Phase {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let phase_name = match self {
Self::Day => "Day",
Self::Night => "Night",
};
write!(f, "{}", phase_name)
}
}
#[derive(Debug, Deserialize, Serialize, Clone, Default, Hash)]
pub struct PlayerData {
pub channel: u64,
pub discord_id: u64,
pub codename: String,
pub vote_target: Option<u64>,
}
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
pub struct GameState {
pub phase_number: u64,
pub current_phase: Phase,
pub phase_end_time: u64,
pub player_data: Vec<PlayerData>,
}
impl GameState {
pub fn codename_exists(&self, codename: &str) -> bool {
self.player_data
.iter()
.any(|data| data.codename.to_lowercase() == codename)
}
pub fn clear(&mut self) {
self.player_data.clear();
self.current_phase = Phase::Night;
self.phase_end_time = 0;
self.phase_number = 1;
}
pub fn get_player_from_channel(&self, channel_id: u64) -> Option<&PlayerData> {
self.player_data.iter().find(|p| p.channel == channel_id)
}
pub fn get_player_from_discord_id(&self, discord_id: u64) -> Option<&PlayerData> {
self.player_data.iter().find(|p| p.discord_id == discord_id)
}
pub fn get_player_from_channel_mut(&mut self, channel_id: u64) -> Option<&mut PlayerData> {
self.player_data
.iter_mut()
.find(|p| p.channel == channel_id)
}
pub fn get_player_by_codename(&self, codename: &str) -> Option<PlayerData> {
self.player_data
.iter()
.find(|p| p.codename.to_lowercase() == codename.to_lowercase())
.cloned()
}
pub fn next_phase(&mut self) {
if self.current_phase == Phase::Night {
self.current_phase = Phase::Day
} else {
self.phase_number += 1;
self.current_phase = Phase::Night
}
for mut player in &mut self.player_data {
player.vote_target = None
}
}
pub fn get_phase_end_time(&self) -> String {
format!("<t:{}:f>", self.phase_end_time)
}
pub fn get_phase_countdown(&self) -> String {
format!("<t:{}:R>", self.phase_end_time)
}
pub fn add_time_to_phase(&mut self, hours: u64) {
self.phase_end_time += hours * 60 * 60;
}
pub fn get_vote_tallies(&self) -> HashMap<String, u32> {
let mut vote_set: HashMap<String, u32> = HashMap::new();
for player in &self.player_data {
if let Some(vote_target) = player.vote_target {
let target = self.get_player_from_discord_id(vote_target);
if let Some(target) = target {
*vote_set.entry(target.codename.clone()).or_insert(0) += 1;
}
}
}
vote_set
}
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct GlobalData {
pub cfg: BotConfig,
pub game_state: GameState,
}
impl GlobalData {
pub fn new(cfg: BotConfig) -> Self {
Self {
cfg,
game_state: GameState::default(),
}
}
}
impl TypeMapKey for GlobalData {
type Value = Arc<Mutex<GlobalData>>;
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub enum MessageSource {
Player(u64),
Host,
Automated,
}