diff options
| author | ha-dai <contact@haradai.net> | 2018-05-04 02:49:46 +0900 |
|---|---|---|
| committer | ha-dai <contact@haradai.net> | 2018-05-04 02:49:46 +0900 |
| commit | f850283147072c681df1b39c57f8bd0b14f18016 (patch) | |
| tree | 63ff533c91097da2d8ca2070fc67a28f67ee33da /src/server/api/bot/interfaces | |
| parent | Merge branch 'master' of github.com:syuilo/misskey (diff) | |
| parent | 1.7.0 (diff) | |
| download | misskey-f850283147072c681df1b39c57f8bd0b14f18016.tar.gz misskey-f850283147072c681df1b39c57f8bd0b14f18016.tar.bz2 misskey-f850283147072c681df1b39c57f8bd0b14f18016.zip | |
Merge branch 'master' of github.com:syuilo/misskey
Diffstat (limited to 'src/server/api/bot/interfaces')
| -rw-r--r-- | src/server/api/bot/interfaces/line.ts | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/src/server/api/bot/interfaces/line.ts b/src/server/api/bot/interfaces/line.ts new file mode 100644 index 0000000000..733315391d --- /dev/null +++ b/src/server/api/bot/interfaces/line.ts @@ -0,0 +1,238 @@ +import * as EventEmitter from 'events'; +import * as Router from 'koa-router'; +import * as request from 'request'; +import * as crypto from 'crypto'; +import User from '../../../../models/user'; +import config from '../../../../config'; +import BotCore from '../core'; +import _redis from '../../../../db/redis'; +import prominence = require('prominence'); +import getAcct from '../../../../acct/render'; +import parseAcct from '../../../../acct/parse'; +import getNoteSummary from '../../../../renderers/get-note-summary'; +import getUserName from '../../../../renderers/get-user-name'; + +const redis = prominence(_redis); + +// SEE: https://developers.line.me/media/messaging-api/messages/sticker_list.pdf +const stickers = [ + '297', + '298', + '299', + '300', + '301', + '302', + '303', + '304', + '305', + '306', + '307' +]; + +class LineBot extends BotCore { + private replyToken: string; + + private reply(messages: any[]) { + request.post({ + url: 'https://api.line.me/v2/bot/message/reply', + headers: { + 'Authorization': `Bearer ${config.line_bot.channel_access_token}` + }, + json: { + replyToken: this.replyToken, + messages: messages + } + }, (err, res, body) => { + if (err) { + console.error(err); + return; + } + }); + } + + public async react(ev: any): Promise<void> { + this.replyToken = ev.replyToken; + + switch (ev.type) { + // メッセージ + case 'message': + switch (ev.message.type) { + // テキスト + case 'text': + const res = await this.q(ev.message.text); + if (res == null) return; + // 返信 + this.reply([{ + type: 'text', + text: res + }]); + break; + + // スタンプ + case 'sticker': + // スタンプで返信 + this.reply([{ + type: 'sticker', + packageId: '4', + stickerId: stickers[Math.floor(Math.random() * stickers.length)] + }]); + break; + } + break; + + // noteback + case 'noteback': + const data = ev.noteback.data; + const cmd = data.split('|')[0]; + const arg = data.split('|')[1]; + switch (cmd) { + case 'showtl': + this.showUserTimelineNoteback(arg); + break; + } + break; + } + } + + public static import(data) { + const bot = new LineBot(); + bot._import(data); + return bot; + } + + public async showUserCommand(q: string) { + const user = await require('../../endpoints/users/show')(parseAcct(q.substr(1)), this.user); + + const acct = getAcct(user); + const actions = []; + + actions.push({ + type: 'noteback', + label: 'タイムラインを見る', + data: `showtl|${user.id}` + }); + + if (user.twitter) { + actions.push({ + type: 'uri', + label: 'Twitterアカウントを見る', + uri: `https://twitter.com/${user.twitter.screenName}` + }); + } + + actions.push({ + type: 'uri', + label: 'Webで見る', + uri: `${config.url}/@${acct}` + }); + + this.reply([{ + type: 'template', + altText: await super.showUserCommand(q), + template: { + type: 'buttons', + thumbnailImageUrl: `${user.avatarUrl}?thumbnail&size=1024`, + title: `${getUserName(user)} (@${acct})`, + text: user.description || '(no description)', + actions: actions + } + }]); + + return null; + } + + public async showUserTimelineNoteback(userId: string) { + const tl = await require('../../endpoints/users/notes')({ + userId: userId, + limit: 5 + }, this.user); + + const text = `${getUserName(tl[0].user)}さんのタイムラインはこちらです:\n\n` + tl + .map(note => getNoteSummary(note)) + .join('\n-----\n'); + + this.reply([{ + type: 'text', + text: text + }]); + } +} + +const handler = new EventEmitter(); + +handler.on('event', async (ev) => { + + const sourceId = ev.source.userId; + const sessionId = `line-bot-sessions:${sourceId}`; + + const session = await redis.get(sessionId); + let bot: LineBot; + + if (session == null) { + const user = await User.findOne({ + host: null, + 'line': { + userId: sourceId + } + }); + + bot = new LineBot(user); + + bot.on('signin', user => { + User.update(user._id, { + $set: { + 'line': { + userId: sourceId + } + } + }); + }); + + bot.on('signout', user => { + User.update(user._id, { + $set: { + 'line': { + userId: null + } + } + }); + }); + + redis.set(sessionId, JSON.stringify(bot.export())); + } else { + bot = LineBot.import(JSON.parse(session)); + } + + bot.on('updated', () => { + redis.set(sessionId, JSON.stringify(bot.export())); + }); + + if (session != null) bot.refreshUser(); + + bot.react(ev); +}); + +// Init router +const router = new Router(); + +if (config.line_bot) { + router.post('/hooks/line', ctx => { + const sig1 = ctx.headers['x-line-signature']; + + const hash = crypto.createHmac('SHA256', config.line_bot.channel_secret) + .update(ctx.request.rawBody); + + const sig2 = hash.digest('base64'); + + // シグネチャ比較 + if (sig1 === sig2) { + ctx.request.body.events.forEach(ev => { + handler.emit('event', ev); + }); + } else { + ctx.status = 400; + } + }); +} + +module.exports = router; |