Added profile pic support + fixed an issue with webhook serailization
+ Added imgur module to handle interacting with the imgur api + Users now get a random profile pic from an imgur album + Switched to using webhook ids instead of storing the full webhook in toml, serenity does not seem to like the toml deserialization
This commit is contained in:
+10
-3
@@ -11,17 +11,24 @@ pub struct Args {
|
||||
pub cfg_path: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
pub struct GameConfig {
|
||||
pub occupation: Vec<String>,
|
||||
pub adjective: Vec<String>,
|
||||
pub profile_album_hash: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
pub struct BotConfig {
|
||||
pub token: String,
|
||||
pub app_id: u64,
|
||||
pub host_channel: u64,
|
||||
pub host_webhook: String,
|
||||
pub host_webhook_id: u64,
|
||||
pub vote_channel: u64,
|
||||
pub category: u64,
|
||||
pub imgur_client_id: String,
|
||||
pub game_state_dir: PathBuf,
|
||||
pub occupation: Vec<String>,
|
||||
pub adjective: Vec<String>,
|
||||
pub game_config: GameConfig,
|
||||
}
|
||||
|
||||
impl BotConfig {
|
||||
|
||||
@@ -61,21 +61,7 @@ impl EventHandler for Handler {
|
||||
}
|
||||
}
|
||||
|
||||
async fn ready(&self, ctx: Context, ready: Ready) {
|
||||
let mut data = ctx.data.write().await;
|
||||
|
||||
let global_data = data.get_mut::<GlobalData>().unwrap();
|
||||
|
||||
let mut global_data = global_data.lock().await;
|
||||
|
||||
let host_webhook = ctx
|
||||
.http
|
||||
.get_webhook_from_url(&global_data.cfg.host_webhook)
|
||||
.await
|
||||
.expect("Unable to open host webhook");
|
||||
|
||||
global_data.host_webhook = Some(host_webhook);
|
||||
|
||||
async fn ready(&self, _ctx: Context, ready: Ready) {
|
||||
println!("{} is connected!", ready.user.name);
|
||||
}
|
||||
}
|
||||
|
||||
+31
-21
@@ -13,9 +13,17 @@ use crate::game::global_data::GlobalData;
|
||||
use crate::game::player_data::PlayerData;
|
||||
use crate::game::MessageSource;
|
||||
use crate::{error, game};
|
||||
use serenity::model::prelude::Webhook;
|
||||
use serenity::prelude::SerenityError;
|
||||
|
||||
fn filter_source_channel(player_data: &&PlayerData, msg_source: &MessageSource) -> bool {
|
||||
if let MessageSource::Player(source_player) = &msg_source {
|
||||
if source_player.channel == player_data.channel {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub async fn send_msg_to_player_channels(
|
||||
ctx: &Context,
|
||||
guild: &Guild,
|
||||
@@ -29,14 +37,7 @@ pub async fn send_msg_to_player_channels(
|
||||
.game_state_mut()?
|
||||
.player_data
|
||||
.iter()
|
||||
.filter(|player_data| {
|
||||
if let MessageSource::Player(source_player) = &msg_source {
|
||||
if source_player.channel == player_data.channel {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
})
|
||||
.filter(|player| filter_source_channel(player, &msg_source))
|
||||
.map(|player_data| {
|
||||
let channel = guild
|
||||
.channels
|
||||
@@ -107,15 +108,23 @@ pub async fn send_msg_to_player_channels(
|
||||
|
||||
pub async fn send_webhook_msg(
|
||||
http: &Http,
|
||||
webhook: &Webhook,
|
||||
webhook_id: u64,
|
||||
username: &str,
|
||||
profile_pic_url: Option<String>,
|
||||
msg: &str,
|
||||
attachment: Option<Vec<AttachmentType<'_>>>,
|
||||
) -> error::Result<()> {
|
||||
|
||||
let webhook = http.get_webhook(webhook_id).await?;
|
||||
|
||||
webhook
|
||||
.execute(http, false, |w| {
|
||||
w.content(&msg).username(username);
|
||||
|
||||
if let Some(profile_pic_url) = profile_pic_url {
|
||||
w.avatar_url(profile_pic_url);
|
||||
}
|
||||
|
||||
if let Some(attachment) = attachment.clone() {
|
||||
w.add_files(attachment);
|
||||
}
|
||||
@@ -141,23 +150,22 @@ pub async fn send_webhook_msg_to_player_channels(
|
||||
MessageSource::Automated => "Woxlf System Message".to_string(),
|
||||
};
|
||||
|
||||
let profile_pic = match &msg_source {
|
||||
MessageSource::Player(p) => Some(p.profile_pic_url.clone()),
|
||||
MessageSource::Host | MessageSource::Automated => None,
|
||||
};
|
||||
|
||||
let msg_tasks = global_data
|
||||
.game_state_mut()?
|
||||
.player_data
|
||||
.iter()
|
||||
.filter(|player_data| {
|
||||
if let MessageSource::Player(source_player) = &msg_source {
|
||||
if source_player.channel == player_data.channel {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
})
|
||||
.filter(|player| filter_source_channel(player, &msg_source))
|
||||
.map(|player_data| {
|
||||
send_webhook_msg(
|
||||
&ctx.http,
|
||||
&player_data.channel_webhook,
|
||||
player_data.channel_webhook_id,
|
||||
&msg_username,
|
||||
profile_pic.clone(),
|
||||
msg,
|
||||
attachment.clone(),
|
||||
)
|
||||
@@ -188,8 +196,9 @@ pub async fn send_webhook_msg_to_player_channels(
|
||||
|
||||
send_webhook_msg(
|
||||
&ctx.http,
|
||||
global_data.host_webhook()?,
|
||||
global_data.cfg.host_webhook_id,
|
||||
&host_channel_username,
|
||||
profile_pic,
|
||||
msg,
|
||||
attachment,
|
||||
)
|
||||
@@ -257,7 +266,8 @@ pub async fn add_user_to_game(
|
||||
discord_id: discord_user.user.id.0,
|
||||
vote_target: None,
|
||||
codename,
|
||||
channel_webhook: webhook,
|
||||
channel_webhook_id: webhook.id.0,
|
||||
profile_pic_url: global_data.get_profile_pic_url().await?,
|
||||
};
|
||||
|
||||
global_data.game_state_mut()?.player_data.push(player_data);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::imgur::ImgurError;
|
||||
use serenity::prelude::SerenityError;
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
@@ -13,6 +14,7 @@ pub enum WoxlfError {
|
||||
DiscordIdParseError(String),
|
||||
GameNotInProgress,
|
||||
HostWebhookError,
|
||||
ImgurError(ImgurError),
|
||||
}
|
||||
|
||||
impl std::error::Error for WoxlfError {}
|
||||
@@ -28,6 +30,7 @@ impl Display for WoxlfError {
|
||||
WoxlfError::DiscordIdParseError(e) => format!("Unable to parse player id {}", e),
|
||||
WoxlfError::GameNotInProgress => "A game is not currently in progress".to_string(),
|
||||
WoxlfError::HostWebhookError => "Unable to communicate to the host webhook".to_string(),
|
||||
WoxlfError::ImgurError(err) => format!("Imgur module error: {}", err.to_string()),
|
||||
};
|
||||
|
||||
write!(f, "Woxlf Error: {}", msg)
|
||||
@@ -57,3 +60,9 @@ impl From<toml::ser::Error> for WoxlfError {
|
||||
Self::GameStateSerializeError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ImgurError> for WoxlfError {
|
||||
fn from(err: ImgurError) -> Self {
|
||||
Self::ImgurError(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,15 +9,15 @@ use crate::config::BotConfig;
|
||||
use crate::error::{Result, WoxlfError};
|
||||
use crate::game::game_state::GameState;
|
||||
use crate::game::Phase;
|
||||
use crate::imgur::{get_album_images, Image};
|
||||
use chrono::Duration;
|
||||
use serenity::model::prelude::Webhook;
|
||||
use rand::prelude::SliceRandom;
|
||||
use serenity::utils::MessageBuilder;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
pub struct GlobalData {
|
||||
pub cfg: BotConfig,
|
||||
pub game_state: Option<GameState>,
|
||||
pub host_webhook: Option<Webhook>,
|
||||
}
|
||||
|
||||
impl GlobalData {
|
||||
@@ -25,7 +25,6 @@ impl GlobalData {
|
||||
Self {
|
||||
cfg,
|
||||
game_state: None,
|
||||
host_webhook: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,10 +104,14 @@ impl GlobalData {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn host_webhook(&self) -> Result<&Webhook> {
|
||||
let webhook = &self.host_webhook;
|
||||
pub async fn get_profile_pic_url(&self) -> Result<String> {
|
||||
let images: Vec<Image> = get_album_images(
|
||||
&self.cfg.imgur_client_id,
|
||||
&self.cfg.game_config.profile_album_hash,
|
||||
)
|
||||
.await?;
|
||||
|
||||
webhook.as_ref().ok_or(WoxlfError::HostWebhookError)
|
||||
Ok(images.choose(&mut rand::thread_rng()).unwrap().link.clone())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
-5
@@ -1,10 +1,11 @@
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
use rand::Rng;
|
||||
use rand::thread_rng;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::config::BotConfig;
|
||||
use crate::game::player_data::PlayerData;
|
||||
use rand::prelude::SliceRandom;
|
||||
|
||||
pub mod game_state;
|
||||
pub mod global_data;
|
||||
@@ -34,10 +35,16 @@ impl Display for Phase {
|
||||
}
|
||||
|
||||
pub fn generate_codename(config: &BotConfig) -> String {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let occupation = &config.occupation[rng.gen_range(0..config.occupation.len())];
|
||||
let adj = &config.adjective[rng.gen_range(0..config.adjective.len())];
|
||||
let occupation = &config
|
||||
.game_config
|
||||
.occupation
|
||||
.choose(&mut thread_rng())
|
||||
.unwrap();
|
||||
let adj = &config
|
||||
.game_config
|
||||
.adjective
|
||||
.choose(&mut thread_rng())
|
||||
.unwrap();
|
||||
|
||||
format!("{} {}", adj, occupation)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serenity::model::prelude::Webhook;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
pub struct PlayerData {
|
||||
@@ -7,7 +6,8 @@ pub struct PlayerData {
|
||||
pub discord_id: u64,
|
||||
pub codename: String,
|
||||
pub vote_target: Option<u64>,
|
||||
pub channel_webhook: Webhook,
|
||||
pub profile_pic_url: String,
|
||||
pub channel_webhook_id: u64,
|
||||
}
|
||||
|
||||
impl PlayerData {
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
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.to_string()),
|
||||
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(),
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ mod config;
|
||||
mod discord;
|
||||
mod error;
|
||||
mod game;
|
||||
mod imgur;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
|
||||
Reference in New Issue
Block a user