Initial Webhook Support
+ Host messages still come through the bot instead of webhooks + Each player channel gets one webhook that all messages come through + The username is set per message + Currently, profile pics are the default + clippy + fmtmsg_refactor
parent
673d5b56f0
commit
71b8bc6e20
|
@ -16,6 +16,7 @@ pub struct BotConfig {
|
||||||
pub token: String,
|
pub token: String,
|
||||||
pub app_id: u64,
|
pub app_id: u64,
|
||||||
pub host_channel: u64,
|
pub host_channel: u64,
|
||||||
|
pub host_webhook: String,
|
||||||
pub vote_channel: u64,
|
pub vote_channel: u64,
|
||||||
pub category: u64,
|
pub category: u64,
|
||||||
pub game_state_dir: PathBuf,
|
pub game_state_dir: PathBuf,
|
||||||
|
|
|
@ -153,7 +153,7 @@ async fn broadcast(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||||
ctx,
|
ctx,
|
||||||
&guild,
|
&guild,
|
||||||
&mut global_data,
|
&mut global_data,
|
||||||
MessageSource::Host,
|
MessageSource::Automated,
|
||||||
&msg,
|
&msg,
|
||||||
None,
|
None,
|
||||||
true,
|
true,
|
||||||
|
@ -189,7 +189,7 @@ async fn next_phase(ctx: &Context, msg: &Message, mut args: Args) -> CommandResu
|
||||||
ctx,
|
ctx,
|
||||||
&guild,
|
&guild,
|
||||||
&mut global_data,
|
&mut global_data,
|
||||||
MessageSource::Host,
|
MessageSource::Automated,
|
||||||
&broadcast,
|
&broadcast,
|
||||||
None,
|
None,
|
||||||
true,
|
true,
|
||||||
|
@ -296,7 +296,7 @@ async fn add_time(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult
|
||||||
ctx,
|
ctx,
|
||||||
&guild,
|
&guild,
|
||||||
&mut global_data,
|
&mut global_data,
|
||||||
MessageSource::Host,
|
MessageSource::Automated,
|
||||||
&broadcast,
|
&broadcast,
|
||||||
None,
|
None,
|
||||||
true,
|
true,
|
||||||
|
|
|
@ -4,7 +4,7 @@ use serenity::http::AttachmentType;
|
||||||
use serenity::model::channel::Message;
|
use serenity::model::channel::Message;
|
||||||
use serenity::model::gateway::Ready;
|
use serenity::model::gateway::Ready;
|
||||||
|
|
||||||
use crate::discord::helper::send_msg_to_player_channels;
|
use crate::discord::helper::send_webhook_msg_to_player_channels;
|
||||||
use crate::game::global_data::GlobalData;
|
use crate::game::global_data::GlobalData;
|
||||||
use crate::game::MessageSource;
|
use crate::game::MessageSource;
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ impl EventHandler for Handler {
|
||||||
.get_player_from_channel(msg.channel_id.0)
|
.get_player_from_channel(msg.channel_id.0)
|
||||||
{
|
{
|
||||||
let guild = msg.guild(&ctx.cache).await.unwrap();
|
let guild = msg.guild(&ctx.cache).await.unwrap();
|
||||||
let user_msg = format!("**{}** > {}", player_data.codename, msg.content);
|
let user_msg = msg.content.clone();
|
||||||
|
|
||||||
let attachments: Vec<AttachmentType> = msg
|
let attachments: Vec<AttachmentType> = msg
|
||||||
.attachments
|
.attachments
|
||||||
|
@ -46,21 +46,36 @@ impl EventHandler for Handler {
|
||||||
.map(|a| AttachmentType::Image(&a.url))
|
.map(|a| AttachmentType::Image(&a.url))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
send_msg_to_player_channels(
|
let msg_source = MessageSource::Player(Box::new(player_data.clone()));
|
||||||
|
|
||||||
|
send_webhook_msg_to_player_channels(
|
||||||
&ctx,
|
&ctx,
|
||||||
&guild,
|
&guild,
|
||||||
&mut global_data,
|
&mut global_data,
|
||||||
MessageSource::Player(msg.channel_id.0),
|
msg_source,
|
||||||
&user_msg,
|
&user_msg,
|
||||||
Some(attachments),
|
Some(attachments),
|
||||||
false,
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.expect("Unable to send message to players");
|
.expect("Unable to send message to players");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn ready(&self, _ctx: Context, ready: Ready) {
|
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);
|
||||||
|
|
||||||
println!("{} is connected!", ready.user.name);
|
println!("{} is connected!", ready.user.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use serenity::client::Context;
|
use serenity::client::Context;
|
||||||
use serenity::framework::standard::Args;
|
use serenity::framework::standard::Args;
|
||||||
use serenity::http::AttachmentType;
|
use serenity::http::{AttachmentType, Http};
|
||||||
use serenity::model::channel::{Message, PermissionOverwrite, PermissionOverwriteType};
|
use serenity::model::channel::{Message, PermissionOverwrite, PermissionOverwriteType};
|
||||||
use serenity::model::guild::{Guild, Member};
|
use serenity::model::guild::{Guild, Member};
|
||||||
use serenity::model::id::{ChannelId, UserId};
|
use serenity::model::id::{ChannelId, UserId};
|
||||||
|
@ -13,6 +13,7 @@ use crate::game::global_data::GlobalData;
|
||||||
use crate::game::player_data::PlayerData;
|
use crate::game::player_data::PlayerData;
|
||||||
use crate::game::MessageSource;
|
use crate::game::MessageSource;
|
||||||
use crate::{error, game};
|
use crate::{error, game};
|
||||||
|
use serenity::model::prelude::Webhook;
|
||||||
use serenity::prelude::SerenityError;
|
use serenity::prelude::SerenityError;
|
||||||
|
|
||||||
pub async fn send_msg_to_player_channels(
|
pub async fn send_msg_to_player_channels(
|
||||||
|
@ -29,8 +30,8 @@ pub async fn send_msg_to_player_channels(
|
||||||
.player_data
|
.player_data
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|player_data| {
|
.filter(|player_data| {
|
||||||
if let MessageSource::Player(channel_id) = msg_source {
|
if let MessageSource::Player(source_player) = &msg_source {
|
||||||
if channel_id == player_data.channel {
|
if source_player.channel == player_data.channel {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,15 +78,10 @@ pub async fn send_msg_to_player_channels(
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let source = match msg_source {
|
let source = match msg_source {
|
||||||
MessageSource::Player(channel_id) => {
|
MessageSource::Player(player_data) => {
|
||||||
let discord_id = global_data
|
|
||||||
.game_state_mut()?
|
|
||||||
.get_player_from_channel(channel_id)
|
|
||||||
.unwrap()
|
|
||||||
.discord_id;
|
|
||||||
let name = guild
|
let name = guild
|
||||||
.members
|
.members
|
||||||
.get(&UserId::from(discord_id))
|
.get(&UserId::from(player_data.discord_id))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.display_name();
|
.display_name();
|
||||||
|
|
||||||
|
@ -109,6 +105,98 @@ pub async fn send_msg_to_player_channels(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn send_webhook_msg(
|
||||||
|
http: &Http,
|
||||||
|
webhook: &Webhook,
|
||||||
|
username: &str,
|
||||||
|
msg: &str,
|
||||||
|
attachment: Option<Vec<AttachmentType<'_>>>,
|
||||||
|
) -> error::Result<()> {
|
||||||
|
webhook
|
||||||
|
.execute(http, false, |w| {
|
||||||
|
w.content(&msg).username(username);
|
||||||
|
|
||||||
|
if let Some(attachment) = attachment.clone() {
|
||||||
|
w.add_files(attachment);
|
||||||
|
}
|
||||||
|
|
||||||
|
w
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_webhook_msg_to_player_channels(
|
||||||
|
ctx: &Context,
|
||||||
|
guild: &Guild,
|
||||||
|
global_data: &mut GlobalData,
|
||||||
|
msg_source: MessageSource,
|
||||||
|
msg: &str,
|
||||||
|
attachment: Option<Vec<AttachmentType<'_>>>,
|
||||||
|
) -> error::Result<()> {
|
||||||
|
let msg_username = match &msg_source {
|
||||||
|
MessageSource::Player(p) => p.codename.clone(),
|
||||||
|
MessageSource::Host => "Woxlf".to_string(),
|
||||||
|
MessageSource::Automated => "Woxlf System Message".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
})
|
||||||
|
.map(|player_data| {
|
||||||
|
send_webhook_msg(
|
||||||
|
&ctx.http,
|
||||||
|
&player_data.channel_webhook,
|
||||||
|
&msg_username,
|
||||||
|
msg,
|
||||||
|
attachment.clone(),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let msgs: Result<(), WoxlfError> = futures::future::join_all(msg_tasks)
|
||||||
|
.await
|
||||||
|
.into_iter()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
msgs?;
|
||||||
|
|
||||||
|
let source = match &msg_source {
|
||||||
|
MessageSource::Player(player_data) => {
|
||||||
|
let name = guild
|
||||||
|
.members
|
||||||
|
.get(&UserId::from(player_data.discord_id))
|
||||||
|
.unwrap()
|
||||||
|
.display_name();
|
||||||
|
|
||||||
|
name.to_string()
|
||||||
|
}
|
||||||
|
MessageSource::Host => "Host".to_string(),
|
||||||
|
MessageSource::Automated => "Automated".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let host_channel_username = format!("{} ({})", msg_username, source);
|
||||||
|
|
||||||
|
send_webhook_msg(
|
||||||
|
&ctx.http,
|
||||||
|
global_data.host_webhook()?,
|
||||||
|
&host_channel_username,
|
||||||
|
msg,
|
||||||
|
attachment,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn add_user_to_game(
|
pub async fn add_user_to_game(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
guild: &Guild,
|
guild: &Guild,
|
||||||
|
@ -139,6 +227,13 @@ pub async fn add_user_to_game(
|
||||||
|
|
||||||
channel.create_permission(&ctx.http, &overwrite).await?;
|
channel.create_permission(&ctx.http, &overwrite).await?;
|
||||||
|
|
||||||
|
let webhook = channel
|
||||||
|
.create_webhook(
|
||||||
|
&ctx.http,
|
||||||
|
format!("{}'s Woxlf Webhook", discord_user.display_name()),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
let msg = channel.send_message(&ctx.http, |m| {
|
let msg = channel.send_message(&ctx.http, |m| {
|
||||||
m.content(MessageBuilder::new()
|
m.content(MessageBuilder::new()
|
||||||
.push("Welcome ")
|
.push("Welcome ")
|
||||||
|
@ -162,6 +257,7 @@ pub async fn add_user_to_game(
|
||||||
discord_id: discord_user.user.id.0,
|
discord_id: discord_user.user.id.0,
|
||||||
vote_target: None,
|
vote_target: None,
|
||||||
codename,
|
codename,
|
||||||
|
channel_webhook: webhook,
|
||||||
};
|
};
|
||||||
|
|
||||||
global_data.game_state_mut()?.player_data.push(player_data);
|
global_data.game_state_mut()?.player_data.push(player_data);
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
pub mod helper;
|
|
||||||
pub mod event_handler;
|
pub mod event_handler;
|
||||||
|
pub mod helper;
|
||||||
|
|
|
@ -12,6 +12,7 @@ pub enum WoxlfError {
|
||||||
SerenityError(serenity::Error),
|
SerenityError(serenity::Error),
|
||||||
DiscordIdParseError(String),
|
DiscordIdParseError(String),
|
||||||
GameNotInProgress,
|
GameNotInProgress,
|
||||||
|
HostWebhookError,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for WoxlfError {}
|
impl std::error::Error for WoxlfError {}
|
||||||
|
@ -26,6 +27,7 @@ impl Display for WoxlfError {
|
||||||
WoxlfError::SerenityError(e) => format!("Serenity error: {}", e),
|
WoxlfError::SerenityError(e) => format!("Serenity error: {}", e),
|
||||||
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(),
|
||||||
};
|
};
|
||||||
|
|
||||||
write!(f, "Woxlf Error: {}", msg)
|
write!(f, "Woxlf Error: {}", msg)
|
||||||
|
|
|
@ -10,12 +10,14 @@ 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 chrono::Duration;
|
use chrono::Duration;
|
||||||
|
use serenity::model::prelude::Webhook;
|
||||||
use serenity::utils::MessageBuilder;
|
use serenity::utils::MessageBuilder;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
pub struct GlobalData {
|
pub struct GlobalData {
|
||||||
pub cfg: BotConfig,
|
pub cfg: BotConfig,
|
||||||
pub game_state: Option<GameState>,
|
pub game_state: Option<GameState>,
|
||||||
|
pub host_webhook: Option<Webhook>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GlobalData {
|
impl GlobalData {
|
||||||
|
@ -23,6 +25,7 @@ impl GlobalData {
|
||||||
Self {
|
Self {
|
||||||
cfg,
|
cfg,
|
||||||
game_state: None,
|
game_state: None,
|
||||||
|
host_webhook: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,6 +104,12 @@ impl GlobalData {
|
||||||
"Game not in progress.".to_string()
|
"Game not in progress.".to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn host_webhook(&self) -> Result<&Webhook> {
|
||||||
|
let webhook = &self.host_webhook;
|
||||||
|
|
||||||
|
webhook.as_ref().ok_or(WoxlfError::HostWebhookError)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TypeMapKey for GlobalData {
|
impl TypeMapKey for GlobalData {
|
||||||
|
|
|
@ -4,6 +4,7 @@ use rand::Rng;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::config::BotConfig;
|
use crate::config::BotConfig;
|
||||||
|
use crate::game::player_data::PlayerData;
|
||||||
|
|
||||||
pub mod game_state;
|
pub mod game_state;
|
||||||
pub mod global_data;
|
pub mod global_data;
|
||||||
|
@ -41,9 +42,9 @@ pub fn generate_codename(config: &BotConfig) -> String {
|
||||||
format!("{} {}", adj, occupation)
|
format!("{} {}", adj, occupation)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum MessageSource {
|
pub enum MessageSource {
|
||||||
Player(u64),
|
Player(Box<PlayerData>),
|
||||||
Host,
|
Host,
|
||||||
Automated,
|
Automated,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serenity::model::prelude::Webhook;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, Default, Hash)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
pub struct PlayerData {
|
pub struct PlayerData {
|
||||||
pub channel: u64,
|
pub channel: u64,
|
||||||
pub discord_id: u64,
|
pub discord_id: u64,
|
||||||
pub codename: String,
|
pub codename: String,
|
||||||
pub vote_target: Option<u64>,
|
pub vote_target: Option<u64>,
|
||||||
|
pub channel_webhook: Webhook,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlayerData {
|
impl PlayerData {
|
||||||
|
|
Loading…
Reference in New Issue