use lazy_static::lazy_static; use serde::Deserialize; use serenity::{ client::Context, model::channel::{Reaction, ReactionType}, }; use std::collections::HashMap; lazy_static! { static ref ROLE_CONFIG: Vec = { let c = serde_json::from_str( &std::fs::read_to_string("reaction_config.json").unwrap_or_else(|_| "[]".to_string()), ) .expect("Could not read reaction config"); eprintln!("Read reaction config: {:?}", c); c }; } macro_rules! role_action { ($reaction: expr, $ctx: expr, $action: ident) => { if let ReactionType::Unicode(emoji) = &$reaction.emoji { if let Some(&role_id) = ROLE_CONFIG .iter() .find(|c| $reaction.message_id == c.message_id) .and_then(|rm| rm.roles.get(emoji)) { $reaction .guild_id .expect("Configured message id is not in a guild") .member(&$ctx, $reaction.user_id.unwrap()) .await? .$action(&$ctx, role_id) .await? } } }; } pub(crate) async fn reaction_added(ctx: Context, reaction: Reaction) -> serenity::Result<()> { role_action!(reaction, ctx, add_role); Ok(()) } pub(crate) async fn reaction_removed(ctx: Context, reaction: Reaction) -> serenity::Result<()> { role_action!(reaction, ctx, remove_role); Ok(()) } #[derive(Debug, PartialEq, Deserialize)] struct ReactableMessage { // I hate how this sounds like a Java interface name… message_id: u64, roles: HashMap, } #[cfg(test)] mod tests { use super::*; const TEST_JSON: &str = r#" [ { "message_id": 12345, "roles": { "πŸ’€": 1, "πŸ‘": 2 } }, { "message_id": 54321, "roles": { "πŸ’™": 3, "β™»": 4 } } ]"#; #[test] fn test_role_config_parsing() { assert_eq!( serde_json::from_str::>(TEST_JSON).unwrap(), [ ReactableMessage { message_id: 12345, roles: HashMap::from([("πŸ’€".to_string(), 1), ("πŸ‘".to_string(), 2)]), }, ReactableMessage { message_id: 54321, roles: HashMap::from([("πŸ’™".to_string(), 3), ("β™»".to_string(), 4)]), }, ], ); } }