From 7172ccd72c2ef40d68a252d7ae5cf45ddc575116 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 27 Aug 2017 16:53:15 +0900 Subject: Use createIndex() instead of index() to avoid deprecation warnings --- src/api/models/user.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/api/models/user.ts') diff --git a/src/api/models/user.ts b/src/api/models/user.ts index cd16459891..9f8cf0161d 100644 --- a/src/api/models/user.ts +++ b/src/api/models/user.ts @@ -2,8 +2,8 @@ import db from '../../db/mongodb'; const collection = db.get('users'); -(collection as any).index('username'); // fuck type definition -(collection as any).index('token'); // fuck type definition +(collection as any).createIndex('username'); // fuck type definition +(collection as any).createIndex('token'); // fuck type definition export default collection as any; // fuck type definition -- cgit v1.2.3-freya From 3feeaccf5955ebf17c26ccc06f32bb39624b89f6 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 8 Sep 2017 04:13:01 +0900 Subject: Add type definition --- src/api/endpoints/posts/create.ts | 4 ++-- src/api/models/post.ts | 14 ++++++++++++++ src/api/models/user.ts | 40 +++++++++++++++++++++++++++++++++++++-- src/utils/type.ts | 3 +++ 4 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 src/utils/type.ts (limited to 'src/api/models/user.ts') diff --git a/src/api/endpoints/posts/create.ts b/src/api/endpoints/posts/create.ts index eb979402c4..805dba7f83 100644 --- a/src/api/endpoints/posts/create.ts +++ b/src/api/endpoints/posts/create.ts @@ -6,7 +6,7 @@ import deepEqual = require('deep-equal'); import parse from '../../common/text'; import Post from '../../models/post'; import { isValidText } from '../../models/post'; -import User from '../../models/user'; +import { default as User, IUser } from '../../models/user'; import Following from '../../models/following'; import DriveFile from '../../models/drive-file'; import Watching from '../../models/post-watching'; @@ -24,7 +24,7 @@ import config from '../../../conf'; * @param {any} app * @return {Promise} */ -module.exports = (params, user, app) => new Promise(async (res, rej) => { +module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { // Get 'text' parameter const [text, textErr] = $(params.text).optional.string().pipe(isValidText).$; if (textErr) return rej('invalid text'); diff --git a/src/api/models/post.ts b/src/api/models/post.ts index baab63f991..8b9f7f5ef6 100644 --- a/src/api/models/post.ts +++ b/src/api/models/post.ts @@ -1,3 +1,5 @@ +import * as mongo from 'mongodb'; + import db from '../../db/mongodb'; export default db.get('posts') as any; // fuck type definition @@ -5,3 +7,15 @@ export default db.get('posts') as any; // fuck type definition export function isValidText(text: string): boolean { return text.length <= 1000 && text.trim() != ''; } + +export type IPost = { + _id: mongo.ObjectID; + created_at: Date; + media_ids: mongo.ObjectID[]; + reply_to_id: mongo.ObjectID; + repost_id: mongo.ObjectID; + poll: {}; // todo + text: string; + user_id: mongo.ObjectID; + app_id: mongo.ObjectID; +}; diff --git a/src/api/models/user.ts b/src/api/models/user.ts index 9f8cf0161d..1591b339bc 100644 --- a/src/api/models/user.ts +++ b/src/api/models/user.ts @@ -1,4 +1,7 @@ +import * as mongo from 'mongodb'; + import db from '../../db/mongodb'; +import { IPost } from './post'; const collection = db.get('users'); @@ -31,6 +34,39 @@ export function isValidBirthday(birthday: string): boolean { return typeof birthday == 'string' && /^([0-9]{4})\-([0-9]{2})-([0-9]{2})$/.test(birthday); } -export interface IUser { +export type IUser = { + _id: mongo.ObjectID; + created_at: Date; + email: string; + followers_count: number; + following_count: number; + links: string[]; name: string; -} + password: string; + posts_count: number; + drive_capacity: number; + username: string; + username_lower: string; + token: string; + avatar_id: mongo.ObjectID; + banner_id: mongo.ObjectID; + data: any; + twitter: { + access_token: string; + access_token_secret: string; + user_id: string; + screen_name: string; + }; + description: string; + profile: { + location: string; + birthday: string; // 'YYYY-MM-DD' + tags: string[]; + }; + last_used_at: Date; + latest_post: IPost; + pinned_post_id: mongo.ObjectID; + is_pro: boolean; + is_suspended: boolean; + keywords: string[]; +}; diff --git a/src/utils/type.ts b/src/utils/type.ts new file mode 100644 index 0000000000..ba6ea0be77 --- /dev/null +++ b/src/utils/type.ts @@ -0,0 +1,3 @@ +// https://github.com/Microsoft/TypeScript/issues/12215 +export type Diff = ({ [P in T]: P } & { [P in U]: never } & { [x: string]: never })[T]; +export type Omit = { [P in Diff]: T[P] }; -- cgit v1.2.3-freya From 6a5c6280ffd3ffe820beb23294f1c2c1f5deb9cf Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 7 Oct 2017 03:36:46 +0900 Subject: :v: --- src/api/bot/core.ts | 88 ++++++++++++++++++++++++ src/api/bot/interfaces/line.ts | 37 ++++++++++ src/api/endpoints/i/appdata/set.ts | 2 +- src/api/models/user.ts | 3 + src/api/serializers/user.ts | 1 + src/api/server.ts | 2 + src/common/get-post-summary.js | 39 +++++++++++ src/config.ts | 3 + src/web/app/common/scripts/get-post-summary.js | 35 ---------- src/web/app/desktop/script.js | 2 +- src/web/app/desktop/tags/notifications.tag | 2 +- src/web/app/desktop/tags/pages/home.tag | 2 +- src/web/app/mobile/tags/notification-preview.tag | 2 +- src/web/app/mobile/tags/notification.tag | 2 +- src/web/app/mobile/tags/notifications.tag | 2 +- src/web/app/mobile/tags/page/home.tag | 2 +- src/web/app/mobile/tags/post-detail.tag | 2 +- src/web/app/mobile/tags/timeline.tag | 2 +- src/web/app/mobile/tags/user.tag | 2 +- tslint.json | 1 + 20 files changed, 185 insertions(+), 46 deletions(-) create mode 100644 src/api/bot/core.ts create mode 100644 src/api/bot/interfaces/line.ts create mode 100644 src/common/get-post-summary.js delete mode 100644 src/web/app/common/scripts/get-post-summary.js (limited to 'src/api/models/user.ts') diff --git a/src/api/bot/core.ts b/src/api/bot/core.ts new file mode 100644 index 0000000000..002ac1b06e --- /dev/null +++ b/src/api/bot/core.ts @@ -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 { + 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; + public abstract async q(query: string): Promise; + + constructor(core: BotCore) { + this.core = core; + } +} + +class SigninContext extends Context { + private temporaryUser: IUser; + + public async greet(): Promise { + return 'まずユーザー名を教えてください:'; + } + + public async q(query: string): Promise { + 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 `パスワードが違います... もう一度教えてください:`; + } + } + } +} diff --git a/src/api/bot/interfaces/line.ts b/src/api/bot/interfaces/line.ts new file mode 100644 index 0000000000..4bee844c12 --- /dev/null +++ b/src/api/bot/interfaces/line.ts @@ -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); + } + }); +}; diff --git a/src/api/endpoints/i/appdata/set.ts b/src/api/endpoints/i/appdata/set.ts index 24f192de6b..9c3dbe185b 100644 --- a/src/api/endpoints/i/appdata/set.ts +++ b/src/api/endpoints/i/appdata/set.ts @@ -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'); diff --git a/src/api/models/user.ts b/src/api/models/user.ts index 1591b339bc..4f8086d42b 100644 --- a/src/api/models/user.ts +++ b/src/api/models/user.ts @@ -57,6 +57,9 @@ export type IUser = { user_id: string; screen_name: string; }; + line: { + user_id: string; + }; description: string; profile: { location: string; diff --git a/src/api/serializers/user.ts b/src/api/serializers/user.ts index 23a176096a..3deff2d003 100644 --- a/src/api/serializers/user.ts +++ b/src/api/serializers/user.ts @@ -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) { diff --git a/src/api/server.ts b/src/api/server.ts index c98167eb3e..fdff0c7546 100644 --- a/src/api/server.ts +++ b/src/api/server.ts @@ -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; diff --git a/src/common/get-post-summary.js b/src/common/get-post-summary.js new file mode 100644 index 0000000000..f7a481a164 --- /dev/null +++ b/src/common/get-post-summary.js @@ -0,0 +1,39 @@ +/** + * 投稿を表す文字列を取得します。 + * @param {*} post 投稿 + */ +const summarize = post => { + let summary = post.text ? post.text : ''; + + // メディアが添付されているとき + if (post.media) { + summary += ` (${post.media.length}つのメディア)`; + } + + // 投票が添付されているとき + if (post.poll) { + summary += ' (投票)'; + } + + // 返信のとき + if (post.reply_to_id) { + if (post.reply_to) { + summary += ` RE: ${summarize(post.reply_to)}`; + } else { + summary += ' RE: ...'; + } + } + + // Repostのとき + if (post.repost_id) { + if (post.repost) { + summary += ` RP: ${summarize(post.repost)}`; + } else { + summary += ' RP: ...'; + } + } + + return summary.trim(); +}; + +export default summarize; diff --git a/src/config.ts b/src/config.ts index f8facdee2e..0ea332f67d 100644 --- a/src/config.ts +++ b/src/config.ts @@ -68,6 +68,9 @@ type Source = { hook_secret: string; username: string; }; + line_bot?: { + channel_secret: string; + }; analysis?: { mecab_command?: string; }; diff --git a/src/web/app/common/scripts/get-post-summary.js b/src/web/app/common/scripts/get-post-summary.js deleted file mode 100644 index 83eda8f6b4..0000000000 --- a/src/web/app/common/scripts/get-post-summary.js +++ /dev/null @@ -1,35 +0,0 @@ -const summarize = post => { - let summary = post.text ? post.text : ''; - - // メディアが添付されているとき - if (post.media) { - summary += ` (${post.media.length}つのメディア)`; - } - - // 投票が添付されているとき - if (post.poll) { - summary += ' (投票)'; - } - - // 返信のとき - if (post.reply_to_id) { - if (post.reply_to) { - summary += ` RE: ${summarize(post.reply_to)}`; - } else { - summary += ' RE: ...'; - } - } - - // Repostのとき - if (post.repost_id) { - if (post.repost) { - summary += ` RP: ${summarize(post.repost)}`; - } else { - summary += ' RP: ...'; - } - } - - return summary.trim(); -}; - -export default summarize; diff --git a/src/web/app/desktop/script.js b/src/web/app/desktop/script.js index 2e81147943..e3dc8b7d96 100644 --- a/src/web/app/desktop/script.js +++ b/src/web/app/desktop/script.js @@ -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 diff --git a/src/web/app/desktop/tags/notifications.tag b/src/web/app/desktop/tags/notifications.tag index 21e4fe7fa5..4747d1c0f4 100644 --- a/src/web/app/desktop/tags/notifications.tag +++ b/src/web/app/desktop/tags/notifications.tag @@ -207,7 +207,7 @@ diff --git a/src/web/app/mobile/tags/notification.tag b/src/web/app/mobile/tags/notification.tag index 3663709525..416493ee23 100644 --- a/src/web/app/mobile/tags/notification.tag +++ b/src/web/app/mobile/tags/notification.tag @@ -163,7 +163,7 @@ diff --git a/src/web/app/mobile/tags/notifications.tag b/src/web/app/mobile/tags/notifications.tag index 2f314769db..9985b3351c 100644 --- a/src/web/app/mobile/tags/notifications.tag +++ b/src/web/app/mobile/tags/notifications.tag @@ -78,7 +78,7 @@