slack_morphism_hyper/listener/
command_events.rs

1use 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}