From 0f2b503f2f545ce11fcca8c2c17a1084fd91df5e Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 7 Oct 2017 05:50:01 +0900
Subject: [PATCH] :v:

---
 src/api/bot/core.ts            | 52 ++++++++++++++++++++++++++++++++--
 src/api/bot/interfaces/line.ts | 38 +++++++++++++------------
 2 files changed, 70 insertions(+), 20 deletions(-)

diff --git a/src/api/bot/core.ts b/src/api/bot/core.ts
index 47989dbaa2..1f624c5f0a 100644
--- a/src/api/bot/core.ts
+++ b/src/api/bot/core.ts
@@ -14,6 +14,31 @@ export default class BotCore extends EventEmitter {
 		this.user = user;
 	}
 
+	private setContect(context: Context) {
+		this.context = context;
+		this.emit('updated');
+
+		if (context) {
+			context.on('updated', () => {
+				this.emit('updated');
+			});
+		}
+	}
+
+	public export() {
+		return {
+			user: this.user,
+			context: this.context ? this.context.export() : null
+		};
+	}
+
+	public static import(data) {
+		const core = new BotCore();
+		core.user = data.user;
+		core.setContect(data.context ? Context.import(core, data.context) : null);
+		return core;
+	}
+
 	public async q(query: string): Promise<string> {
 		if (this.context != null) {
 			return await this.context.q(query);
@@ -22,9 +47,11 @@ export default class BotCore extends EventEmitter {
 		switch (query) {
 			case 'ping':
 				return 'PONG';
+			case 'me':
+				return this.user ? `${this.user.name}としてサインインしています` : 'サインインしていません';
 			case 'ログイン':
 			case 'サインイン':
-				this.context = new SigninContext(this);
+				this.setContect(new SigninContext(this));
 				return await this.context.greet();
 			default:
 				return '?';
@@ -34,18 +61,26 @@ export default class BotCore extends EventEmitter {
 	public setUser(user: IUser) {
 		this.user = user;
 		this.emit('set-user', user);
+		this.emit('updated');
 	}
 }
 
-abstract class Context {
+abstract class Context extends EventEmitter {
 	protected core: BotCore;
 
 	public abstract async greet(): Promise<string>;
 	public abstract async q(query: string): Promise<string>;
+	public abstract export(): any;
 
 	constructor(core: BotCore) {
+		super();
 		this.core = core;
 	}
+
+	public static import(core: BotCore, data: any) {
+		if (data.type == 'signin') return SigninContext.import(core, data.content);
+		return null;
+	}
 }
 
 class SigninContext extends Context {
@@ -71,6 +106,7 @@ class SigninContext extends Context {
 				return `${query}というユーザーは存在しませんでした... もう一度教えてください:`;
 			} else {
 				this.temporaryUser = user;
+				this.emit('updated');
 				return `パスワードを教えてください:`;
 			}
 		} else {
@@ -85,4 +121,16 @@ class SigninContext extends Context {
 			}
 		}
 	}
+
+	public export() {
+		return {
+			temporaryUser: this.temporaryUser
+		};
+	}
+
+	public static import(core: BotCore, data: any) {
+		const context = new SigninContext(core);
+		context.temporaryUser = data.temporaryUser;
+		return context;
+	}
 }
diff --git a/src/api/bot/interfaces/line.ts b/src/api/bot/interfaces/line.ts
index 9e1c813570..61aa728121 100644
--- a/src/api/bot/interfaces/line.ts
+++ b/src/api/bot/interfaces/line.ts
@@ -5,11 +5,10 @@ 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 sessions: Array<{
-	sourceId: string;
-	core: BotCore;
-}> = [];
+const redis = prominence(_redis);
 
 module.exports = async (app: express.Application) => {
 	if (config.line_bot == null) return;
@@ -21,22 +20,23 @@ module.exports = async (app: express.Application) => {
 		if (ev.message.type !== 'text') return;
 
 		const sourceId = ev.source.userId;
-		let session = sessions.find(s => s.sourceId === sourceId);
+		const sessionId = `line-bot-sessions:${sourceId}`;
 
-		if (!session) {
+		const _session = await redis.get(sessionId);
+		let session: BotCore;
+
+		if (_session == null) {
 			const user = await User.findOne({
 				line: {
 					user_id: sourceId
 				}
 			});
 
-			let core: BotCore;
-
 			if (user) {
-				core = new BotCore(user);
+				session = new BotCore(user);
 			} else {
-				core = new BotCore();
-				core.on('set-user', user => {
+				session = new BotCore();
+				session.on('set-user', user => {
 					User.update(user._id, {
 						$set: {
 							line: {
@@ -47,16 +47,18 @@ module.exports = async (app: express.Application) => {
 				});
 			}
 
-			session = {
-				sourceId: sourceId,
-				core: core
-			};
-
-			sessions.push(session);
+			redis.set(sessionId, JSON.stringify(session.export()));
+		} else {
+			session = BotCore.import(JSON.parse(_session));
 		}
 
-		const res = await session.core.q(ev.message.text);
+		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: {