Compare commits
4 Commits
main
...
msg_refact
Author | SHA1 | Date |
---|---|---|
Joey Hines | a37febd7cd | |
Joey Hines | a5e1a275e8 | |
Joey Hines | 90ec453b70 | |
Joey Hines | ca80846e0d |
|
@ -8,13 +8,13 @@ pipeline:
|
||||||
- "cargo build --verbose"
|
- "cargo build --verbose"
|
||||||
- "cargo clippy --workspace --tests --all-targets --all-features"
|
- "cargo clippy --workspace --tests --all-targets --all-features"
|
||||||
- "cargo test --workspace --no-fail-fast"
|
- "cargo test --workspace --no-fail-fast"
|
||||||
image: "rust:1-bust"
|
image: "rust:latest"
|
||||||
when:
|
when:
|
||||||
event: pull_request, push
|
event: pull_request, push
|
||||||
build:
|
build:
|
||||||
commands:
|
commands:
|
||||||
- "cargo build --release"
|
- "cargo build --release"
|
||||||
image: "rust:1-buster"
|
image: "rust:latest"
|
||||||
when:
|
when:
|
||||||
branch: main
|
branch: main
|
||||||
event: push
|
event: push
|
||||||
|
|
|
@ -245,7 +245,7 @@ dependencies = [
|
||||||
"rust-ini",
|
"rust-ini",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"toml 0.5.8",
|
"toml",
|
||||||
"yaml-rust",
|
"yaml-rust",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -310,7 +310,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc"
|
checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"hashbrown",
|
"hashbrown 0.12.3",
|
||||||
"lock_api",
|
"lock_api",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"parking_lot_core",
|
"parking_lot_core",
|
||||||
|
@ -577,6 +577,12 @@ dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.12.3"
|
version = "0.12.3"
|
||||||
|
@ -725,12 +731,12 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "1.9.2"
|
version = "1.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
|
checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"hashbrown",
|
"hashbrown 0.11.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -918,15 +924,6 @@ dependencies = [
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "nom8"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8"
|
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ntapi"
|
name = "ntapi"
|
||||||
version = "0.3.7"
|
version = "0.3.7"
|
||||||
|
@ -1026,7 +1023,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a"
|
checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dlv-list",
|
"dlv-list",
|
||||||
"hashbrown",
|
"hashbrown 0.12.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1491,15 +1488,6 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_spanned"
|
|
||||||
version = "0.6.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_urlencoded"
|
name = "serde_urlencoded"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
|
@ -1859,40 +1847,6 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "toml"
|
|
||||||
version = "0.7.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f7afcae9e3f0fe2c370fd4657108972cbb2fa9db1b9f84849cefd80741b01cb6"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
"serde_spanned",
|
|
||||||
"toml_datetime",
|
|
||||||
"toml_edit",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "toml_datetime"
|
|
||||||
version = "0.6.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "toml_edit"
|
|
||||||
version = "0.19.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5e6a7712b49e1775fb9a7b998de6635b299237f48b404dde71704f2e0e7f37e5"
|
|
||||||
dependencies = [
|
|
||||||
"indexmap",
|
|
||||||
"nom8",
|
|
||||||
"serde",
|
|
||||||
"serde_spanned",
|
|
||||||
"toml_datetime",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower-service"
|
name = "tower-service"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
|
@ -2348,7 +2302,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "woxlf"
|
name = "woxlf"
|
||||||
version = "0.3.1"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
@ -2362,7 +2316,7 @@ dependencies = [
|
||||||
"structopt",
|
"structopt",
|
||||||
"tera",
|
"tera",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml 0.7.2",
|
"toml",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "woxlf"
|
name = "woxlf"
|
||||||
version = "0.3.1"
|
version = "0.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
@ -11,7 +11,7 @@ structopt = "0.3.26"
|
||||||
chrono = {version="0.4.19", features=["serde"]}
|
chrono = {version="0.4.19", features=["serde"]}
|
||||||
serde = "1.0.136"
|
serde = "1.0.136"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
toml = "0.7.2"
|
toml = "0.5.8"
|
||||||
regex = "1.5.5"
|
regex = "1.5.5"
|
||||||
futures = "0.3.21"
|
futures = "0.3.21"
|
||||||
reqwest = "0.11.10"
|
reqwest = "0.11.10"
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
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;
|
||||||
|
@ -20,9 +19,8 @@ pub struct GameConfig {
|
||||||
pub vote_phase_name: String,
|
pub vote_phase_name: String,
|
||||||
pub enemy_phase_name: String,
|
pub enemy_phase_name: String,
|
||||||
pub player_group_name: String,
|
pub player_group_name: String,
|
||||||
pub profile_album: Vec<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,
|
||||||
|
|
|
@ -1,27 +1,33 @@
|
||||||
|
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::discord::helper::{add_user_to_game, parse_duration_arg};
|
||||||
use crate::error;
|
use crate::error::{Result, WoxlfError};
|
||||||
use crate::error::WoxlfError;
|
|
||||||
use crate::game::global_data::GlobalData;
|
use crate::game::global_data::GlobalData;
|
||||||
use crate::game::message_router::{
|
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;
|
||||||
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]
|
#[group]
|
||||||
#[commands(start, say, end, broadcast, next_phase, kill, add_time, test_theme)]
|
#[commands(
|
||||||
|
start, say, end, broadcast, next_phase, kill, add_time, test_theme, whisper
|
||||||
|
)]
|
||||||
struct Host;
|
struct Host;
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
|
@ -40,18 +46,18 @@ async fn start(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||||
|
|
||||||
global_data.start_game(&game_name, Phase::Night, duration.into())?;
|
global_data.start_game(&game_name, Phase::Night, duration.into())?;
|
||||||
|
|
||||||
let players: error::Result<Vec<&Member>> = args
|
let players: Result<Vec<&Member>> = args
|
||||||
.iter::<String>()
|
.iter::<String>()
|
||||||
.flatten()
|
.flatten()
|
||||||
.map(|discord_id| {
|
.map(|discord_id| {
|
||||||
let discord_id = match discord_id.parse::<UserId>() {
|
let discord_id = match discord_id.parse::<u64>() {
|
||||||
Ok(discord_id) => discord_id,
|
Ok(discord_id) => discord_id,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return Err(WoxlfError::DiscordIdParseError(discord_id));
|
return Err(WoxlfError::DiscordIdParseError(discord_id));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(discord_user) = guild.members.get(&discord_id) {
|
if let Some(discord_user) = guild.members.get(&UserId::from(discord_id)) {
|
||||||
Ok(discord_user)
|
Ok(discord_user)
|
||||||
} else {
|
} else {
|
||||||
Err(WoxlfError::DiscordIdParseError(discord_id.to_string()))
|
Err(WoxlfError::DiscordIdParseError(discord_id.to_string()))
|
||||||
|
@ -83,19 +89,14 @@ async fn start(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||||
first_names.shuffle(&mut thread_rng());
|
first_names.shuffle(&mut thread_rng());
|
||||||
last_names.shuffle(&mut thread_rng());
|
last_names.shuffle(&mut thread_rng());
|
||||||
|
|
||||||
let mut profile_pics = global_data.game_cfg()?.profile_album.clone();
|
let mut profile_pics = global_data.get_profile_pic_album().await?;
|
||||||
|
|
||||||
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,
|
||||||
|
@ -104,7 +105,6 @@ 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?;
|
||||||
}
|
}
|
||||||
|
@ -167,7 +167,7 @@ async fn say(ctx: &Context, _msg: &Message, args: Args) -> CommandResult {
|
||||||
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 global_data = global_data.lock().await;
|
||||||
|
|
||||||
let msg = WoxlfMessage::default()
|
let msg = WoxlfMessage::default()
|
||||||
.source(MessageSource::Host)
|
.source(MessageSource::Host)
|
||||||
|
@ -176,7 +176,7 @@ async fn say(ctx: &Context, _msg: &Message, args: Args) -> CommandResult {
|
||||||
.content(args.rest())
|
.content(args.rest())
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
dispatch_message(ctx, &mut global_data, msg).await?;
|
dispatch_message(ctx, &global_data, msg).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -188,7 +188,7 @@ async fn broadcast(ctx: &Context, _msg: &Message, args: Args) -> CommandResult {
|
||||||
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 global_data = global_data.lock().await;
|
||||||
|
|
||||||
let broadcast = global_data
|
let broadcast = global_data
|
||||||
.templates()?
|
.templates()?
|
||||||
|
@ -199,11 +199,9 @@ async fn broadcast(ctx: &Context, _msg: &Message, args: Args) -> CommandResult {
|
||||||
.dest(MessageDest::Broadcast)
|
.dest(MessageDest::Broadcast)
|
||||||
.content(&broadcast)
|
.content(&broadcast)
|
||||||
.median(Median::Webhook)
|
.median(Median::Webhook)
|
||||||
.ping()
|
|
||||||
.pin()
|
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
dispatch_message(ctx, &mut global_data, woxlf_msg).await?;
|
dispatch_message(ctx, &global_data, woxlf_msg).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -212,8 +210,8 @@ async fn broadcast(ctx: &Context, _msg: &Message, args: Args) -> CommandResult {
|
||||||
#[only_in(guilds)]
|
#[only_in(guilds)]
|
||||||
#[allowed_roles("wolfx host")]
|
#[allowed_roles("wolfx host")]
|
||||||
async fn next_phase(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
async fn next_phase(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||||
let data = ctx.data.read().await;
|
let mut data = ctx.data.write().await;
|
||||||
let global_data = data.get::<GlobalData>().unwrap();
|
let global_data = data.get_mut::<GlobalData>().unwrap();
|
||||||
let guild = msg.guild(&ctx.cache).unwrap();
|
let guild = msg.guild(&ctx.cache).unwrap();
|
||||||
|
|
||||||
let mut global_data = global_data.lock().await;
|
let mut global_data = global_data.lock().await;
|
||||||
|
@ -237,11 +235,9 @@ async fn next_phase(ctx: &Context, msg: &Message, mut args: Args) -> CommandResu
|
||||||
.dest(MessageDest::Broadcast)
|
.dest(MessageDest::Broadcast)
|
||||||
.median(Median::Webhook)
|
.median(Median::Webhook)
|
||||||
.content(&broadcast)
|
.content(&broadcast)
|
||||||
.pin()
|
|
||||||
.ping()
|
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
dispatch_message(ctx, &mut global_data, woxlf_msg).await?;
|
dispatch_message(ctx, &global_data, woxlf_msg).await?;
|
||||||
|
|
||||||
if global_data.game_state_mut()?.current_phase == Phase::Day {
|
if global_data.game_state_mut()?.current_phase == Phase::Day {
|
||||||
let vote_channel = guild
|
let vote_channel = guild
|
||||||
|
@ -303,6 +299,52 @@ async fn kill(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||||
Ok(())
|
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::<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, &global_data, woxlf_msg).await?;
|
||||||
|
|
||||||
|
msg.reply(&ctx.http, "Phase has been updated")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
global_data.save_game_state().unwrap();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
#[only_in(guilds)]
|
#[only_in(guilds)]
|
||||||
#[allowed_roles("wolfx host")]
|
#[allowed_roles("wolfx host")]
|
||||||
|
@ -327,7 +369,6 @@ 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 {
|
||||||
|
@ -343,7 +384,6 @@ 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()];
|
||||||
|
@ -445,50 +485,249 @@ async fn test_theme(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[group]
|
||||||
|
#[commands(vote, status, players)]
|
||||||
|
struct Player;
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
#[only_in(guilds)]
|
#[only_in(guilds)]
|
||||||
#[allowed_roles("wolfx host")]
|
#[description = "vote another subject for termination. $vote <code_name>"]
|
||||||
async fn add_time(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
async fn vote(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||||
let data = ctx.data.read().await;
|
let mut data = ctx.data.write().await;
|
||||||
let global_data = data.get::<GlobalData>().unwrap();
|
let global_data = data.get_mut::<GlobalData>().unwrap();
|
||||||
|
let guild = msg.guild(&ctx.cache).unwrap();
|
||||||
|
|
||||||
let mut global_data = global_data.lock().await;
|
let mut global_data = global_data.lock().await;
|
||||||
|
|
||||||
let duration = parse_duration_arg(&mut args).await?;
|
if global_data.game_state_mut()?.current_phase != Phase::Day {
|
||||||
|
msg.reply(
|
||||||
global_data
|
&ctx.http,
|
||||||
.game_state_mut()?
|
format!(
|
||||||
.add_time_to_phase(duration.into());
|
"You can only vote during the {} phase.",
|
||||||
|
global_data.game_cfg()?.vote_phase_name
|
||||||
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)
|
|
||||||
.ping()
|
|
||||||
.pin()
|
|
||||||
.clone();
|
|
||||||
|
|
||||||
dispatch_message(ctx, &mut global_data, woxlf_msg).await?;
|
|
||||||
|
|
||||||
msg.reply(&ctx.http, "Phase has been updated")
|
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.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();
|
global_data.save_game_state().unwrap();
|
||||||
Ok(())
|
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()));
|
||||||
|
} 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 {
|
||||||
|
if args.len() < 2 {
|
||||||
|
msg.reply(&ctx.http, "Need a recipient and message!")
|
||||||
|
.await?;
|
||||||
|
} else {
|
||||||
|
let data = ctx.data.read().await;
|
||||||
|
let global_data = data.get::<GlobalData>().unwrap();
|
||||||
|
let global_data = global_data.lock().await;
|
||||||
|
|
||||||
|
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, &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)
|
||||||
|
}
|
|
@ -26,7 +26,7 @@ impl EventHandler for Handler {
|
||||||
|
|
||||||
let global_data = data.get::<GlobalData>().unwrap();
|
let global_data = data.get::<GlobalData>().unwrap();
|
||||||
|
|
||||||
let mut global_data = global_data.lock().await;
|
let global_data = global_data.lock().await;
|
||||||
|
|
||||||
if global_data.game_state.is_none() {
|
if global_data.game_state.is_none() {
|
||||||
// no game in progress
|
// no game in progress
|
||||||
|
@ -75,7 +75,7 @@ impl EventHandler for Handler {
|
||||||
.attachments(attachments)
|
.attachments(attachments)
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
dispatch_message(&ctx, &mut global_data, woxlf_msg)
|
dispatch_message(&ctx, &global_data, woxlf_msg)
|
||||||
.await
|
.await
|
||||||
.expect("Unable to send message to players");
|
.expect("Unable to send message to players");
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,8 @@ 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;
|
||||||
|
|
||||||
#[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,8 +19,7 @@ pub async fn add_user_to_game(
|
||||||
discord_user: &Member,
|
discord_user: &Member,
|
||||||
first_name: Option<String>,
|
first_name: Option<String>,
|
||||||
last_name: Option<String>,
|
last_name: Option<String>,
|
||||||
profile_pic: Option<String>,
|
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);
|
||||||
|
@ -31,10 +29,6 @@ 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)
|
||||||
|
@ -42,7 +36,7 @@ pub async fn add_user_to_game(
|
||||||
|
|
||||||
let channel = guild
|
let channel = guild
|
||||||
.create_channel(&ctx.http, |c| {
|
.create_channel(&ctx.http, |c| {
|
||||||
c.category(ChannelId::from(global_data.cfg.discord_config.category))
|
c.category(&ChannelId::from(global_data.cfg.discord_config.category))
|
||||||
.name(format!("{}'s Channel", discord_user.display_name()))
|
.name(format!("{}'s Channel", discord_user.display_name()))
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -71,9 +65,8 @@ pub async fn add_user_to_game(
|
||||||
vote_target: None,
|
vote_target: None,
|
||||||
codename,
|
codename,
|
||||||
channel_webhook_id: webhook.id.0,
|
channel_webhook_id: webhook.id.0,
|
||||||
profile_pic_url: profile_pic.unwrap(),
|
profile_pic_url: profile_pic.unwrap().link,
|
||||||
alive: true,
|
alive: true,
|
||||||
role: role.unwrap(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
global_data
|
global_data
|
||||||
|
|
|
@ -1,59 +1,3 @@
|
||||||
use serenity::client::Context;
|
pub mod commands;
|
||||||
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 event_handler;
|
||||||
pub mod helper;
|
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)
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,224 +0,0 @@
|
||||||
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(())
|
|
||||||
}
|
|
15
src/error.rs
15
src/error.rs
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::imgur::ImgurError;
|
||||||
use serenity::prelude::SerenityError;
|
use serenity::prelude::SerenityError;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use tera::Error;
|
use tera::Error;
|
||||||
|
@ -14,13 +15,11 @@ pub enum WoxlfError {
|
||||||
DiscordIdParseError(String),
|
DiscordIdParseError(String),
|
||||||
GameNotInProgress,
|
GameNotInProgress,
|
||||||
HostWebhookError,
|
HostWebhookError,
|
||||||
|
ImgurError(ImgurError),
|
||||||
RanOutOfCodenames,
|
RanOutOfCodenames,
|
||||||
TemplateError(tera::Error),
|
TemplateError(tera::Error),
|
||||||
RanOutOfProfilePics,
|
RanOutOfProfilePics,
|
||||||
UnsupportedMsgMedium,
|
UnsupportedMsgMedium,
|
||||||
RanOutOfRoles,
|
|
||||||
ConfigNotfound,
|
|
||||||
WebhookMsgError,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for WoxlfError {}
|
impl std::error::Error for WoxlfError {}
|
||||||
|
@ -36,6 +35,7 @@ impl Display for WoxlfError {
|
||||||
WoxlfError::DiscordIdParseError(e) => format!("Unable to parse player id {}", e),
|
WoxlfError::DiscordIdParseError(e) => format!("Unable to parse player id {}", e),
|
||||||
WoxlfError::GameNotInProgress => "A game is not currently in progress".to_string(),
|
WoxlfError::GameNotInProgress => "A game is not currently in progress".to_string(),
|
||||||
WoxlfError::HostWebhookError => "Unable to communicate to the host webhook".to_string(),
|
WoxlfError::HostWebhookError => "Unable to communicate to the host webhook".to_string(),
|
||||||
|
WoxlfError::ImgurError(err) => format!("Imgur module error: {}", err),
|
||||||
WoxlfError::RanOutOfCodenames => {
|
WoxlfError::RanOutOfCodenames => {
|
||||||
"Ran out of codename combinations, add more first/last names to the config"
|
"Ran out of codename combinations, add more first/last names to the config"
|
||||||
.to_string()
|
.to_string()
|
||||||
|
@ -45,9 +45,6 @@ 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(),
|
|
||||||
WoxlfError::WebhookMsgError => "Webhook returned is None".to_string(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
write!(f, "Woxlf Error: {}", msg)
|
write!(f, "Woxlf Error: {}", msg)
|
||||||
|
@ -78,6 +75,12 @@ impl From<toml::ser::Error> for WoxlfError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<ImgurError> for WoxlfError {
|
||||||
|
fn from(err: ImgurError) -> Self {
|
||||||
|
Self::ImgurError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<tera::Error> for WoxlfError {
|
impl From<tera::Error> for WoxlfError {
|
||||||
fn from(err: Error) -> Self {
|
fn from(err: Error) -> Self {
|
||||||
Self::TemplateError(err)
|
Self::TemplateError(err)
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use chrono::Duration;
|
|
||||||
use serenity::prelude::{Mutex, TypeMapKey};
|
use serenity::prelude::{Mutex, TypeMapKey};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
|
@ -9,7 +8,9 @@ use crate::config::{BotConfig, GameConfig};
|
||||||
use crate::error::{Result, WoxlfError};
|
use crate::error::{Result, WoxlfError};
|
||||||
use crate::game::game_state::GameState;
|
use crate::game::game_state::GameState;
|
||||||
use crate::game::Phase;
|
use crate::game::Phase;
|
||||||
|
use crate::imgur::{get_album_images, Image};
|
||||||
use crate::messages::MessageTemplates;
|
use crate::messages::MessageTemplates;
|
||||||
|
use chrono::Duration;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct GlobalData {
|
pub struct GlobalData {
|
||||||
|
@ -35,10 +36,7 @@ impl GlobalData {
|
||||||
starting_phase: Phase,
|
starting_phase: Phase,
|
||||||
starting_phase_duration: Duration,
|
starting_phase_duration: Duration,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let game_config = self
|
let game_config = self.cfg.get_game_config(game_name).unwrap();
|
||||||
.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);
|
||||||
|
@ -128,6 +126,14 @@ impl GlobalData {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_profile_pic_album(&self) -> Result<Vec<Image>> {
|
||||||
|
Ok(get_album_images(
|
||||||
|
&self.cfg.imgur_client_id,
|
||||||
|
&self.game_cfg()?.profile_album_hash,
|
||||||
|
)
|
||||||
|
.await?)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_phase_name(&self) -> Result<String> {
|
pub fn get_phase_name(&self) -> Result<String> {
|
||||||
let game_cfg = self.game_cfg()?;
|
let game_cfg = self.game_cfg()?;
|
||||||
let state = self.game_state()?;
|
let state = self.game_state()?;
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
use crate::game::listener::{EventStatus, Listener, ListenerContext, Priority};
|
|
||||||
use crate::game::message_router::{send_to_host_channel, WoxlfMessage};
|
|
||||||
use serenity::async_trait;
|
|
||||||
use serenity::model::prelude::GuildId;
|
|
||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct HostSnooper {}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl Listener for HostSnooper {
|
|
||||||
fn name(&self) -> String {
|
|
||||||
"Host Snooper".to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_priority(&self) -> Priority {
|
|
||||||
Priority::Logging
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn on_chat(
|
|
||||||
&mut self,
|
|
||||||
ctx: &mut ListenerContext,
|
|
||||||
msg: &WoxlfMessage,
|
|
||||||
) -> crate::error::Result<EventStatus> {
|
|
||||||
let guild_id = ctx.data.cfg.discord_config.guild_id;
|
|
||||||
let guild = GuildId::from(guild_id)
|
|
||||||
.to_guild_cached(&ctx.ctx.cache)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
send_to_host_channel(&ctx.ctx.http, &guild, ctx.data, msg.clone()).await?;
|
|
||||||
Ok(EventStatus::Okay)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,106 +0,0 @@
|
||||||
pub mod host_snooper;
|
|
||||||
|
|
||||||
use crate::error::Result;
|
|
||||||
use crate::game::global_data::GlobalData;
|
|
||||||
use crate::game::listener::host_snooper::HostSnooper;
|
|
||||||
use crate::game::message_router::WoxlfMessage;
|
|
||||||
use serenity::async_trait;
|
|
||||||
use serenity::client::Context;
|
|
||||||
use serenity::prelude::TypeMapKey;
|
|
||||||
use std::fmt::Debug;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use tokio::sync::Mutex;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Listeners {
|
|
||||||
listeners: Vec<Box<dyn Listener>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Listeners {
|
|
||||||
pub async fn process_event(
|
|
||||||
&mut self,
|
|
||||||
ctx: &Context,
|
|
||||||
data: &mut GlobalData,
|
|
||||||
event: WoxlfEvent<'_>,
|
|
||||||
) -> Result<EventStatus> {
|
|
||||||
let mut listener_ctx = ListenerContext { data, ctx };
|
|
||||||
|
|
||||||
for listener in &mut self.listeners {
|
|
||||||
let status = match &event {
|
|
||||||
WoxlfEvent::Chat(msg) => listener.on_chat(&mut listener_ctx, msg).await?,
|
|
||||||
};
|
|
||||||
|
|
||||||
if status == EventStatus::Canceled {
|
|
||||||
return Ok(status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(EventStatus::Okay)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_listener(&mut self, listener: Box<dyn Listener>) {
|
|
||||||
println!("Adding {} listener", listener.name());
|
|
||||||
self.listeners.push(listener);
|
|
||||||
|
|
||||||
self.listeners.sort_by_key(|l1| l1.get_priority());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Listeners {
|
|
||||||
fn default() -> Self {
|
|
||||||
let mut listeners = Self { listeners: vec![] };
|
|
||||||
|
|
||||||
// Add default listeners here
|
|
||||||
listeners.add_listener(Box::new(HostSnooper {}));
|
|
||||||
|
|
||||||
listeners
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TypeMapKey for Listeners {
|
|
||||||
type Value = Arc<Mutex<Listeners>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
|
||||||
pub enum EventStatus {
|
|
||||||
Okay,
|
|
||||||
Canceled,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum WoxlfEvent<'a> {
|
|
||||||
Chat(WoxlfMessage<'a>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq)]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub enum Priority {
|
|
||||||
Highest,
|
|
||||||
High,
|
|
||||||
Medium,
|
|
||||||
Low,
|
|
||||||
Lowest,
|
|
||||||
Logging,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ListenerContext<'a> {
|
|
||||||
pub data: &'a mut GlobalData,
|
|
||||||
pub ctx: &'a Context,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait Listener: Debug + Send + Sync {
|
|
||||||
fn name(&self) -> String;
|
|
||||||
|
|
||||||
fn get_priority(&self) -> Priority {
|
|
||||||
Priority::Medium
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn on_chat(
|
|
||||||
&mut self,
|
|
||||||
_ctx: &mut ListenerContext,
|
|
||||||
_msg: &WoxlfMessage,
|
|
||||||
) -> Result<EventStatus> {
|
|
||||||
Ok(EventStatus::Okay)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +1,13 @@
|
||||||
use crate::error;
|
use crate::error;
|
||||||
use crate::error::WoxlfError;
|
use crate::error::WoxlfError;
|
||||||
use crate::game::global_data::GlobalData;
|
use crate::game::global_data::GlobalData;
|
||||||
use crate::game::listener::{EventStatus, Listeners, WoxlfEvent};
|
|
||||||
use crate::game::player_data::PlayerData;
|
use crate::game::player_data::PlayerData;
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use serenity::client::Context;
|
use serenity::client::Context;
|
||||||
use serenity::http::{CacheHttp, Http};
|
use serenity::http::{CacheHttp, Http};
|
||||||
use serenity::model::guild::Guild;
|
use serenity::model::guild::Guild;
|
||||||
use serenity::model::id::{ChannelId, UserId};
|
use serenity::model::id::{ChannelId, UserId};
|
||||||
use serenity::model::prelude::{AttachmentType, Message, WebhookId};
|
use serenity::model::prelude::{AttachmentType, GuildId, WebhookId};
|
||||||
use serenity::prelude::Mentionable;
|
|
||||||
use serenity::utils::MessageBuilder;
|
use serenity::utils::MessageBuilder;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -39,7 +37,7 @@ impl Default for MessageDest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub enum Median {
|
pub enum Median {
|
||||||
DirectMessage,
|
DirectMessage,
|
||||||
|
@ -57,7 +55,6 @@ bitflags! {
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct MsgFlags: u32 {
|
pub struct MsgFlags: u32 {
|
||||||
const PIN_MSG = 0b00000001;
|
const PIN_MSG = 0b00000001;
|
||||||
const PING = 0b00000010;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +83,7 @@ impl<'a> WoxlfMessage<'a> {
|
||||||
Ok(match &self.source {
|
Ok(match &self.source {
|
||||||
MessageSource::Player(p) => p.codename.clone(),
|
MessageSource::Player(p) => p.codename.clone(),
|
||||||
MessageSource::Host => global_data.game_cfg()?.bot_name.clone(),
|
MessageSource::Host => global_data.game_cfg()?.bot_name.clone(),
|
||||||
MessageSource::Automated => "Woxlf Game Message".to_string(),
|
MessageSource::Automated => "Woxlf System Message".to_string(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,11 +121,6 @@ impl<'a> WoxlfMessage<'a> {
|
||||||
self.flags |= MsgFlags::PIN_MSG;
|
self.flags |= MsgFlags::PIN_MSG;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ping(mut self) -> Self {
|
|
||||||
self.flags |= MsgFlags::PING;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn filter_source_channel(player_data: &&PlayerData, msg_source: &MessageSource) -> bool {
|
fn filter_source_channel(player_data: &&PlayerData, msg_source: &MessageSource) -> bool {
|
||||||
|
@ -140,19 +132,19 @@ fn filter_source_channel(player_data: &&PlayerData, msg_source: &MessageSource)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_webhook_msg(
|
async fn send_webhook_msg(
|
||||||
http: &Http,
|
http: &Http,
|
||||||
webhook_id: WebhookId,
|
webhook_id: WebhookId,
|
||||||
username: &str,
|
username: &str,
|
||||||
profile_pic_url: Option<String>,
|
profile_pic_url: Option<String>,
|
||||||
msg: &str,
|
msg: &str,
|
||||||
attachments: &Option<Vec<AttachmentType<'_>>>,
|
attachments: &Option<Vec<AttachmentType<'_>>>,
|
||||||
) -> error::Result<Message> {
|
) -> error::Result<()> {
|
||||||
let webhook = http.get_webhook(webhook_id.0).await?;
|
let webhook = http.get_webhook(webhook_id.0).await?;
|
||||||
|
|
||||||
let sent_msg = webhook
|
webhook
|
||||||
.execute(http, true, move |w| {
|
.execute(http, false, move |w| {
|
||||||
w.content(msg).username(username);
|
w.content(&msg).username(username);
|
||||||
|
|
||||||
if let Some(profile_pic_url) = profile_pic_url {
|
if let Some(profile_pic_url) = profile_pic_url {
|
||||||
w.avatar_url(profile_pic_url);
|
w.avatar_url(profile_pic_url);
|
||||||
|
@ -166,24 +158,26 @@ pub async fn send_webhook_msg(
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if let Some(sent_msg) = sent_msg {
|
Ok(())
|
||||||
Ok(sent_msg)
|
|
||||||
} else {
|
|
||||||
Err(WoxlfError::WebhookMsgError)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_private_message(
|
async fn send_private_message(
|
||||||
http: &Http,
|
http: &Http,
|
||||||
|
src_username: &str,
|
||||||
dest: UserId,
|
dest: UserId,
|
||||||
msg_content: &str,
|
msg: &str,
|
||||||
attachments: &Option<Vec<AttachmentType<'_>>>,
|
attachments: &Option<Vec<AttachmentType<'_>>>,
|
||||||
) -> error::Result<Message> {
|
) -> error::Result<()> {
|
||||||
let dest_user = dest.to_user(http).await?;
|
let dest_user = dest.to_user(http).await?;
|
||||||
|
|
||||||
let msg = dest_user
|
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
|
||||||
.dm(http, |msg| {
|
.dm(http, |msg| {
|
||||||
msg.content(msg_content);
|
msg.content(dm_message);
|
||||||
|
|
||||||
if let Some(attachments) = attachments {
|
if let Some(attachments) = attachments {
|
||||||
msg.add_files(attachments.clone());
|
msg.add_files(attachments.clone());
|
||||||
|
@ -193,15 +187,15 @@ pub async fn send_private_message(
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(msg)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_to_host_channel(
|
async fn send_to_host_channel(
|
||||||
http: &Http,
|
http: &Http,
|
||||||
guild: &Guild,
|
guild: &Guild,
|
||||||
global_data: &GlobalData,
|
global_data: &GlobalData,
|
||||||
msg: WoxlfMessage<'_>,
|
msg: WoxlfMessage<'_>,
|
||||||
) -> error::Result<Message> {
|
) -> error::Result<()> {
|
||||||
let source = match &msg.source {
|
let source = match &msg.source {
|
||||||
MessageSource::Player(player_data) => {
|
MessageSource::Player(player_data) => {
|
||||||
let name = guild
|
let name = guild
|
||||||
|
@ -224,7 +218,7 @@ pub async fn send_to_host_channel(
|
||||||
.get(&UserId::from(dest_player.discord_id))
|
.get(&UserId::from(dest_player.discord_id))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.display_name();
|
.display_name();
|
||||||
format!("**[DM to {} ({})]** ", dest_player.codename, name)
|
format!(" to {} ({})", dest_player.codename, name)
|
||||||
} else {
|
} else {
|
||||||
"".to_string()
|
"".to_string()
|
||||||
}
|
}
|
||||||
|
@ -232,19 +226,23 @@ pub async fn send_to_host_channel(
|
||||||
_ => "".to_string(),
|
_ => "".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let host_channel_username = format!("{} ({})", msg.get_message_username(global_data)?, source,);
|
let host_channel_username = format!(
|
||||||
|
"{} ({}){}",
|
||||||
let content = format!("{}{}", dest, msg.content);
|
msg.get_message_username(global_data)?,
|
||||||
|
source,
|
||||||
|
dest
|
||||||
|
);
|
||||||
|
|
||||||
send_webhook_msg(
|
send_webhook_msg(
|
||||||
http,
|
http,
|
||||||
WebhookId::from(global_data.cfg.discord_config.host_webhook_id),
|
WebhookId::from(global_data.cfg.discord_config.host_webhook_id),
|
||||||
&host_channel_username,
|
&host_channel_username,
|
||||||
Some(msg.get_profile_pic(global_data)?),
|
Some(msg.get_profile_pic(global_data)?),
|
||||||
&content,
|
&msg.content,
|
||||||
&msg.attachments,
|
&msg.attachments,
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_message(
|
pub async fn send_message(
|
||||||
|
@ -253,77 +251,44 @@ pub async fn send_message(
|
||||||
msg: &WoxlfMessage<'_>,
|
msg: &WoxlfMessage<'_>,
|
||||||
dest_player: &PlayerData,
|
dest_player: &PlayerData,
|
||||||
) -> Result<(), WoxlfError> {
|
) -> Result<(), WoxlfError> {
|
||||||
let content = if msg.flags.contains(MsgFlags::PING) {
|
match &msg.median {
|
||||||
let dest_player_id = UserId::from(dest_player.discord_id);
|
|
||||||
|
|
||||||
MessageBuilder::new()
|
|
||||||
.push_line(dest_player_id.mention())
|
|
||||||
.push(msg.content.clone())
|
|
||||||
.build()
|
|
||||||
} else {
|
|
||||||
msg.content.clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
let send_msg = match &msg.median {
|
|
||||||
Median::Webhook => {
|
Median::Webhook => {
|
||||||
send_webhook_msg(
|
send_webhook_msg(
|
||||||
&ctx.http,
|
&ctx.http,
|
||||||
WebhookId::from(dest_player.channel_webhook_id),
|
WebhookId::from(dest_player.channel_webhook_id),
|
||||||
&msg.get_message_username(global_data)?,
|
&msg.get_message_username(global_data)?,
|
||||||
Some(msg.get_profile_pic(global_data)?),
|
Some(msg.get_profile_pic(global_data)?),
|
||||||
&content,
|
&msg.content,
|
||||||
&msg.attachments,
|
&msg.attachments,
|
||||||
)
|
)
|
||||||
.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(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),
|
||||||
&dm_msg,
|
&msg.content,
|
||||||
&msg.attachments,
|
&msg.attachments,
|
||||||
)
|
)
|
||||||
.await?
|
.await?;
|
||||||
}
|
}
|
||||||
Median::StandardMessage => {
|
Median::StandardMessage => {
|
||||||
let channel = ChannelId::from(dest_player.channel);
|
let channel = ChannelId::from(dest_player.channel);
|
||||||
channel.say(&ctx.http(), &content).await?
|
channel.say(&ctx.http(), &msg.content).await?;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if msg.flags.contains(MsgFlags::PIN_MSG) {
|
|
||||||
send_msg.pin(&ctx.http).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
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: &GlobalData,
|
||||||
msg: WoxlfMessage<'_>,
|
msg: WoxlfMessage<'_>,
|
||||||
) -> error::Result<()> {
|
) -> error::Result<()> {
|
||||||
let data = ctx.data.read().await;
|
let guild_id = global_data.cfg.discord_config.guild_id;
|
||||||
let listeners = data.get::<Listeners>().unwrap();
|
let guild = GuildId::from(guild_id).to_guild_cached(&ctx.cache).unwrap();
|
||||||
let mut listeners = listeners.lock().await;
|
|
||||||
|
|
||||||
if listeners
|
|
||||||
.process_event(ctx, global_data, WoxlfEvent::Chat(msg.clone()))
|
|
||||||
.await?
|
|
||||||
== EventStatus::Canceled
|
|
||||||
{
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
|
|
||||||
let msg_tasks = global_data
|
let msg_tasks = global_data
|
||||||
.game_state()?
|
.game_state()?
|
||||||
|
@ -344,5 +309,7 @@ pub async fn dispatch_message(
|
||||||
|
|
||||||
results?;
|
results?;
|
||||||
|
|
||||||
|
send_to_host_channel(&ctx.http, &guild, global_data, msg).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,8 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub mod game_state;
|
pub mod game_state;
|
||||||
pub mod global_data;
|
pub mod global_data;
|
||||||
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,4 +1,3 @@
|
||||||
use crate::game::role::Role;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
@ -10,7 +9,6 @@ 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 {
|
||||||
|
|
|
@ -1,164 +0,0 @@
|
||||||
use crate::game::listener::Listeners;
|
|
||||||
use crate::game::role::spy::SpyListener;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::fmt::{Display, Formatter};
|
|
||||||
|
|
||||||
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,
|
|
||||||
Dreamer,
|
|
||||||
ApprenticeSeer,
|
|
||||||
Necromancer,
|
|
||||||
Helsing,
|
|
||||||
Priest,
|
|
||||||
// Wolf Roles
|
|
||||||
MasterWolf,
|
|
||||||
WolfShaman,
|
|
||||||
Wolf,
|
|
||||||
WolfAgent,
|
|
||||||
WolfDreamer,
|
|
||||||
// Vampires,
|
|
||||||
TheCount,
|
|
||||||
ThinBlood,
|
|
||||||
Vampire,
|
|
||||||
// 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,
|
|
||||||
Role::Dreamer => "Dreamer",
|
|
||||||
Role::ApprenticeSeer => "Apprentice Seer",
|
|
||||||
Role::WolfAgent => "Wolf Agent",
|
|
||||||
Role::WolfDreamer => "Wolf Dreamer",
|
|
||||||
Role::Necromancer => "Necromancer",
|
|
||||||
Role::Helsing => "Helsing",
|
|
||||||
Role::TheCount => "The Count",
|
|
||||||
Role::ThinBlood => "Thin Blood",
|
|
||||||
Role::Vampire => "Vampire",
|
|
||||||
Role::Priest => "Priest",
|
|
||||||
};
|
|
||||||
|
|
||||||
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::Green,
|
|
||||||
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::Dreamer => RoleColor::Red,
|
|
||||||
Role::Custom(_, color) => color.clone(),
|
|
||||||
Role::ApprenticeSeer => RoleColor::Blue,
|
|
||||||
Role::WolfAgent => RoleColor::Red,
|
|
||||||
Role::WolfDreamer => RoleColor::Red,
|
|
||||||
Role::Necromancer => RoleColor::Blue,
|
|
||||||
Role::Helsing => RoleColor::Blue,
|
|
||||||
Role::TheCount => RoleColor::Purple,
|
|
||||||
Role::ThinBlood => RoleColor::Green,
|
|
||||||
Role::Vampire => RoleColor::Purple,
|
|
||||||
Role::Priest => RoleColor::Blue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Color as seen by player
|
|
||||||
pub fn player_color(&self) -> RoleColor {
|
|
||||||
match self {
|
|
||||||
Role::Miller => RoleColor::Green,
|
|
||||||
Role::Herring => 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(),
|
|
||||||
Role::Herring => 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 { role: Role::Spy })),
|
|
||||||
Role::WolfAgent => listeners.add_listener(Box::new(SpyListener {
|
|
||||||
role: Role::WolfAgent,
|
|
||||||
})),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
use crate::game::listener::{EventStatus, Listener, ListenerContext, Priority};
|
|
||||||
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;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct SpyListener {
|
|
||||||
pub role: Role,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl Listener for SpyListener {
|
|
||||||
fn name(&self) -> String {
|
|
||||||
format!("{} DM Listener", self.role)
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("{} is processing {:?}", self.name(), msg);
|
|
||||||
|
|
||||||
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 == self.role);
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 50% chance to intercept message
|
|
||||||
if thread_rng().gen_bool(0.50) {
|
|
||||||
println!("Sending a spy message...");
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
use reqwest::Client;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ImgurError {
|
||||||
|
ReqwestError(reqwest::Error),
|
||||||
|
ImgurRequestError(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<reqwest::Error> for ImgurError {
|
||||||
|
fn from(e: reqwest::Error) -> Self {
|
||||||
|
Self::ReqwestError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ImgurError {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let msg = match self {
|
||||||
|
ImgurError::ReqwestError(err) => format!("Reqwest error: {}", err),
|
||||||
|
ImgurError::ImgurRequestError(msg) => format!("Imgur request error: {}", msg),
|
||||||
|
};
|
||||||
|
|
||||||
|
write!(f, "{}", msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
pub struct AlbumData {
|
||||||
|
images: Option<Vec<Image>>,
|
||||||
|
error: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
pub struct AlbumResponse {
|
||||||
|
data: AlbumData,
|
||||||
|
success: bool,
|
||||||
|
status: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
pub struct Image {
|
||||||
|
pub id: String,
|
||||||
|
pub title: Option<String>,
|
||||||
|
pub description: Option<String>,
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub img_type: String,
|
||||||
|
pub animated: bool,
|
||||||
|
pub width: i32,
|
||||||
|
pub height: i32,
|
||||||
|
pub size: i32,
|
||||||
|
pub link: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_album_images(client_id: &str, album_hash: &str) -> Result<Vec<Image>, ImgurError> {
|
||||||
|
let client = Client::new();
|
||||||
|
|
||||||
|
let res = client
|
||||||
|
.get(format!("https://api.imgur.com/3/album/{}", album_hash))
|
||||||
|
.header("Authorization", format!("Client-ID {}", client_id))
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let album_response: AlbumResponse = res.json().await?;
|
||||||
|
|
||||||
|
if album_response.success {
|
||||||
|
Ok(album_response.data.images.unwrap())
|
||||||
|
} else {
|
||||||
|
Err(ImgurError::ImgurRequestError(
|
||||||
|
album_response.data.error.unwrap(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
12
src/main.rs
12
src/main.rs
|
@ -3,20 +3,19 @@ use std::sync::Arc;
|
||||||
use serenity::prelude::*;
|
use serenity::prelude::*;
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use discord::command_framework;
|
use discord::commands::command_framework;
|
||||||
use discord::event_handler::Handler;
|
use discord::event_handler::Handler;
|
||||||
use game::global_data::GlobalData;
|
use game::global_data::GlobalData;
|
||||||
|
|
||||||
use crate::config::{Args, BotConfig};
|
use crate::config::{Args, BotConfig};
|
||||||
use crate::game::listener::Listeners;
|
|
||||||
use crate::game::role::Role;
|
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
mod discord;
|
mod discord;
|
||||||
mod error;
|
mod error;
|
||||||
mod game;
|
mod game;
|
||||||
|
mod imgur;
|
||||||
mod messages;
|
mod messages;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let args: Args = Args::from_args();
|
let args: Args = Args::from_args();
|
||||||
|
@ -32,15 +31,10 @@ 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);
|
|
||||||
Role::WolfAgent.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)))
|
|
||||||
.await
|
.await
|
||||||
.expect("Err creating client");
|
.expect("Err creating client");
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
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};
|
||||||
|
@ -34,48 +33,6 @@ 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()),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn seer_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.seer_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,
|
||||||
|
@ -200,9 +157,6 @@ 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.register_function("seer_color", seer_color());
|
|
||||||
|
|
||||||
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