import * as EventEmitter from 'events'; import * as express from 'express'; import * as request from 'request'; import * as crypto from 'crypto'; import User from '../../models/user'; import config from '../../../conf'; import BotCore from '../core'; import _redis from '../../../db/redis'; import prominence = require('prominence'); const redis = prominence(_redis); module.exports = async (app: express.Application) => { if (config.line_bot == null) return; const handler = new EventEmitter(); handler.on('message', async (ev) => { // テキスト以外(スタンプなど)は無視 if (ev.message.type !== 'text') return; const sourceId = ev.source.userId; const sessionId = `line-bot-sessions:${sourceId}`; const _session = await redis.get(sessionId); let session: BotCore; if (_session == null) { const user = await User.findOne({ line: { user_id: sourceId } }); session = new BotCore(user); session.on('set-user', user => { User.update(user._id, { $set: { line: { user_id: sourceId } } }); }); redis.set(sessionId, JSON.stringify(session.export())); } else { session = BotCore.import(JSON.parse(_session)); } session.on('updated', () => { redis.set(sessionId, JSON.stringify(session.export())); }); const res = await session.q(ev.message.text); // 返信 request.post({ url: 'https://api.line.me/v2/bot/message/reply', headers: { 'Authorization': `Bearer ${config.line_bot.channel_access_token}` }, json: { replyToken: ev.replyToken, messages: [{ type: 'text', text: res }] } }, (err, res, body) => { if (err) { console.error(err); return; } }); }); app.post('/hooks/line', (req, res, next) => { // req.headers['x-line-signature'] は常に string ですが、型定義の都合上 // string | string[] になっているので string を明示しています const sig1 = req.headers['x-line-signature'] as string; const hash = crypto.createHmac('SHA256', config.line_bot.channel_secret) .update((req as any).rawBody); const sig2 = hash.digest('base64'); // シグネチャ比較 if (sig1 === sig2) { req.body.events.forEach(ev => { handler.emit(ev.type, ev); }); res.sendStatus(200); } else { res.sendStatus(400); } }); };