https://github.com/seratch/seratch-slack-app-toolkit
A toolkit to build Slack Apps in TypeScript
https://github.com/seratch/seratch-slack-app-toolkit
npm-module npm-package slack-api slack-bot typescript
Last synced: 16 days ago
JSON representation
A toolkit to build Slack Apps in TypeScript
- Host: GitHub
- URL: https://github.com/seratch/seratch-slack-app-toolkit
- Owner: seratch
- License: mit
- Created: 2019-05-01T05:53:56.000Z (almost 6 years ago)
- Default Branch: master
- Last Pushed: 2019-05-01T09:49:21.000Z (almost 6 years ago)
- Last Synced: 2025-03-24T04:22:10.602Z (about 1 month ago)
- Topics: npm-module, npm-package, slack-api, slack-bot, typescript
- Language: TypeScript
- Size: 18.6 KB
- Stars: 4
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.txt
Awesome Lists containing this project
README
# seratch-slack-app-toolkit
This module is a useful toolkit to build Slack Apps in TypeScript. It depends on:
* seratch-slack-types
* express# Code Examples
## Express App
The following is a minimum example which runs on both AWS Lambda and Google Cloud Functions.
### app.ts
```typescript
//
// A TypeScript implementation of https://github.com/slackapi/reacjilator
//
// Author: Kazuhiro Sera @seratch
// MIT License as with the original code
//// express
import * as express from 'express';
import { Express, Request, Response } from 'express';
import * as bodyParser from 'body-parser';// Slack
import * as Slack from '@slack/web-api';import * as SlackWebApi from 'seratch-slack-types/web-api';
type ConversationsReplies = SlackWebApi.ConversationsRepliesResponse;
type ChatPostMessage = SlackWebApi.ChatPostMessageResponse;import * as SlackEventsApi from 'seratch-slack-types/events-api';
type ReactionAdded = SlackEventsApi.ReactionAddedPayload;import * as SlackAppToolkit from 'seratch-slack-app-toolkit';
const Op = SlackAppToolkit.EventsApi.EventsApiOperation;
type OpArgs = SlackAppToolkit.EventsApi.EventsApiOperationArgs;export const slackApi = new Slack.WebClient(process.env.SLACK_API_TOKEN);
const slackEventsOperator = new SlackAppToolkit.EventsApi.EventsApiOperator();// Express app
export const app: Express = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));// Google Translate API
import { Translate as GoogleTranslateApi } from '@google-cloud/translate';
// https://console.cloud.google.com/apis/api/translate.googleapis.com/credentials?project={Project ID}
// $ export GOOGLE_PROJECT_ID={Project ID}
// $ export GOOGLE_KEY={API Key}
const googleApiCredentials = {
projectId: process.env.GOOGLE_PROJECT_ID,
key: process.env.GOOGLE_KEY
}
const googleApi: GoogleTranslateApi = new GoogleTranslateApi(googleApiCredentials);// Enable debug logging if true
const debug: boolean = true;
// lang code mapping data
import { langcode } from './langcode';slackEventsOperator.add('reaction_added', new Op(
function (args: OpArgs) {
const payload = args.payload;
const res = args.response;
if (debug) {
console.log(payload.event);
}
if (payload.event.item.type !== 'message') {
// Skip any events apart from reactions put on messages
return res.status(200);
}
const reactionName = payload.event.reaction;
let country: string = null;
// Check the reaction name if it is a country flag
if (reactionName.match(/flag-/)) { // when the name has flag- prefix
country = reactionName.match(/(?!flag-\b)\b\w+/)[0];
} else { // jp, fr, etc.
const flags = Object.keys(langcode.All); // array
if (flags.includes(reactionName)) {
country = reactionName;
} else {
return res.status(200);
}
}
// Finding a lang based on a country is not the best way but oh well
// Matching ISO 639-1 language code
const lang: string = langcode.All[country];
if (!lang) {
return res.status(200);
}
if (debug) {
console.log(`Detected country: ${country}, lang: ${lang} from reaction: ${reactionName}`);
}const channelId: string = payload.event.item.channel;
const messageTs: string = payload.event.item.ts;// Fetch all the messages in the thread
slackApi.conversations.replies({
channel: channelId,
ts: messageTs,
inclusive: true
}) // The returned value is a Promise - chaining operations started here
.then((repliesRes: ConversationsReplies) => {
if (debug) {
console.log(repliesRes.messages);
}
const messages = repliesRes.messages;
const message = messages[0];
if (message.text) {
// Call Google Translate API to get a translated text
googleApi.translate(message.text, lang)
.then((array) => {
const [translatedText, googleApiRes] = array; // [string, r.Response]
if (debug) {
console.log(`Response from Google Translate API: ${JSON.stringify(googleApiRes)}`);
}// To avoid posting same messages several times, make sure if a same message in the thread doesn't exist
let alreadyPosted: boolean = false;
messages.forEach(messageInTheThread => {
if (!alreadyPosted && messageInTheThread.text && messageInTheThread.text === translatedText) {
alreadyPosted = true;
}
});
if (alreadyPosted) {
return;
}// Post the translated text as a following message in the thread
slackApi.chat.postMessage({
channel: channelId,
text: translatedText,
as_user: false,
username: "Reacjilator Bot",
thread_ts: message.thread_ts ? message.thread_ts : message.ts
})
.then((postRes: ChatPostMessage) => {
if (postRes.ok) {
console.log(`Successfully posted a translated message (ts: ${postRes.ts})`);
} else {
if (debug) {
console.error(postRes);
}
console.error(`Got an error from chat.postMessage (error: ${postRes.error})`);
}
})
.catch(reason => {
console.error(`Failed to post a message because ${reason}`);
})})
.catch(reason => {
console.error(`Failed to call Google Translate API because ${reason}`);
})} else {
console.log(`Skipped the message because it doesn't have text property (ts: ${message.ts})`);
}
})
.catch(reason => {
console.error(`Failed to fetch message replies because ${reason}`);
});// Return 200 OK right away
return res.status(200).json({ ok: true });
}
));app.post('/events', function (req: Request, res: Response) {
slackEventsOperator.dispatch(req.body, req, res);
});
```### handler.ts
```typescript
import 'source-map-support/register';
import { app } from './app';
export const dispatcher = require('serverless-http')(app);
```### serverless.yml
```yaml
service:
name: awesome-app
plugins:
- serverless-offline
- serverless-webpack
provider:
name: aws
runtime: nodejs8.10
functions:
dispatcher:
handler: handler.dispatcher
events:
- http:
method: post
path: /events
```