✌️
This commit is contained in:
		
							parent
							
								
									f89c3af982
								
							
						
					
					
						commit
						6a5c6280ff
					
				
					 19 changed files with 150 additions and 11 deletions
				
			
		
							
								
								
									
										88
									
								
								src/api/bot/core.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								src/api/bot/core.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,88 @@
 | 
			
		|||
import * as EventEmitter from 'events';
 | 
			
		||||
import * as bcrypt from 'bcryptjs';
 | 
			
		||||
 | 
			
		||||
import User, { IUser } from '../models/user';
 | 
			
		||||
 | 
			
		||||
export default class BotCore extends EventEmitter {
 | 
			
		||||
	public user: IUser;
 | 
			
		||||
 | 
			
		||||
	private context: Context = null;
 | 
			
		||||
 | 
			
		||||
	constructor(user: IUser) {
 | 
			
		||||
		super();
 | 
			
		||||
 | 
			
		||||
		this.user = user;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public async q(query: string): Promise<string> {
 | 
			
		||||
		if (this.context != null) {
 | 
			
		||||
			return await this.context.q(query);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		switch (query) {
 | 
			
		||||
			case 'ping':
 | 
			
		||||
				return 'PONG';
 | 
			
		||||
			case 'ログイン':
 | 
			
		||||
			case 'サインイン':
 | 
			
		||||
				this.context = new SigninContext(this);
 | 
			
		||||
				return await this.context.greet();
 | 
			
		||||
			default:
 | 
			
		||||
				return '?';
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public setUser(user: IUser) {
 | 
			
		||||
		this.user = user;
 | 
			
		||||
		this.emit('set-user', user);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
abstract class Context {
 | 
			
		||||
	protected core: BotCore;
 | 
			
		||||
 | 
			
		||||
	public abstract async greet(): Promise<string>;
 | 
			
		||||
	public abstract async q(query: string): Promise<string>;
 | 
			
		||||
 | 
			
		||||
	constructor(core: BotCore) {
 | 
			
		||||
		this.core = core;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class SigninContext extends Context {
 | 
			
		||||
	private temporaryUser: IUser;
 | 
			
		||||
 | 
			
		||||
	public async greet(): Promise<string> {
 | 
			
		||||
		return 'まずユーザー名を教えてください:';
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public async q(query: string): Promise<string> {
 | 
			
		||||
		if (this.temporaryUser == null) {
 | 
			
		||||
			// Fetch user
 | 
			
		||||
			const user: IUser = await User.findOne({
 | 
			
		||||
				username_lower: query.toLowerCase()
 | 
			
		||||
			}, {
 | 
			
		||||
				fields: {
 | 
			
		||||
					data: false,
 | 
			
		||||
					profile: false
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			if (user === null) {
 | 
			
		||||
				return `${query}というユーザーは存在しませんでした... もう一度教えてください:`;
 | 
			
		||||
			} else {
 | 
			
		||||
				this.temporaryUser = user;
 | 
			
		||||
				return `パスワードを教えてください:`;
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			// Compare password
 | 
			
		||||
			const same = bcrypt.compareSync(query, this.temporaryUser.password);
 | 
			
		||||
 | 
			
		||||
			if (same) {
 | 
			
		||||
				this.core.setUser(this.temporaryUser);
 | 
			
		||||
				return `${this.temporaryUser.name}さん、おかえりなさい!`;
 | 
			
		||||
			} else {
 | 
			
		||||
				return `パスワードが違います... もう一度教えてください:`;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										37
									
								
								src/api/bot/interfaces/line.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/api/bot/interfaces/line.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,37 @@
 | 
			
		|||
import * as EventEmitter from 'events';
 | 
			
		||||
import * as express from 'express';
 | 
			
		||||
import * as crypto from 'crypto';
 | 
			
		||||
//import User from '../../models/user';
 | 
			
		||||
import config from '../../../conf';
 | 
			
		||||
/*import BotCore from '../core';
 | 
			
		||||
 | 
			
		||||
const sessions: {
 | 
			
		||||
	userId: string;
 | 
			
		||||
	session: BotCore;
 | 
			
		||||
}[] = [];
 | 
			
		||||
*/
 | 
			
		||||
module.exports = async (app: express.Application) => {
 | 
			
		||||
	if (config.line_bot == null) return;
 | 
			
		||||
 | 
			
		||||
	const handler = new EventEmitter();
 | 
			
		||||
 | 
			
		||||
	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(JSON.stringify(req.body));
 | 
			
		||||
 | 
			
		||||
		const sig2 = hash.digest('base64');
 | 
			
		||||
 | 
			
		||||
		// シグネチャ比較
 | 
			
		||||
		if (sig1 === sig2) {
 | 
			
		||||
			console.log(req.body);
 | 
			
		||||
			handler.emit(req.body.type);
 | 
			
		||||
			res.sendStatus(200);
 | 
			
		||||
		} else {
 | 
			
		||||
			res.sendStatus(400);
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -21,7 +21,7 @@ module.exports = (params, user, app, isSecure) => new Promise(async (res, rej) =
 | 
			
		|||
	const [data, dataError] = $(params.data).optional.object()
 | 
			
		||||
		.pipe(obj => {
 | 
			
		||||
			const hasInvalidData = Object.entries(obj).some(([k, v]) =>
 | 
			
		||||
				$(k).string().match(/^[a-z_]+$/).isNg() && $(v).string().isNg());
 | 
			
		||||
				$(k).string().match(/^[a-z_]+$/).nok() && $(v).string().nok());
 | 
			
		||||
			return !hasInvalidData;
 | 
			
		||||
		}).$;
 | 
			
		||||
	if (dataError) return rej('invalid data param');
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -57,6 +57,9 @@ export type IUser = {
 | 
			
		|||
		user_id: string;
 | 
			
		||||
		screen_name: string;
 | 
			
		||||
	};
 | 
			
		||||
	line: {
 | 
			
		||||
		user_id: string;
 | 
			
		||||
	};
 | 
			
		||||
	description: string;
 | 
			
		||||
	profile: {
 | 
			
		||||
		location: string;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -79,6 +79,7 @@ export default (
 | 
			
		|||
		delete _user.twitter.access_token;
 | 
			
		||||
		delete _user.twitter.access_token_secret;
 | 
			
		||||
	}
 | 
			
		||||
	delete _user.line;
 | 
			
		||||
 | 
			
		||||
	// Visible via only the official client
 | 
			
		||||
	if (!opts.includeSecrets) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,4 +54,6 @@ app.use((req, res, next) => {
 | 
			
		|||
require('./service/github')(app);
 | 
			
		||||
require('./service/twitter')(app);
 | 
			
		||||
 | 
			
		||||
require('./bot/interfaces/line')(app);
 | 
			
		||||
 | 
			
		||||
module.exports = app;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,7 @@
 | 
			
		|||
/**
 | 
			
		||||
 * 投稿を表す文字列を取得します。
 | 
			
		||||
 * @param {*} post 投稿
 | 
			
		||||
 */
 | 
			
		||||
const summarize = post => {
 | 
			
		||||
	let summary = post.text ? post.text : '';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -68,6 +68,9 @@ type Source = {
 | 
			
		|||
		hook_secret: string;
 | 
			
		||||
		username: string;
 | 
			
		||||
	};
 | 
			
		||||
	line_bot?: {
 | 
			
		||||
		channel_secret: string;
 | 
			
		||||
	};
 | 
			
		||||
	analysis?: {
 | 
			
		||||
		mecab_command?: string;
 | 
			
		||||
	};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,7 +11,7 @@ import * as riot from 'riot';
 | 
			
		|||
import init from '../init';
 | 
			
		||||
import route from './router';
 | 
			
		||||
import fuckAdBlock from './scripts/fuck-ad-block';
 | 
			
		||||
import getPostSummary from '../common/scripts/get-post-summary';
 | 
			
		||||
import getPostSummary from '../../../common/get-post-summary';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * init
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -207,7 +207,7 @@
 | 
			
		|||
 | 
			
		||||
	</style>
 | 
			
		||||
	<script>
 | 
			
		||||
		import getPostSummary from '../../common/scripts/get-post-summary';
 | 
			
		||||
		import getPostSummary from '../../../../common/get-post-summary';
 | 
			
		||||
		this.getPostSummary = getPostSummary;
 | 
			
		||||
 | 
			
		||||
		this.mixin('i');
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,7 +8,7 @@
 | 
			
		|||
	</style>
 | 
			
		||||
	<script>
 | 
			
		||||
		import Progress from '../../../common/scripts/loading';
 | 
			
		||||
		import getPostSummary from '../../../common/scripts/get-post-summary';
 | 
			
		||||
		import getPostSummary from '../../../../../common/get-post-summary';
 | 
			
		||||
 | 
			
		||||
		this.mixin('i');
 | 
			
		||||
		this.mixin('api');
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -110,7 +110,7 @@
 | 
			
		|||
 | 
			
		||||
	</style>
 | 
			
		||||
	<script>
 | 
			
		||||
		import getPostSummary from '../../common/scripts/get-post-summary';
 | 
			
		||||
		import getPostSummary from '../../../../common/get-post-summary';
 | 
			
		||||
		this.getPostSummary = getPostSummary;
 | 
			
		||||
		this.notification = this.opts.notification;
 | 
			
		||||
	</script>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -163,7 +163,7 @@
 | 
			
		|||
 | 
			
		||||
	</style>
 | 
			
		||||
	<script>
 | 
			
		||||
		import getPostSummary from '../../common/scripts/get-post-summary';
 | 
			
		||||
		import getPostSummary from '../../../../common/get-post-summary';
 | 
			
		||||
		this.getPostSummary = getPostSummary;
 | 
			
		||||
		this.notification = this.opts.notification;
 | 
			
		||||
	</script>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -78,7 +78,7 @@
 | 
			
		|||
 | 
			
		||||
	</style>
 | 
			
		||||
	<script>
 | 
			
		||||
		import getPostSummary from '../../common/scripts/get-post-summary';
 | 
			
		||||
		import getPostSummary from '../../../../common/get-post-summary';
 | 
			
		||||
		this.getPostSummary = getPostSummary;
 | 
			
		||||
 | 
			
		||||
		this.mixin('api');
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,7 +9,7 @@
 | 
			
		|||
	<script>
 | 
			
		||||
		import ui from '../../scripts/ui-event';
 | 
			
		||||
		import Progress from '../../../common/scripts/loading';
 | 
			
		||||
		import getPostSummary from '../../../common/scripts/get-post-summary';
 | 
			
		||||
		import getPostSummary from '../../../../../common/get-post-summary';
 | 
			
		||||
		import openPostForm from '../../scripts/open-post-form';
 | 
			
		||||
 | 
			
		||||
		this.mixin('i');
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -264,7 +264,7 @@
 | 
			
		|||
	</style>
 | 
			
		||||
	<script>
 | 
			
		||||
		import compile from '../../common/scripts/text-compiler';
 | 
			
		||||
		import getPostSummary from '../../common/scripts/get-post-summary';
 | 
			
		||||
		import getPostSummary from '../../../../common/get-post-summary';
 | 
			
		||||
		import openPostForm from '../scripts/open-post-form';
 | 
			
		||||
 | 
			
		||||
		this.mixin('api');
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -464,7 +464,7 @@
 | 
			
		|||
	</style>
 | 
			
		||||
	<script>
 | 
			
		||||
		import compile from '../../common/scripts/text-compiler';
 | 
			
		||||
		import getPostSummary from '../../common/scripts/get-post-summary';
 | 
			
		||||
		import getPostSummary from '../../../../common/get-post-summary';
 | 
			
		||||
		import openPostForm from '../scripts/open-post-form';
 | 
			
		||||
 | 
			
		||||
		this.mixin('api');
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -428,7 +428,7 @@
 | 
			
		|||
 | 
			
		||||
	</style>
 | 
			
		||||
	<script>
 | 
			
		||||
		import summary from '../../common/scripts/get-post-summary';
 | 
			
		||||
		import summary from '../../../../common/get-post-summary';
 | 
			
		||||
 | 
			
		||||
		this.post = this.opts.post;
 | 
			
		||||
		this.text = summary(this.post);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,6 +23,7 @@
 | 
			
		|||
		"comment-format": [false],
 | 
			
		||||
		"interface-over-type-literal": false,
 | 
			
		||||
		"max-line-length": [false],
 | 
			
		||||
		"max-classes-per-file": false,
 | 
			
		||||
		"member-ordering": [false],
 | 
			
		||||
		"ban-types": [
 | 
			
		||||
			"Object"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue