From ec681a6705081ca694a1414105f69a8cb253d704 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 2 Sep 2020 21:54:01 +0900 Subject: [PATCH] nanka iroiro --- package.json | 2 +- src/ai.ts | 14 ++++++----- src/friend.ts | 21 +++++++++++----- src/index.ts | 3 ++- src/message.ts | 39 +++++++++--------------------- src/module.ts | 4 +-- src/modules/chart/index.ts | 4 +-- src/modules/chart/render-chart.ts | 6 ++--- src/modules/core/index.ts | 21 +++++++++++++--- src/modules/fortune/index.ts | 4 ++- src/modules/guessing-game/index.ts | 8 +++++- src/modules/kazutori/index.ts | 9 +++++-- src/modules/maze/gen-maze.ts | 8 +++--- src/modules/maze/index.ts | 4 +-- src/modules/reversi/back.ts | 4 +-- src/modules/timer/index.ts | 3 ++- src/stream.ts | 16 ++++++------ src/utils/japanese.ts | 4 +-- src/utils/or.ts | 2 +- src/vocabulary.ts | 4 +-- tsconfig.json | 2 +- 21 files changed, 104 insertions(+), 78 deletions(-) diff --git a/package.json b/package.json index 977db3f..fb2a749 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "ai", + "_v": "1.1", "main": "./built/index.js", "scripts": { "start": "node ./built", diff --git a/src/ai.ts b/src/ai.ts index d131db6..9982655 100644 --- a/src/ai.ts +++ b/src/ai.ts @@ -15,13 +15,14 @@ import Friend, { FriendDoc } from './friend'; import { User } from './misskey/user'; import Stream from './stream'; import log from './utils/log'; +const pkg = require('../package.json'); type MentionHook = (msg: Message) => Promise; type ContextHook = (msg: Message, data?: any) => Promise; type TimeoutCallback = (data?: any) => void; export type HandlerResult = { - reaction: string; + reaction: string | null; }; export type InstallerResult = { @@ -38,6 +39,7 @@ export type Meta = { * 藍 */ export default class 藍 { + public readonly version = pkg._v; public account: User; public connection: Stream; public modules: Module[] = []; @@ -54,7 +56,7 @@ export default class 藍 { noteId?: string; userId?: string; module: string; - key: string; + key: string | null; data?: any; }>; @@ -215,7 +217,7 @@ export default class 藍 { noteId: msg.replyId }); - let reaction = 'love'; + let reaction: string | null = 'love'; //#region // コンテキストがあればコンテキストフック呼び出し @@ -228,7 +230,7 @@ export default class 藍 { reaction = res.reaction; } } else { - let res: boolean | HandlerResult; + let res: boolean | HandlerResult | null = null; for (const handler of this.mentionHooks) { res = await handler(msg); @@ -367,7 +369,7 @@ export default class 藍 { * @param data コンテキストに保存するオプションのデータ */ @autobind - public subscribeReply(module: Module, key: string, isDm: boolean, id: string, data?: any) { + public subscribeReply(module: Module, key: string | null, isDm: boolean, id: string, data?: any) { this.contexts.insertOne(isDm ? { isDm: true, userId: id, @@ -389,7 +391,7 @@ export default class 藍 { * @param key コンテキストを識別するためのキー */ @autobind - public unsubscribeReply(module: Module, key: string) { + public unsubscribeReply(module: Module, key: string | null) { this.contexts.findAndRemove({ key: key, module: module.name diff --git a/src/friend.ts b/src/friend.ts index 63358b7..d5b47f6 100644 --- a/src/friend.ts +++ b/src/friend.ts @@ -8,7 +8,7 @@ import { genItem } from './vocabulary'; export type FriendDoc = { userId: string; user: User; - name?: string; + name?: string | null; love?: number; lastLoveIncrementedAt?: string; todayLoveIncrements?: number; @@ -42,21 +42,30 @@ export default class Friend { this.ai = ai; if (opts.user) { - this.doc = this.ai.friends.findOne({ + const exist = this.ai.friends.findOne({ userId: opts.user.id }); - if (this.doc == null) { - this.doc = this.ai.friends.insertOne({ + if (exist == null) { + const inserted = this.ai.friends.insertOne({ userId: opts.user.id, user: opts.user }); + + if (inserted == null) { + throw new Error('Failed to insert friend doc'); + } + + this.doc = inserted; } else { + this.doc = exist; this.doc.user = opts.user; this.save(); } - } else { + } else if (opts.doc) { this.doc = opts.doc; + } else { + throw new Error('No friend info specified'); } } @@ -100,7 +109,7 @@ export default class Friend { } // 1日に上げられる親愛度は最大3 - if (this.doc.lastLoveIncrementedAt == today && this.doc.todayLoveIncrements >= 3) return; + if (this.doc.lastLoveIncrementedAt == today && (this.doc.todayLoveIncrements || 0) >= 3) return; if (this.doc.love == null) this.doc.love = 0; this.doc.love++; diff --git a/src/index.ts b/src/index.ts index d062a41..18a76e1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,6 +7,7 @@ const promiseRetry = require('promise-retry'); import 藍 from './ai'; import config from './config'; import _log from './utils/log'; +const pkg = require('../package.json'); import CoreModule from './modules/core'; import TalkModule from './modules/talk'; @@ -39,7 +40,7 @@ function log(msg: string): void { _log(`[Boot]: ${msg}`); } -log(chalk.bold('Ai v1.0')); +log(chalk.bold(`Ai v${pkg._v}`)); promiseRetry(retry => { log(`Account fetching... ${chalk.gray(config.host)}`); diff --git a/src/message.ts b/src/message.ts index 891d490..d9ed675 100644 --- a/src/message.ts +++ b/src/message.ts @@ -60,47 +60,32 @@ export default class Message { } @autobind - public async reply(text: string, cw?: string, renote?: string) { + public async reply(text: string | null, opts?: { + file?: any; + cw?: string; + renote?: string; + immediate?: boolean; + }) { if (text == null) return; this.ai.log(`>>> Sending reply to ${chalk.underline(this.id)}`); - await delay(2000); - - if (this.isDm) { - return await this.ai.sendMessage(this.messageOrNote.userId, { - text: text - }); - } else { - return await this.ai.post({ - replyId: this.messageOrNote.id, - text: text, - cw: cw, - renoteId: renote - }); + if (!opts?.immediate) { + await delay(2000); } - } - - @autobind - public async replyWithFile(text: string, file: any, cw?: string, renote?: string) { - if (text == null) return; - - this.ai.log(`>>> Sending reply to ${chalk.underline(this.id)}`); - - await delay(2000); if (this.isDm) { return await this.ai.sendMessage(this.messageOrNote.userId, { text: text, - fileId: file.id + fileId: opts?.file?.id }); } else { return await this.ai.post({ replyId: this.messageOrNote.id, text: text, - fileIds: [file.id], - cw: cw, - renoteId: renote + fileIds: opts?.file ? [opts?.file.id] : undefined, + cw: opts?.cw, + renoteId: opts?.renote }); } } diff --git a/src/module.ts b/src/module.ts index c5e3ba7..8777413 100644 --- a/src/module.ts +++ b/src/module.ts @@ -37,7 +37,7 @@ export default abstract class Module { * @param data コンテキストに保存するオプションのデータ */ @autobind - protected subscribeReply(key: string, isDm: boolean, id: string, data?: any) { + protected subscribeReply(key: string | null, isDm: boolean, id: string, data?: any) { this.ai.subscribeReply(this, key, isDm, id, data); } @@ -46,7 +46,7 @@ export default abstract class Module { * @param key コンテキストを識別するためのキー */ @autobind - protected unsubscribeReply(key: string) { + protected unsubscribeReply(key: string | null) { this.ai.unsubscribeReply(this, key); } diff --git a/src/modules/chart/index.ts b/src/modules/chart/index.ts index 6bcb849..a0a0409 100644 --- a/src/modules/chart/index.ts +++ b/src/modules/chart/index.ts @@ -101,7 +101,7 @@ export default class extends Module { const diffRange = 150; const datasetCount = 1 + Math.floor(Math.random() * 3); - let datasets = []; + let datasets: any[] = []; for (let d = 0; d < datasetCount; d++) { let values = [Math.random() * 1000]; @@ -151,7 +151,7 @@ export default class extends Module { }); this.log('Replying...'); - msg.replyWithFile(serifs.chart.foryou, file); + msg.reply(serifs.chart.foryou, { file }); return { reaction: 'like' diff --git a/src/modules/chart/render-chart.ts b/src/modules/chart/render-chart.ts index f383f18..a17e804 100644 --- a/src/modules/chart/render-chart.ts +++ b/src/modules/chart/render-chart.ts @@ -96,7 +96,7 @@ export function renderChart(chart: Chart) { ctx.fillText(step.toString(), chartAreaX, chartAreaY + y - 8); } - const newDatasets = []; + const newDatasets: any[] = []; for (let series = 0; series < serieses; series++) { newDatasets.push({ @@ -127,7 +127,7 @@ export function renderChart(chart: Chart) { ctx.lineCap = 'round'; for (let xAxis = 0; xAxis < xAxisCount; xAxis++) { - const xAxisPerTypeHeights = []; + const xAxisPerTypeHeights: number[] = []; for (let series = 0; series < serieses; series++) { const v = newDatasets[series].data[xAxis]; @@ -175,7 +175,7 @@ function niceScale(lowerBound: number, upperBound: number, ticks: number): numbe // // Output will be an array of the Y axis values that // encompass the Y values. - const steps = []; + const steps: number[] = []; // Determine Range const range = upperBound - lowerBound; diff --git a/src/modules/core/index.ts b/src/modules/core/index.ts index eeed825..10cf836 100644 --- a/src/modules/core/index.ts +++ b/src/modules/core/index.ts @@ -25,7 +25,8 @@ export default class extends Module { this.transferBegin(msg) || this.transferEnd(msg) || this.setName(msg) || - this.modules(msg) + this.modules(msg) || + this.version(msg) ); } @@ -79,7 +80,7 @@ export default class extends Module { return true; } - const name = msg.text.match(/^(.+?)って呼んで/)[1]; + const name = msg.text.match(/^(.+?)って呼んで/)![1]; if (name.length > 10) { msg.reply(serifs.core.tooLong); @@ -120,7 +121,21 @@ export default class extends Module { text += '```'; - msg.reply(text); + msg.reply(text, { + immediate: true + }); + + return true; + } + + @autobind + private version(msg: Message): boolean { + if (!msg.text) return false; + if (!msg.or(['v', 'version', 'バージョン'])) return false; + + msg.reply(`\`\`\`\nv${this.ai.version}\n\`\`\``, { + immediate: true + }); return true; } diff --git a/src/modules/fortune/index.ts b/src/modules/fortune/index.ts index 31f715a..8e39667 100644 --- a/src/modules/fortune/index.ts +++ b/src/modules/fortune/index.ts @@ -37,7 +37,9 @@ export default class extends Module { const rng = seedrandom(seed); const omikuji = blessing[Math.floor(rng() * blessing.length)]; const item = genItem(rng); - msg.reply(`**${omikuji}🎉**\nラッキーアイテム: ${item}`, serifs.fortune.cw(msg.friend.name)); + msg.reply(`**${omikuji}🎉**\nラッキーアイテム: ${item}`, { + cw: serifs.fortune.cw(msg.friend.name) + }); return true; } else { return false; diff --git a/src/modules/guessing-game/index.ts b/src/modules/guessing-game/index.ts index e6d386a..77c5c2f 100644 --- a/src/modules/guessing-game/index.ts +++ b/src/modules/guessing-game/index.ts @@ -13,7 +13,7 @@ export default class extends Module { tries: number[]; isEnded: boolean; startedAt: number; - endedAt: number; + endedAt: number | null; }>; @autobind @@ -74,6 +74,12 @@ export default class extends Module { isEnded: false }); + // 処理の流れ上、実際にnullになることは無さそうだけど一応 + if (exist == null) { + this.unsubscribeReply(msg.userId); + return; + } + if (msg.text.includes('やめ')) { msg.reply(serifs.guessingGame.cancel); exist.isEnded = true; diff --git a/src/modules/kazutori/index.ts b/src/modules/kazutori/index.ts index 3cd3033..84119e9 100644 --- a/src/modules/kazutori/index.ts +++ b/src/modules/kazutori/index.ts @@ -51,7 +51,9 @@ export default class extends Module { if (recentGame) { // 現在アクティブなゲームがある場合 if (!recentGame.isEnded) { - msg.reply(serifs.kazutori.alreadyStarted, null, recentGame.postId); + msg.reply(serifs.kazutori.alreadyStarted, { + renote: recentGame.postId + }); return true; } @@ -90,6 +92,9 @@ export default class extends Module { isEnded: false }); + // 処理の流れ上、実際にnullになることは無さそうだけど一応 + if (game == null) return; + // 既に数字を取っていたら if (game.votes.some(x => x.user.id == msg.userId)) return { reaction: 'confused' @@ -175,7 +180,7 @@ export default class extends Module { } let results: string[] = []; - let winner: User = null; + let winner: User | null = null; for (let i = 100; i >= 0; i--) { const users = game.votes diff --git a/src/modules/maze/gen-maze.ts b/src/modules/maze/gen-maze.ts index c95441f..a14e4ee 100644 --- a/src/modules/maze/gen-maze.ts +++ b/src/modules/maze/gen-maze.ts @@ -149,7 +149,7 @@ export function genMaze(seed, complexity?) { let dir: Dir; if (straightMode && rand(straightness) !== 0) { - if (dirs.includes(prevDir)) { + if (prevDir != null && dirs.includes(prevDir)) { dir = prevDir; } else { dir = dirs[rand(dirs.length)]; @@ -158,7 +158,7 @@ export function genMaze(seed, complexity?) { dir = dirs[rand(dirs.length)]; } - maze[x][y] = cellVariants[maze[x][y]].digg[dir]; + maze[x][y] = cellVariants[maze[x][y]].digg[dir]!; if (dir === 'top') { maze[x][y - 1] = maze[x][y - 1] === 'empty' ? 'bottom' : 'cross'; @@ -183,7 +183,7 @@ export function genMaze(seed, complexity?) { } //#region start digg - const nonVoidCells = []; + const nonVoidCells: [number, number][] = []; for (let y = 0; y < mazeSize; y++) { for (let x = 0; x < mazeSize; x++) { @@ -199,7 +199,7 @@ export function genMaze(seed, complexity?) { let hasEmptyCell = true; while (hasEmptyCell) { - const nonEmptyCells = []; + const nonEmptyCells: [number, number][] = []; for (let y = 0; y < mazeSize; y++) { for (let x = 0; x < mazeSize; x++) { diff --git a/src/modules/maze/index.ts b/src/modules/maze/index.ts index 124c019..e3a1f94 100644 --- a/src/modules/maze/index.ts +++ b/src/modules/maze/index.ts @@ -58,7 +58,7 @@ export default class extends Module { @autobind private async mentionHook(msg: Message) { if (msg.includes(['迷路'])) { - let size = null; + let size: string | null = null; if (msg.includes(['接待'])) size = 'veryEasy'; if (msg.includes(['簡単', 'かんたん', '易しい', 'やさしい', '小さい', 'ちいさい'])) size = 'easy'; if (msg.includes(['難しい', 'むずかしい', '複雑な', '大きい', 'おおきい'])) size = 'hard'; @@ -68,7 +68,7 @@ export default class extends Module { setTimeout(async () => { const file = await this.genMazeFile(Date.now(), size); this.log('Replying...'); - msg.replyWithFile(serifs.maze.foryou, file); + msg.reply(serifs.maze.foryou, { file }); }, 3000); return { reaction: 'like' diff --git a/src/modules/reversi/back.ts b/src/modules/reversi/back.ts index 68bcd9d..fed3a9a 100644 --- a/src/modules/reversi/back.ts +++ b/src/modules/reversi/back.ts @@ -209,7 +209,7 @@ class Session { */ private onEnded = async (msg: any) => { // ストリームから切断 - process.send({ + process.send!({ type: 'ended' }); @@ -406,7 +406,7 @@ class Session { console.timeEnd('think'); setTimeout(() => { - process.send({ + process.send!({ type: 'put', pos }); diff --git a/src/modules/timer/index.ts b/src/modules/timer/index.ts index 680c93c..280abf1 100644 --- a/src/modules/timer/index.ts +++ b/src/modules/timer/index.ts @@ -43,7 +43,7 @@ export default class extends Module { msg.reply(serifs.timer.set); - const str = `${hours ? hoursQuery[0] : ''}${minutes ? minutesQuery[0] : ''}${seconds ? secondsQuery[0] : ''}`; + const str = `${hours ? hoursQuery![0] : ''}${minutes ? minutesQuery![0] : ''}${seconds ? secondsQuery![0] : ''}`; // タイマーセット this.setTimeoutWithPersistence(time, { @@ -59,6 +59,7 @@ export default class extends Module { @autobind private timeoutCallback(data) { const friend = this.ai.lookupFriend(data.userId); + if (friend == null) return; // 処理の流れ上、実際にnullになることは無さそうだけど一応 const text = serifs.timer.notify(data.time, friend.name); if (data.isDm) { this.ai.sendMessage(friend.userId, { diff --git a/src/stream.ts b/src/stream.ts index 490d769..696101c 100644 --- a/src/stream.ts +++ b/src/stream.ts @@ -71,11 +71,11 @@ export default class Stream extends EventEmitter { this.emit('_connected_'); // バッファーを処理 - const _buffer = [].concat(this.buffer); // Shallow copy + const _buffer = [...this.buffer]; // Shallow copy this.buffer = []; // Clear buffer - _buffer.forEach(data => { + for (const data of _buffer) { this.send(data); // Resend each buffered messages - }); + } // チャンネル再接続 if (isReconnect) { @@ -107,7 +107,7 @@ export default class Stream extends EventEmitter { if (type == 'channel') { const id = body.id; - let connections: Connection[]; + let connections: (Connection | undefined)[]; connections = this.sharedConnections.filter(c => c.id === id); @@ -115,10 +115,10 @@ export default class Stream extends EventEmitter { connections = [this.nonSharedConnections.find(c => c.id === id)]; } - connections.filter(c => c != null).forEach(c => { - c.emit(body.type, body.body); - c.emit('*', { type: body.type, body: body.body }); - }); + for (const c of connections.filter(c => c != null)) { + c!.emit(body.type, body.body); + c!.emit('*', { type: body.type, body: body.body }); + } } else { this.emit(type, body); this.emit('*', { type, body }); diff --git a/src/utils/japanese.ts b/src/utils/japanese.ts index a8ca01e..66e8ac5 100644 --- a/src/utils/japanese.ts +++ b/src/utils/japanese.ts @@ -56,7 +56,7 @@ export function zenkakuToHankaku(str: string): string { return str .replace(reg, match => - kanaMap.find(x => x[0] == match)[1] + kanaMap.find(x => x[0] == match)![1] ) .replace(/゛/g, '゙') .replace(/゜/g, '゚'); @@ -72,7 +72,7 @@ export function hankakuToZenkaku(str: string): string { return str .replace(reg, match => - kanaMap.find(x => x[1] == match)[0] + kanaMap.find(x => x[1] == match)![0] ) .replace(/゙/g, '゛') .replace(/゚/g, '゜'); diff --git a/src/utils/or.ts b/src/utils/or.ts index 32fabbf..32a1b03 100644 --- a/src/utils/or.ts +++ b/src/utils/or.ts @@ -40,7 +40,7 @@ export default function(text: string, words: (string | RegExp)[]): boolean { } let textBefore = text; - let textAfter = null; + let textAfter: string | null = null; while (textBefore != textAfter) { textBefore = text; diff --git a/src/vocabulary.ts b/src/vocabulary.ts index 1efaffc..149707e 100644 --- a/src/vocabulary.ts +++ b/src/vocabulary.ts @@ -221,11 +221,11 @@ export const and = [ 'のそばにある', ]; -export function genItem(seedOrRng = null) { +export function genItem(seedOrRng?: (() => number) | string | number) { const rng = seedOrRng ? typeof seedOrRng === 'function' ? seedOrRng - : seedrandom(seedOrRng) + : seedrandom(seedOrRng.toString()) : Math.random; let item = ''; diff --git a/tsconfig.json b/tsconfig.json index 07dbb1a..e0a70d6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,7 @@ "noImplicitReturns": true, "noImplicitThis": true, "noFallthroughCasesInSwitch": true, - "strictNullChecks": false, + "strictNullChecks": true, "experimentalDecorators": true, "sourceMap": false, "target": "es2017",