Events API and OAuth for Hyper
The library provides routes and middleware implementation in SlackClientEventsListener
for:
- Push Events
- Interaction Events
- Command Events
- OAuth v2 redirects and client functions nested router
You can chain all of the routes using chain_service_routes_fn
from the library.
Hyper configuration
In order to use Events API/OAuth you need to configure Hyper HTTP server. There is nothing special about how to do that, and you can use the official hyper docs. This is just merely a quick example how to use it with Slack Morphism routes.
Example
use slack_morphism::prelude::*;
// Hyper imports
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Request, Response};
// For logging
use log::*;
// For convinience there is an alias SlackHyperClient as SlackClient<SlackClientHyperConnector>
async fn create_slack_events_listener_server() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080));
info!("Loading server: {}", addr);
// This is our default HTTP route when Slack routes didn't handle incoming request (different/other path).
async fn your_others_routes(
_req: Request<Body>,
) -> Result<Response<Body>, Box<dyn std::error::Error + Send + Sync>> {
Response::builder()
.body("Hey, this is a default users route handler".into())
.map_err(|e| e.into())
}
// Our error handler for Slack Events API
fn slack_listener_error_handler(err: Box<dyn std::error::Error + Send + Sync>,
_client: Arc<SlackHyperClient>,
_states: SlackClientEventsUserState) -> http::StatusCode {
error!("Slack Events error: {:#?}", err);
// Defines what we return Slack server
http::StatusCode::BAD_REQUEST
}
// We need also a client instance. `Arc` used here because we would like
// to share the the same client for all of the requests and all hyper threads
let client = Arc::new(SlackClient::new(SlackClientHyperConnector::new()?));
// In this example we're going to use all of the events handlers, but
// you don't have to.
// Our Slack OAuth handler with a token response after installation
async fn slack_oauth_install_function(
resp: SlackOAuthV2AccessTokenResponse,
_client: Arc<SlackHyperClient>,
_states: SlackClientEventsUserState
) {
println!("{:#?}", resp);
Ok(())
}
// Push events handler
async fn slack_push_events_function(event: SlackPushEvent,
_client: Arc<SlackHyperClient>,
_states: SlackClientEventsUserState
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
println!("{:#?}", event);
Ok(())
}
// Interaction events handler
async fn slack_interaction_events_function(event: SlackInteractionEvent,
_client: Arc<SlackHyperClient>,
_states: SlackClientEventsUserState
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
println!("{:#?}", event);
Ok(())
}
// Commands events handler
async fn slack_command_events_function(
event: SlackCommandEvent,
_client: Arc<SlackHyperClient>,
_states: SlackClientEventsUserState
) -> Result<SlackCommandEventResponse, Box<dyn std::error::Error + Send + Sync>> {
println!("{:#?}", event);
Ok(SlackCommandEventResponse::new(
SlackMessageContent::new().with_text("Working on it".into()),
))
}
// Now we need some configuration for our Slack listener routes.
// You can additionally configure HTTP route paths using theses configs,
// but for simplicity we will skip that part here and configure only required parameters.
let oauth_listener_config = Arc::new(SlackOAuthListenerConfig::new(
config_env_var("SLACK_CLIENT_ID")?.into(),
config_env_var("SLACK_CLIENT_SECRET")?.into(),
config_env_var("SLACK_BOT_SCOPE")?,
config_env_var("SLACK_REDIRECT_HOST")?,
));
let push_events_config = Arc::new(SlackPushEventsListenerConfig::new(
config_env_var("SLACK_SIGNING_SECRET")?.into(),
));
let interactions_events_config = Arc::new(SlackInteractionEventsListenerConfig::new(
config_env_var("SLACK_SIGNING_SECRET")?.into(),
));
let command_events_config = Arc::new(SlackCommandEventsListenerConfig::new(
config_env_var("SLACK_SIGNING_SECRET")?.into(),
));
// Creating a shared listener environment with an ability to share client and user state
let listener_environment = Arc::new(
SlackClientEventsListenerEnvironment::new(client.clone())
.with_error_handler(test_error_handler)
);
let make_svc = make_service_fn(move |_| {
// Because of threading model you have to create copies of configs.
let thread_oauth_config = oauth_listener_config.clone();
let thread_push_events_config = push_events_config.clone();
let thread_interaction_events_config = interactions_events_config.clone();
let thread_command_events_config = command_events_config.clone();
// Creating listener
let listener = SlackClientEventsHyperListener::new(listener_environment.clone());
// Chaining all of the possible routes for Slack.
// `chain_service_routes_fn` is an auxiliary function from Slack Morphism.
async move {
let routes = chain_service_routes_fn(
listener.oauth_service_fn(thread_oauth_config, test_oauth_install_function),
chain_service_routes_fn(
listener.push_events_service_fn(
thread_push_events_config,
slack_push_events_function,
),
chain_service_routes_fn(
listener.interaction_events_service_fn(
thread_interaction_events_config,
slack_interaction_events_function,
),
chain_service_routes_fn(
listener.command_events_service_fn(
thread_command_events_config,
slack_command_events_function,
),
your_others_routes,
),
),
),
);
Ok::<_, Box<dyn std::error::Error + Send + Sync>>(service_fn(routes))
}
)};
// Starting a server with listener routes
let server = hyper::server::Server::bind(&addr).serve(make_svc);
server.await.map_err(|e| {
error!("Server error: {}", e);
e.into()
})
}
Complete example look at github