Skip to content

Instantly share code, notes, and snippets.

@k2wanko
Created July 15, 2019 12:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save k2wanko/4df5501da581b7370d148e6a7429ccdd to your computer and use it in GitHub Desktop.
Save k2wanko/4df5501da581b7370d148e6a7429ccdd to your computer and use it in GitHub Desktop.
import {
Receiver,
ReceiverEvent,
ReceiverAckTimeoutError,
ExpressReceiverOptions,
} from '@slack/bolt'
import { ConsoleLogger } from '@slack/logger'
import { verifySignatureAndParseBody } from '@slack/bolt/dist/ExpressReceiver'
import express, { Application, RequestHandler, Request, Response, NextFunction } from 'express'
import axios from 'axios'
export class HTTPFunctionsReceiver implements Receiver {
public app: Application
private listenerMap = new Map<string | symbol, Array<Function> | undefined>()
constructor({
signingSecret = '',
logger = new ConsoleLogger(),
endpoints = { events: '/slack/events' }
}: ExpressReceiverOptions) {
this.app = express()
this.app.use((err: any, req: Request, res: Response, next: NextFunction) => {
this.errorHandler(err, req, res, next)
.catch(err => this.emit('error', err))
})
const expressMiddleware: RequestHandler[] = [
verifySignatureAndParseBody(logger, signingSecret),
respondToSslCheck,
respondToUrlVerification,
(req: Request, res: Response, next: NextFunction) => {
this.requestHandler(req, res)
.then(() => {
next() })
.catch(next)
}
]
const endpointList: string[] = typeof endpoints === 'string' ? [endpoints] : Object.values(endpoints);
for (const endpoint of endpointList) {
this.app.post(endpoint, ...expressMiddleware);
}
}
private async requestHandler(req: Request, res: Response) {
const event: ReceiverEvent = {
body: req.body as { [key: string]: any },
ack: (response: any): void => {
if (!response) res.send('')
if (typeof response === 'string') {
res.send(response)
} else {
res.json(response)
}
},
respond: undefined,
}
if (req.body && req.body.response_url) {
event.respond = (response): void => {
axios.post(req.body.response_url, response)
.catch((e) => {
return this.emit('error', e)
})
}
}
await this.emit('message', event)
}
on(event: "message", listener: (event: ReceiverEvent) => void): unknown; on(event: "error", listener: (error: Error | ReceiverAckTimeoutError) => void): unknown
on(event: any, listener: any) {
const v = this.listenerMap.get(event)
if (!v) {
this.listenerMap.set(event, [listener])
return
}
v.push(listener)
}
start(...args: any[]): Promise<unknown> {
throw new Error("Method not implemented.")
}
stop(...args: any[]): Promise<unknown> {
throw new Error("Method not implemented.")
}
private async errorHandler(err: any, _req: Request, _res: Response, next: NextFunction) {
await this.emit('error', err)
next(err)
}
listeners(event: string | symbol): Array<Function> {
const v = this.listenerMap.get(event)
if (!v) {
return []
}
return v
}
emit(event: string | symbol, ...args: any[]): Promise<any[]> {
return Promise.all(
this.listeners(event)
.map(listener => listener(...args))
)
}
}
const respondToSslCheck: RequestHandler = (req, res, next) => {
if (req.body && req.body.ssl_check) {
res.send()
return
}
next()
}
const respondToUrlVerification: RequestHandler = (req, res, next) => {
if (req.body && req.body.type && req.body.type === 'url_verification') {
res.json({ challenge: req.body.challenge })
return
}
next()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment