{"id":16061086,"url":"https://github.com/amancevice/slackend","last_synced_at":"2025-03-18T05:30:31.192Z","repository":{"id":53017143,"uuid":"152242515","full_name":"amancevice/slackend","owner":"amancevice","description":"Simple asynchronous back end for Slack apps","archived":false,"fork":false,"pushed_at":"2023-01-12T15:17:38.000Z","size":1933,"stargazers_count":12,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-16T17:11:49.785Z","etag":null,"topics":["lambda","nodejs","serverless","slack"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/amancevice.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-10-09T11:52:37.000Z","updated_at":"2023-01-12T15:17:43.000Z","dependencies_parsed_at":"2023-02-09T12:31:48.072Z","dependency_job_id":null,"html_url":"https://github.com/amancevice/slackend","commit_stats":null,"previous_names":[],"tags_count":50,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amancevice%2Fslackend","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amancevice%2Fslackend/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amancevice%2Fslackend/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amancevice%2Fslackend/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/amancevice","download_url":"https://codeload.github.com/amancevice/slackend/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244164520,"owners_count":20408947,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["lambda","nodejs","serverless","slack"],"created_at":"2024-10-09T04:07:49.146Z","updated_at":"2025-03-18T05:30:30.307Z","avatar_url":"https://github.com/amancevice.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Asynchronous Slackbot\n\n![npm](https://img.shields.io/npm/v/slackend?style=flat-square)\n[![test](https://img.shields.io/github/actions/workflow/status/amancevice/slackend/test.yml?logo=github\u0026style=flat-square)](https://github.com/amancevice/slackend/actions/workflows/test.yml)\n[![coverage](https://img.shields.io/codeclimate/coverage/amancevice/slackend?logo=code-climate\u0026style=flat-square)](https://codeclimate.com/github/amancevice/slackend/test_coverage)\n[![maintainability](https://img.shields.io/codeclimate/maintainability/amancevice/slackend?logo=code-climate\u0026style=flat-square)](https://codeclimate.com/github/amancevice/slackend/maintainability)\n\nA simple, asynchronous back end for your Slack app.\n\nThe app intentionally does very little: it is essentially middleware for [ExpressJS](https://expressjs.com) that accepts an incoming request, verifies its origin, and passes the request to a user-provided callback, where the payload is sent to a queue/trigger for asynchronous processing.\n\nEndpoints are provided for:\n\n- `GET /health` check to verify the service is running\n- `GET /install` begin the process of installing your app to a workspace\n- `GET /oauth` completes the [OAuth2](https://api.slack.com/docs/oauth) workflow\n- `GET /oauth/v2` completes the [OAuth2](https://api.slack.com/docs/oauth) workflow (v2)\n- `POST /callbacks` publishes [interactive messages](https://api.slack.com/interactive-messages)\n- `POST /events` publishes events from the [Events API](https://api.slack.com/events-api)\n- `POST /slash/:cmd` publishes [slash commands](https://api.slack.com/slash-commands)\n\nIn production it is expected that users will attach their own publishing functions to connect to a messaging service like [Amazon EventBridge](https://aws.amazon.com/eventbridge/), or [Google Pub/Sub](https://cloud.google.com/pubsub/docs/).\n\n## Advantages\n\n- Separates the concerns of responding to incoming requests and the logic to handle them.\n  - Handlers can be added/removed independently of this app; deploy once and forget.\n  - Requests can be published to any platform.\n  - Handlers can be written in any language supported by the topic trigger.\n- Designed to work within serverless frameworks, such as [AWS Lambda](https://aws.amazon.com/lambda/) or [Google Cloud Functions](https://cloud.google.com/functions/docs/).\n- Authenticates requests using Slack's [signing secrets](https://api.slack.com/docs/verifying-requests-from-slack) so you'll know that events published to internal triggers/queues are verified.\n\n## Drawbacks\n\n- Slack has a strict 3-second lifetime for many API operations, so it is critical that your asynchronous tasks complete quickly. Cold start times of some serverless computing platforms may be prohibitively slow. (_Note: this concern can be effectively eliminated on most platforms by configuring your serverless functions for speed_)\n\n## Processing Events\n\nIn very simple terms, all events are processed by transforming the request payload to a JSON object and assigning it to the express local `res.locals.slack`.\n\nIt is left to the user to handle further transformation and publishing of the event, however the [`aws`](./aws.js) module provides an implementation that publishes the events to EventBridge.\n\nHere is an example configuration that simply responds to incoming requests with the processed payload:\n\n```javascript\nconst express = require(\"express\");\nconst slackend = require(\"slackend\");\nconst app = express();\napp.use(slackend(), (req, res) =\u003e res.json(res.locals.slack));\napp.listen(3000);\n```\n\n## Serverless Deployment\n\n![AWS](./docs/aws.png?)\n\nDeploying a version of this app to Amazon Web Services (AWS) serverless offerings might take the above shape, where incoming requests from Slack to your app are handled as follows:\n\n**API Gateway** receives and routes all requests to a single **Lambda function** integration.\n\nOn cold starts, the **Lambda function** pulls its Slack tokens/secrets from its encrypted **SecretsManager** secret, starts a proxy express server, and publishes the request to an **EventBridge** bus.\n\nOn warm starts the environment and server are cached and the request is published to **EventBridge** without needing to re-fetch the app secrets.\n\nOnce the request is published, the API sends a `204 NO CONTENT` response back to Slack.\n\nUsing this method, each feature of your app can be added one-by-one independently of the API and is highly scalable.\n\n## NodeJS Usage\n\nAt its core, `slackend` is middleware for [ExpressJS](https://expressjs.com) with several routes predefined for handling Slack messages. None of the routes are configured to respond to the request. This is done deliberately so users can customize the behavior of the app.\n\nThe Slack message and an inferred topic name are stored in the `res.locals` object and can be used to publish the request to your preferred messaging/queueing service.\n\nHere is an example usage that simply logs the request to the console:\n\n```javascript\nconst slackend = require(\"slackend\");\n\n// Create express app\nconst app = slackend({\n  client_id: process.env.SLACK_CLIENT_ID,\n  client_secret: process.env.SLACK_CLIENT_SECRET,\n  oauth_error_uri: process.env.SLACK_OAUTH_ERROR_URI,\n  oauth_redirect_uri: process.env.SLACK_OAUTH_REDIRECT_URI,\n  oauth_success_uri: process.env.SLACK_OAUTH_SUCCESS_URI,\n  signing_secret: process.env.SLACK_SIGNING_SECRET,\n  signing_version: process.env.SLACK_SIGNING_VERSION,\n  token: process.env.SLACK_TOKEN,\n});\n\n// You *must* add a callback that responds to the request\napp.use((req, res) =\u003e {\n  console.log(res.locals);\n  res.json({ ok: true });\n});\n```\n\n_WARNING \u0026mdash; All of the configuration options to `slackend()` are optional, but omitting the `signing_secret` will disable the verification step where received requests are confirmed as originating from Slack. Disabling verification can also be done by setting the environmental variable `DISABLE_VERIFICATION=1`._\n\n## Local Development\n\nRun a local instance of your slack app by cloning this repository, configuring settings, installing dependencies, and starting the express server.\n\nConfigure settings by copying [`.env.example`](./.env.example) to `.env` and adding your keys/settings.\n\n```bash\ncp .env.example .env\n```\n\nInstall dependencies using `npm` or `docker-compose`:\n\n```bash\nnpm install\n```\n\nStart the server:\n\n```bash\nnpm start\n```\n\nSend a sample request:\n\n```bash\n# Callback\ncurl --request POST \\\n  --data 'payload=%7B%22callback_id%22%3A%22fizz%22%7D' \\\n  --url 'http://localhost:3000/callbacks'\n\n# Event\ncurl --request POST \\\n  --header 'Content-Type: application/json' \\\n  --data '{\"type\": \"event_callback\", \"event\": {\"type\": \"team_join\"}}' \\\n  --url 'http://localhost:3000/events'\n\n# Slash command\ncurl --request POST \\\n  --data 'fizz=buzz' \\\n  --url 'http://localhost:3000/slash/fizz'\n```\n\n## AWS\n\nA module is provided to deploy to Lambda using SecretsManager to store the Slack secrets.\n\nExample Lambda handler:\n\n```javascript\nconst slackend = require(\"slackend/aws\");\nmodule.exports = slackend();\n```\n\n## Deploy with Terraform\n\nDeploy directly to AWS using [`terraform`](https://terraform.io) and the [`slackbot`](https://github.com/amancevice/terraform-aws-slackbot) + [`slackbot-secrets`](https://github.com/amancevice/terraform-aws-slackbot-secrets) modules:\n\n```terraform\nresource \"aws_apigatewayv2_api\" \"slackbot_api\" {\n  name          = \"my-slack-api\"\n  protocol_type = \"HTTP\"\n  # …\n}\n\nmodule \"slackbot\" {\n  source  = \"amancevice/slackbot/aws\"\n  version = \"~\u003e 20.1\"\n\n  http_api_execution_arn = aws_apigatewayv2_api.http_api.execution_arn\n  http_api_id            = aws_apigatewayv2_api.http_api.id\n  lambda_function_name   = \"my-function-name\"\n  role_name              = \"my-role-name\"\n  secret_name            = module.slackbot_secrets.secret.name\n  topic_name             = \"my-topic-name\"\n\n  # …\n}\n\nmodule \"slackbot_secrets\" {\n  source               = \"amancevice/slackbot-secrets/aws\"\n  kms_key_alias        = \"alias/slack/your-kms-key-alias\"\n  secret_name          = \"slack/your-secret-name\"\n  slack_bot_token      = \"your-bot-token\"\n  slack_client_id      = \"your-client-id\"\n  slack_client_secret  = \"your-client-secret\"\n  slack_signing_secret = \"your-signing-secret\"\n  slack_user_token     = \"your-user-token\"\n\n  // Optional additional secrets\n  secrets = { FIZZ = \"buzz\" }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famancevice%2Fslackend","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Famancevice%2Fslackend","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famancevice%2Fslackend/lists"}