slack_morphism_hyper/listener/
command_events.rs1use crate::listener::SlackClientEventsHyperListener;
2
3use crate::connector::SlackClientHyperConnector;
4use slack_morphism::errors::*;
5use slack_morphism::listener::*;
6use slack_morphism::signature_verifier::SlackEventSignatureVerifier;
7
8use futures::future::{BoxFuture, FutureExt, TryFutureExt};
9use hyper::body::*;
10use hyper::client::connect::Connect;
11use hyper::{Method, Request, Response, StatusCode};
12use slack_morphism::UserCallbackResult;
13pub use slack_morphism_models::events::*;
14pub use slack_morphism_models::SlackResponseUrl;
15use std::collections::HashMap;
16use std::future::Future;
17use std::sync::Arc;
18
19impl<H: 'static + Send + Sync + Connect + Clone> SlackClientEventsHyperListener<H> {
20 pub fn command_events_service_fn<'a, D, F>(
21 &self,
22 config: Arc<SlackCommandEventsListenerConfig>,
23 command_service_fn: UserCallbackFunction<
24 SlackCommandEvent,
25 impl Future<Output = UserCallbackResult<SlackCommandEventResponse>> + 'static + Send,
26 SlackClientHyperConnector<H>,
27 >,
28 ) -> impl Fn(
29 Request<Body>,
30 D,
31 ) -> BoxFuture<
32 'a,
33 Result<Response<Body>, Box<dyn std::error::Error + Send + Sync + 'a>>,
34 >
35 + 'a
36 + Send
37 + Clone
38 where
39 D: Fn(Request<Body>) -> F + 'a + Send + Sync + Clone,
40 F: Future<Output = Result<Response<Body>, Box<dyn std::error::Error + Send + Sync + 'a>>>
41 + 'a
42 + Send,
43 {
44 let signature_verifier: Arc<SlackEventSignatureVerifier> = Arc::new(
45 SlackEventSignatureVerifier::new(&config.events_signing_secret),
46 );
47 let client = self.environment.client.clone();
48 let error_handler = self.environment.error_handler.clone();
49 let user_state_storage = self.environment.user_state.clone();
50
51 move |req: Request<Body>, chain: D| {
52 let cfg = config.clone();
53 let sign_verifier = signature_verifier.clone();
54 let sc = client.clone();
55 let thread_error_handler = error_handler.clone();
56 let thread_user_state_storage = user_state_storage.clone();
57
58 async move {
59 match (req.method(), req.uri().path()) {
60 (&Method::POST, url) if url == cfg.events_path => {
61 SlackClientHyperConnector::<H>::decode_signed_response(req, &sign_verifier)
62 .map_ok(|body| {
63 let body_params: HashMap<String, String> =
64 url::form_urlencoded::parse(body.as_bytes())
65 .into_owned()
66 .collect();
67
68 match (
69 body_params.get("team_id"),
70 body_params.get("channel_id"),
71 body_params.get("user_id"),
72 body_params.get("command"),
73 body_params.get("text"),
74 body_params.get("response_url"),
75 body_params.get("trigger_id"),
76 ) {
77 (
78 Some(team_id),
79 Some(channel_id),
80 Some(user_id),
81 Some(command),
82 text,
83 Some(response_url),
84 Some(trigger_id),
85 ) => Ok(SlackCommandEvent::new(
86 team_id.into(),
87 channel_id.into(),
88 user_id.into(),
89 command.into(),
90 url::Url::parse(response_url)?.into(),
91 trigger_id.into(),
92 )
93 .opt_text(text.cloned())),
94 _ => Err(SlackClientError::SystemError(
95 SlackClientSystemError::new().with_message(
96 "Absent payload in the request from Slack".into(),
97 ),
98 ))
99 .map_err(|e| e.into()),
100 }
101 })
102 .and_then(|event| async move {
103 match event {
104 Ok(command_event) => {
105 match command_service_fn(
106 command_event,
107 sc.clone(),
108 thread_user_state_storage.clone(),
109 )
110 .await
111 {
112 Ok(cresp) => Response::builder()
113 .status(StatusCode::OK)
114 .header(
115 "content-type",
116 "application/json; charset=utf-8",
117 )
118 .body(serde_json::to_string(&cresp).unwrap().into())
119 .map_err(|e| e.into()),
120 Err(err) => {
121 let status_code = thread_error_handler(
122 err,
123 sc.clone(),
124 thread_user_state_storage.clone(),
125 );
126 Response::builder()
127 .status(status_code)
128 .body(Body::empty())
129 .map_err(|e| e.into())
130 }
131 }
132 }
133 Err(command_event_err) => {
134 let status_code = thread_error_handler(
135 command_event_err,
136 sc,
137 thread_user_state_storage,
138 );
139 Response::builder()
140 .status(status_code)
141 .body(Body::empty())
142 .map_err(|e| e.into())
143 }
144 }
145 })
146 .await
147 }
148 _ => chain(req).await,
149 }
150 }
151 .boxed()
152 }
153 }
154}