1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
use crate::{SlackClient, SlackClientHttpConnector}; use rsb_derive::Builder; use std::any::{Any, TypeId}; use std::collections::HashMap; use std::fmt::Debug; use std::sync::{Arc, RwLock}; type UserStatesMap = HashMap<TypeId, Box<dyn Any + Send + Sync + 'static>>; pub struct SlackClientEventsListenerEnvironment<SCHC> where SCHC: SlackClientHttpConnector + Send + Clone + Sync, { pub client: Arc<SlackClient<SCHC>>, pub error_handler: BoxedErrorHandler<SCHC>, pub user_state_storage: Arc<RwLock<SlackClientEventsUserStateStorage>>, } impl<SCHC> SlackClientEventsListenerEnvironment<SCHC> where SCHC: SlackClientHttpConnector + Send + Clone + Sync, { pub fn new(client: Arc<SlackClient<SCHC>>) -> Self { Self { client, error_handler: Box::new(Self::empty_error_handler), user_state_storage: Arc::new(RwLock::new(SlackClientEventsUserStateStorage::new())), } } pub fn with_error_handler(self, error_handler: ErrorHandler<SCHC>) -> Self { Self { error_handler: Box::new(error_handler), ..self } } fn empty_error_handler( _err: Box<dyn std::error::Error + Send + Sync>, _client: Arc<SlackClient<SCHC>>, _user_state_storage: Arc<RwLock<SlackClientEventsUserStateStorage>>, ) -> http::StatusCode { http::StatusCode::BAD_REQUEST } pub fn with_user_state<T: Send + Sync + 'static>(self, state: T) -> Self { self.user_state_storage .write() .unwrap() .set_user_state(state); self } } pub struct SlackClientEventsUserStateStorage { user_state_map: UserStatesMap, } impl SlackClientEventsUserStateStorage { pub fn new() -> Self { SlackClientEventsUserStateStorage { user_state_map: HashMap::new(), } } pub fn get_user_state<T: Send + Sync + 'static>(&self) -> Option<&T> { self.user_state_map .get(&TypeId::of::<T>()) .and_then(|boxed| (&**boxed as &(dyn Any + 'static)).downcast_ref()) } pub fn set_user_state<T: Send + Sync + 'static>(&mut self, state: T) { self.user_state_map .insert(TypeId::of::<T>(), Box::new(state)); } pub fn len(&self) -> usize { self.user_state_map.len() } pub fn is_empty(&self) -> bool { self.user_state_map.is_empty() } } pub type BoxedErrorHandler<SCHC> = Box<ErrorHandler<SCHC>>; pub type ErrorHandler<SCHC> = fn( Box<dyn std::error::Error + Send + Sync + 'static>, Arc<SlackClient<SCHC>>, Arc<RwLock<SlackClientEventsUserStateStorage>>, ) -> http::StatusCode; #[derive(Debug, PartialEq, Clone, Builder)] pub struct SlackCommandEventsListenerConfig { pub events_signing_secret: String, #[default = "SlackCommandEventsListenerConfig::DEFAULT_EVENTS_URL_VALUE.into()"] pub events_path: String, } impl SlackCommandEventsListenerConfig { pub const DEFAULT_EVENTS_URL_VALUE: &'static str = "/command"; } #[derive(Debug, PartialEq, Clone, Builder)] pub struct SlackPushEventsListenerConfig { pub events_signing_secret: String, #[default = "SlackPushEventsListenerConfig::DEFAULT_EVENTS_URL_VALUE.into()"] pub events_path: String, } impl SlackPushEventsListenerConfig { const DEFAULT_EVENTS_URL_VALUE: &'static str = "/push"; } #[derive(Debug, PartialEq, Clone, Builder)] pub struct SlackInteractionEventsListenerConfig { pub events_signing_secret: String, #[default = "SlackInteractionEventsListenerConfig::DEFAULT_EVENTS_URL_VALUE.into()"] pub events_path: String, } impl SlackInteractionEventsListenerConfig { pub const DEFAULT_EVENTS_URL_VALUE: &'static str = "/interaction"; } #[derive(Debug, PartialEq, Clone, Builder)] pub struct SlackOAuthListenerConfig { pub client_id: String, pub client_secret: String, pub bot_scope: String, pub redirect_callback_host: String, #[default = "SlackOAuthListenerConfig::DEFAULT_INSTALL_PATH_VALUE.into()"] pub install_path: String, #[default = "SlackOAuthListenerConfig::DEFAULT_CALLBACK_PATH_VALUE.into()"] pub redirect_callback_path: String, #[default = "SlackOAuthListenerConfig::DEFAULT_INSTALLED_URL_VALUE.into()"] pub redirect_installed_url: String, #[default = "SlackOAuthListenerConfig::DEFAULT_CANCELLED_URL_VALUE.into()"] pub redirect_cancelled_url: String, #[default = "SlackOAuthListenerConfig::DEFAULT_ERROR_URL_VALUE.into()"] pub redirect_error_redirect_url: String, } impl SlackOAuthListenerConfig { pub const DEFAULT_INSTALL_PATH_VALUE: &'static str = "/auth/install"; pub const DEFAULT_CALLBACK_PATH_VALUE: &'static str = "/auth/callback"; pub const DEFAULT_INSTALLED_URL_VALUE: &'static str = "/installed"; pub const DEFAULT_CANCELLED_URL_VALUE: &'static str = "/cancelled"; pub const DEFAULT_ERROR_URL_VALUE: &'static str = "/error"; pub const OAUTH_AUTHORIZE_URL_VALUE: &'static str = "https://slack.com/oauth/v2/authorize"; pub fn to_redirect_url(&self) -> String { format!( "{}{}", &self.redirect_callback_host, &self.redirect_callback_path ) } }