This content originally appeared on Level Up Coding - Medium and was authored by Itsuki
Being able to react to Slack messages in real time is the start of infinite (maybe over-exaggerating) possibilities!
For example
- You can run the message through a GenerativeAI with some Function Calls to perform specific task
- You can forward it to other communication tools of your choices
And many more!
And Slack Event API is the key to this!
In this article, we will be building the following, using APIGateway + Lambda to respond to our Slack events.
Event API Overview
In order for us to start writing our Lambda code, let’s first take a look at what Event API is and how it works.
The Events API is a streamlined way to build apps and bots that respond to activities in Slack.
First of all, we need a Slack App to get started with Events APIs and Event Subscriptions.
After that, we will be registering our endpoint URL for the subscription. In order for us to be able to complete this registration, we will need to respond to a URL verification handshake.
This is challenge request from Slack so that it can confirm that events are being delivered to a server under our direct control.
We will be going into more details about how we can respond to this in this next section where we build our Lambda.
After verification, we will be choosing the events we want to subscribe to and install the App to our channel.
Getting the basic Approach? Let’s get started!
API Gateway & Lambda
I will be using Rust here to build our Lambda and deploy everything with CDK. If you need a more detailed guide on getting started with Rust Lambda, API Gateway and CDK, please feel free to check out one of my previous articles here.
Handle HandShake Request
Let’s start with the handshake.
The request will have the following structure.
{
"token": "Jhj5dZrVaK7ZwHHjRyZWjbDl",
"challenge": "3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P",
"type": "url_verification"
}
- token: This deprecated verification token is proof that the request is coming from Slack on behalf of your application. You'll find this value in the App Credentials section of your app's application management interface. Verifying this value is more important when working with real events after this verification sequence has been completed. When responding to real events, always use the more secure signing secret process to verify Slack requests' authenticity.
- challenge: a randomly generated string produced by Slack. The point of this string is that you'll respond to this request with a response body containing this value.
- type: this payload is similarly formatted to other event types you'll encounter in the Events API. To help you differentiate URL verification requests from other event types, we inform you that this is of the url_verification variety.
To respond to it, all we have to do is to echo back the challenge.
Keep that in mind, let’s create our webhook handler Lambda.
use axum::extract::State;
use axum::http::header::CONTENT_TYPE;
use axum::http::{HeaderMap, StatusCode};
use axum::response::{IntoResponse, Response};
use axum::response::Json;
use serde_json::{json, Value};
#[tokio::main]
async fn main() -> Result<(), Error> {
set_var("AWS_LAMBDA_HTTP_IGNORE_STAGE_IN_PATH", "true");
tracing::init_default_subscriber();
let app = Router::new()
.route("/", post(post(webhook_received)));
run(app).await
}
pub async fn webhook_received(
Json(params): Json<Value>
) -> Response {
println!("params: {}", params);
let challenge_request = serde_json::from_value::<EventChallengeRequest>(params.clone());
if challenge_request.is_ok() {
let challenge_request = challenge_request.unwrap();
let verification_result = verify_challenge(&challenge_request);
if verification_result.is_err() || verification_result.unwrap() == false {
return build_error_response("Error Verifying.");
} else {
let response_body = json!({
"challenge": challenge_request.challenge
});
return build_success_response(&response_body);
}
}
return build_success_response(&json!({}));
}
fn verify_challenge(challenge_request: &EventChallengeRequest) -> Result<bool> {
let verification_token = std::env::var(SLACK_VERIFICATION_TOKEN)?;
Ok(verification_token == challenge_request.token && challenge_request.r#type == VERIFICATION_TYPE)
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct EventChallengeRequest {
pub challenge: String,
pub token: String,
pub r#type: String
}
const VERIFICATION_TYPE: &str = "url_verification";
fn build_error_response(message: &str) -> Response {
let mut json_header = HeaderMap::new();
json_header.insert(CONTENT_TYPE, "application/json".parse().unwrap());
let mut response = Response::new(json!({
"success": false,
"message": message
}).to_string());
*response.status_mut() = StatusCode::BAD_REQUEST;
return (json_header, response).into_response();
}
fn build_success_response(body: &Value) -> Response {
let mut json_header = HeaderMap::new();
json_header.insert(CONTENT_TYPE, "application/json".parse().unwrap());
let response = Response::new(body.to_string());
return (json_header, response).into_response();
}
I am passing in the token as an environment variable SLACK_VERIFICATION_TOKEN here.
Also, I have responded to the challenge and sent my echo as application/json here, but you can also use text/plain or application/x-www-form-urlencoded.
Regular Events
Handling regular events, the events you will be subscribing to in couple seconds, will actually depend on what you want to do with those so all I have here is just simply printing those out.
However, there is indeed one REALLY IMPORTANT point I would like to share with you here on handling regular events.
Those events will be resent for one of the following cases.
- http_timeout: responding time greater than 3 seconds
- too_many_redirects: more than 2 HTTP redirects
- connection_failed: failed to connect to the server
- ssl_error: SSL Error
- http_error: Any response with a StatusCode other than 2xx
- unknown_error: It is unknown…
The biggest point to consider if you are using Lambda is probably http_timeout and http_error.
That is always respond to it immediately with success!
If your processing requires more time, consider send it to an SQS first and process it from there!
CDK
I hope you are familiar enough with CDK so I will just be sharing the Stack with you here.
export class EmotionHandlerStack extends Stack {
restApi: LambdaRestApi
constructor(scope: Construct, id: string, props: HandlerStackProps) {
super(scope, id, props);
const apigatewayLambda = new RustFunction(this, 'EmotionAPIGatewayLambda', {
// Path to the root directory.
manifestPath: join(__dirname, '..', '..', 'lambdas/receive_handler/'),
environment: {
"SLACK_VERIFICATION_TOKEN": "your_token_placeholder"
},
timeout: Duration.minutes(5),
memorySize: 10000,
ephemeralStorageSize: Size.gibibytes(1)
});
const restApi = new LambdaRestApi(this, 'EmotionAPIGateway', {
handler: apigatewayLambda,
endpointTypes: [EndpointType.REGIONAL],
});
this.restApi = restApi
}
}
The manifestPath should be pointing to the directory containing Cargo.toml. You might need to modify this a little bit based on your directory structure.
Wait!
Don’t Run cdk deploy yet!
We have not get our actual token yet to replace your_token_placeholder.
Slack
Let’s head to Slack and make some configurations.
Create Slack Workspace
I bet you already have your Workspace created but if not, follow the steps below.
- Head to https://slack.com/get-started#/createnew.
- Enter your email address and click Continue, or continue with Apple or Google.
- Check your email for a confirmation code.
- Enter your code, then click Create a Workspace and follow the prompts.
Create Slack App
Let’s head to Apps Console and click on Create an App.
Choose From scratch.
Enter a name and choose the workspace you would like to subscribe the events. Choose Create App.
Upon success, you will lead to the Basic Information page of the newly created app.
Grab your Verification Token, replace your_token_placeholder with it and run cdk deploy!
After success, memo down the API Gateway url and let’s head to event subscriptions.
Event Subscriptions
On the exact same App page, scroll down and find Event Subscriptions under Features.
Click on it!
For Request URL, enter the API Gateway URL.
And once URL verification is complete, we will get that little verified green check mark.
Now, let’s choose the Events we want to subscribe to.
This is totally up to your needs but I have message.channels, message.im, and message.mpim added for both Subscribe to bot events as well as Subscribe to events on behalf of users. This will allow us to be notified on messages send to channels as well as DM, including responding to threads.
Save Changes! (I totally forgot about it when I did it and have to repeat all I have above again!)
Install App
We are ready to install our app to the workspace!
I have read about some online articles saying that we have to specify Scopes under OAuth & Permissions for the events we would like to receive.
NO!
You don’t!
Scopes needed are added automatically when we add event subscriptions!
Choose Install App!
Note that every time you change your event subscriptions, you will have to reinstall it!
Test it out!
Since I am only printing out the regular events, I will be checking the result directly in CloudWatch. Let’s send couple messages and wait for it to deliver!
You should see something like following!
params: {
"type": "event_callback",
"token": "XXYYZZ",
"team_id": "T123ABC456",
"api_app_id": "A123ABC456",
"event": {
"type": "name_of_event",
"event_ts": "1234567890.123456",
"user": "U123ABC456",
...
},
"event_context": "EC123ABC456",
"event_id": "Ev123ABC456",
"event_time": 1234567890,
"authorizations": [
{
"enterprise_id": "E123ABC456",
"team_id": "T123ABC456",
"user_id": "U123ABC456",
"is_bot": false,
"is_enterprise_install": false,
}
],
"is_ext_shared_channel": false,
"context_team_id": "T123ABC456",
"context_enterprise_id": null
}
Thank you for reading!
That’s all I have for today!
Happy subscribing!
Trigger Lambda From Slack Messages with Slack Events API was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.
This content originally appeared on Level Up Coding - Medium and was authored by Itsuki
Itsuki | Sciencx (2024-10-19T13:15:01+00:00) Trigger Lambda From Slack Messages with Slack Events API. Retrieved from https://www.scien.cx/2024/10/19/trigger-lambda-from-slack-messages-with-slack-events-api/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.