{"id":16696882,"url":"https://github.com/seratch/seratch-slack-app-toolkit","last_synced_at":"2025-04-10T02:51:59.265Z","repository":{"id":139894273,"uuid":"184375047","full_name":"seratch/seratch-slack-app-toolkit","owner":"seratch","description":"A toolkit to build Slack Apps in TypeScript","archived":false,"fork":false,"pushed_at":"2019-05-01T09:49:21.000Z","size":19,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-24T04:22:10.602Z","etag":null,"topics":["npm-module","npm-package","slack-api","slack-bot","typescript"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","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/seratch.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-05-01T05:53:56.000Z","updated_at":"2022-05-21T12:26:36.000Z","dependencies_parsed_at":"2023-07-11T14:34:14.713Z","dependency_job_id":null,"html_url":"https://github.com/seratch/seratch-slack-app-toolkit","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seratch%2Fseratch-slack-app-toolkit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seratch%2Fseratch-slack-app-toolkit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seratch%2Fseratch-slack-app-toolkit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seratch%2Fseratch-slack-app-toolkit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/seratch","download_url":"https://codeload.github.com/seratch/seratch-slack-app-toolkit/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248147034,"owners_count":21055460,"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":["npm-module","npm-package","slack-api","slack-bot","typescript"],"created_at":"2024-10-12T17:45:08.985Z","updated_at":"2025-04-10T02:51:59.250Z","avatar_url":"https://github.com/seratch.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# seratch-slack-app-toolkit\n\nThis module is a useful toolkit to build Slack Apps in TypeScript. It depends on:\n\n* seratch-slack-types\n* express\n\n# Code Examples\n\n## Express App\n\nThe following is a minimum example which runs on both AWS Lambda and Google Cloud Functions.\n\n### app.ts\n\n```typescript\n//\n// A TypeScript implementation of https://github.com/slackapi/reacjilator\n//\n// Author: Kazuhiro Sera @seratch\n// MIT License as with the original code\n//\n\n// express\nimport * as express from 'express';\nimport { Express, Request, Response } from 'express';\nimport * as bodyParser from 'body-parser';\n\n// Slack\nimport * as Slack from '@slack/web-api';\n\nimport * as SlackWebApi from 'seratch-slack-types/web-api';\ntype ConversationsReplies = SlackWebApi.ConversationsRepliesResponse;\ntype ChatPostMessage = SlackWebApi.ChatPostMessageResponse;\n\nimport * as SlackEventsApi from 'seratch-slack-types/events-api';\ntype ReactionAdded = SlackEventsApi.ReactionAddedPayload;\n\nimport * as SlackAppToolkit from 'seratch-slack-app-toolkit';\nconst Op = SlackAppToolkit.EventsApi.EventsApiOperation;\ntype OpArgs\u003cA\u003e = SlackAppToolkit.EventsApi.EventsApiOperationArgs\u003cA\u003e;\n\nexport const slackApi = new Slack.WebClient(process.env.SLACK_API_TOKEN);\nconst slackEventsOperator = new SlackAppToolkit.EventsApi.EventsApiOperator();\n\n// Express app\nexport const app: Express = express();\napp.use(bodyParser.json());\napp.use(bodyParser.urlencoded({ extended: true }));\n\n// Google Translate API\nimport { Translate as GoogleTranslateApi } from '@google-cloud/translate';\n// https://console.cloud.google.com/apis/api/translate.googleapis.com/credentials?project={Project ID}\n// $ export GOOGLE_PROJECT_ID={Project ID}\n// $ export GOOGLE_KEY={API Key}\nconst googleApiCredentials = {\n  projectId: process.env.GOOGLE_PROJECT_ID,\n  key: process.env.GOOGLE_KEY\n}\nconst googleApi: GoogleTranslateApi = new GoogleTranslateApi(googleApiCredentials);\n\n// Enable debug logging if true\nconst debug: boolean = true;\n// lang code mapping data\nimport { langcode } from './langcode';\n\nslackEventsOperator.add('reaction_added', new Op\u003cReactionAdded\u003e(\n  function (args: OpArgs\u003cReactionAdded\u003e) {\n    const payload = args.payload;\n    const res = args.response;\n    if (debug) {\n      console.log(payload.event);\n    }\n    if (payload.event.item.type !== 'message') {\n      // Skip any events apart from reactions put on messages\n      return res.status(200);\n    }\n    const reactionName = payload.event.reaction;\n    let country: string = null;\n    // Check the reaction name if it is a country flag\n    if (reactionName.match(/flag-/)) { // when the name has flag- prefix\n      country = reactionName.match(/(?!flag-\\b)\\b\\w+/)[0];\n    } else { // jp, fr, etc.\n      const flags = Object.keys(langcode.All); // array\n      if (flags.includes(reactionName)) {\n        country = reactionName;\n      } else {\n        return res.status(200);\n      }\n    }\n    // Finding a lang based on a country is not the best way but oh well\n    // Matching ISO 639-1 language code\n    const lang: string = langcode.All[country];\n    if (!lang) {\n      return res.status(200);\n    }\n    if (debug) {\n      console.log(`Detected country: ${country}, lang: ${lang} from reaction: ${reactionName}`);\n    }\n\n    const channelId: string = payload.event.item.channel;\n    const messageTs: string = payload.event.item.ts;\n\n    // Fetch all the messages in the thread\n    slackApi.conversations.replies({\n      channel: channelId,\n      ts: messageTs,\n      inclusive: true\n    }) // The returned value is a Promise - chaining operations started here\n      .then((repliesRes: ConversationsReplies) =\u003e {\n        if (debug) {\n          console.log(repliesRes.messages);\n        }\n        const messages = repliesRes.messages;\n        const message = messages[0];\n        if (message.text) {\n          // Call Google Translate API to get a translated text\n          googleApi.translate(message.text, lang)\n            .then((array) =\u003e {\n              const [translatedText, googleApiRes] = array; // [string, r.Response]\n              if (debug) {\n                console.log(`Response from Google Translate API: ${JSON.stringify(googleApiRes)}`);\n              }\n\n              // To avoid posting same messages several times, make sure if a same message in the thread doesn't exist\n              let alreadyPosted: boolean = false;\n              messages.forEach(messageInTheThread =\u003e {\n                if (!alreadyPosted \u0026\u0026 messageInTheThread.text \u0026\u0026 messageInTheThread.text === translatedText) {\n                  alreadyPosted = true;\n                }\n              });\n              if (alreadyPosted) {\n                return;\n              }\n\n              // Post the translated text as a following message in the thread\n              slackApi.chat.postMessage({\n                channel: channelId,\n                text: translatedText,\n                as_user: false,\n                username: \"Reacjilator Bot\",\n                thread_ts: message.thread_ts ? message.thread_ts : message.ts\n              })\n                .then((postRes: ChatPostMessage) =\u003e {\n                  if (postRes.ok) {\n                    console.log(`Successfully posted a translated message (ts: ${postRes.ts})`);\n                  } else {\n                    if (debug) {\n                      console.error(postRes);\n                    }\n                    console.error(`Got an error from chat.postMessage (error: ${postRes.error})`);\n                  }\n                })\n                .catch(reason =\u003e {\n                  console.error(`Failed to post a message because ${reason}`);\n                })\n\n            })\n            .catch(reason =\u003e {\n              console.error(`Failed to call Google Translate API because ${reason}`);\n            })\n\n          } else {\n          console.log(`Skipped the message because it doesn't have text property (ts: ${message.ts})`);\n        }\n      })\n      .catch(reason =\u003e {\n        console.error(`Failed to fetch message replies because ${reason}`);\n      });\n\n    // Return 200 OK right away\n    return res.status(200).json({ ok: true });\n  }\n));\n\napp.post('/events', function (req: Request, res: Response) {\n  slackEventsOperator.dispatch(req.body, req, res);\n});\n```\n\n### handler.ts\n\n```typescript\nimport 'source-map-support/register';\nimport { app } from './app';\nexport const dispatcher = require('serverless-http')(app);\n```\n\n### serverless.yml\n\n```yaml\nservice:\n  name: awesome-app\nplugins:\n  - serverless-offline\n  - serverless-webpack\nprovider:\n  name: aws\n  runtime: nodejs8.10\nfunctions:\n  dispatcher:\n    handler: handler.dispatcher\n    events:\n      - http:\n          method: post\n          path: /events\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseratch%2Fseratch-slack-app-toolkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fseratch%2Fseratch-slack-app-toolkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseratch%2Fseratch-slack-app-toolkit/lists"}