This commit is contained in:
syuilo 2017-10-07 03:36:46 +09:00
parent f5e179c72c
commit e70d36f6e7
19 changed files with 150 additions and 11 deletions

88
src/api/bot/core.ts Normal file
View 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 `パスワードが違います... もう一度教えてください:`;
}
}
}
}

View 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);
}
});
};

View file

@ -21,7 +21,7 @@ module.exports = (params, user, app, isSecure) => new Promise(async (res, rej) =
const [data, dataError] = $(params.data).optional.object() const [data, dataError] = $(params.data).optional.object()
.pipe(obj => { .pipe(obj => {
const hasInvalidData = Object.entries(obj).some(([k, v]) => 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; return !hasInvalidData;
}).$; }).$;
if (dataError) return rej('invalid data param'); if (dataError) return rej('invalid data param');

View file

@ -57,6 +57,9 @@ export type IUser = {
user_id: string; user_id: string;
screen_name: string; screen_name: string;
}; };
line: {
user_id: string;
};
description: string; description: string;
profile: { profile: {
location: string; location: string;

View file

@ -79,6 +79,7 @@ export default (
delete _user.twitter.access_token; delete _user.twitter.access_token;
delete _user.twitter.access_token_secret; delete _user.twitter.access_token_secret;
} }
delete _user.line;
// Visible via only the official client // Visible via only the official client
if (!opts.includeSecrets) { if (!opts.includeSecrets) {

View file

@ -54,4 +54,6 @@ app.use((req, res, next) => {
require('./service/github')(app); require('./service/github')(app);
require('./service/twitter')(app); require('./service/twitter')(app);
require('./bot/interfaces/line')(app);
module.exports = app; module.exports = app;

View file

@ -1,3 +1,7 @@
/**
* 投稿を表す文字列を取得します
* @param {*} post 投稿
*/
const summarize = post => { const summarize = post => {
let summary = post.text ? post.text : ''; let summary = post.text ? post.text : '';

View file

@ -68,6 +68,9 @@ type Source = {
hook_secret: string; hook_secret: string;
username: string; username: string;
}; };
line_bot?: {
channel_secret: string;
};
analysis?: { analysis?: {
mecab_command?: string; mecab_command?: string;
}; };

View file

@ -11,7 +11,7 @@ import * as riot from 'riot';
import init from '../init'; import init from '../init';
import route from './router'; import route from './router';
import fuckAdBlock from './scripts/fuck-ad-block'; import fuckAdBlock from './scripts/fuck-ad-block';
import getPostSummary from '../common/scripts/get-post-summary'; import getPostSummary from '../../../common/get-post-summary';
/** /**
* init * init

View file

@ -207,7 +207,7 @@
</style> </style>
<script> <script>
import getPostSummary from '../../common/scripts/get-post-summary'; import getPostSummary from '../../../../common/get-post-summary';
this.getPostSummary = getPostSummary; this.getPostSummary = getPostSummary;
this.mixin('i'); this.mixin('i');

View file

@ -8,7 +8,7 @@
</style> </style>
<script> <script>
import Progress from '../../../common/scripts/loading'; 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('i');
this.mixin('api'); this.mixin('api');

View file

@ -110,7 +110,7 @@
</style> </style>
<script> <script>
import getPostSummary from '../../common/scripts/get-post-summary'; import getPostSummary from '../../../../common/get-post-summary';
this.getPostSummary = getPostSummary; this.getPostSummary = getPostSummary;
this.notification = this.opts.notification; this.notification = this.opts.notification;
</script> </script>

View file

@ -163,7 +163,7 @@
</style> </style>
<script> <script>
import getPostSummary from '../../common/scripts/get-post-summary'; import getPostSummary from '../../../../common/get-post-summary';
this.getPostSummary = getPostSummary; this.getPostSummary = getPostSummary;
this.notification = this.opts.notification; this.notification = this.opts.notification;
</script> </script>

View file

@ -78,7 +78,7 @@
</style> </style>
<script> <script>
import getPostSummary from '../../common/scripts/get-post-summary'; import getPostSummary from '../../../../common/get-post-summary';
this.getPostSummary = getPostSummary; this.getPostSummary = getPostSummary;
this.mixin('api'); this.mixin('api');

View file

@ -9,7 +9,7 @@
<script> <script>
import ui from '../../scripts/ui-event'; import ui from '../../scripts/ui-event';
import Progress from '../../../common/scripts/loading'; 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'; import openPostForm from '../../scripts/open-post-form';
this.mixin('i'); this.mixin('i');

View file

@ -264,7 +264,7 @@
</style> </style>
<script> <script>
import compile from '../../common/scripts/text-compiler'; 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'; import openPostForm from '../scripts/open-post-form';
this.mixin('api'); this.mixin('api');

View file

@ -464,7 +464,7 @@
</style> </style>
<script> <script>
import compile from '../../common/scripts/text-compiler'; 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'; import openPostForm from '../scripts/open-post-form';
this.mixin('api'); this.mixin('api');

View file

@ -428,7 +428,7 @@
</style> </style>
<script> <script>
import summary from '../../common/scripts/get-post-summary'; import summary from '../../../../common/get-post-summary';
this.post = this.opts.post; this.post = this.opts.post;
this.text = summary(this.post); this.text = summary(this.post);

View file

@ -23,6 +23,7 @@
"comment-format": [false], "comment-format": [false],
"interface-over-type-literal": false, "interface-over-type-literal": false,
"max-line-length": [false], "max-line-length": [false],
"max-classes-per-file": false,
"member-ordering": [false], "member-ordering": [false],
"ban-types": [ "ban-types": [
"Object" "Object"