From 1ca93c4edf6997a72716edf91c0cb138c5791f8d Mon Sep 17 00:00:00 2001 From: takejohn <105504345+takejohn@users.noreply.github.com> Date: Mon, 25 Mar 2024 01:38:30 +0900 Subject: [PATCH] =?UTF-8?q?=E8=97=8D=E3=82=92=E3=82=A4=E3=83=B3=E3=82=BF?= =?UTF-8?q?=E3=83=BC=E3=83=95=E3=82=A7=E3=82=A4=E3=82=B9=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ai.ts | 93 +++++++++++++++++++++++++++++++++++++++++++--------- src/index.ts | 4 +-- 2 files changed, 80 insertions(+), 17 deletions(-) diff --git a/src/ai.ts b/src/ai.ts index ae3315a..b7720ee 100644 --- a/src/ai.ts +++ b/src/ai.ts @@ -41,20 +41,31 @@ export type Meta = { /** * 藍 */ -export default class 藍 { +export default interface 藍 extends Ai { + connection: Stream; + lastSleepedAt: number; + + friends: loki.Collection; + moduleData: loki.Collection; +} + +/** + * 起動中の藍 + */ +export class Ai { public readonly version = pkg._v; public account: User; - public connection: Stream; + public connection?: Stream; public modules: Module[] = []; private mentionHooks: MentionHook[] = []; private contextHooks: { [moduleName: string]: ContextHook } = {}; private timeoutCallbacks: { [moduleName: string]: TimeoutCallback } = {}; public db: loki; - public lastSleepedAt: number; + public lastSleepedAt?: number; - private meta: loki.Collection; + private meta?: loki.Collection; - private contexts: loki.Collection<{ + private contexts?: loki.Collection<{ noteId?: string; userId?: string; module: string; @@ -62,7 +73,7 @@ export default class 藍 { data?: any; }>; - private timers: loki.Collection<{ + private timers?: loki.Collection<{ id: string; module: string; insertedAt: number; @@ -70,8 +81,10 @@ export default class 藍 { data?: any; }>; - public friends: loki.Collection; - public moduleData: loki.Collection; + public friends?: loki.Collection; + public moduleData?: loki.Collection; + + private ready: boolean = false; /** * 藍インスタンスを生成します @@ -138,6 +151,9 @@ export default class 藍 { // Init stream this.connection = new Stream(); + // この時点から藍インスタンスに + this.setReady(); + //#region Main stream const mainStream = this.connection.useSharedConnection('main'); @@ -205,12 +221,40 @@ export default class 藍 { this.log(chalk.green.bold('Ai am now running!')); } + /** + * 準備が完了したフラグを立てる。 + */ + private setReady(): asserts this is 藍 { + // 呼び出すタイミングが正しいか検証 + if ( + this.connection == null || + this.lastSleepedAt == null || + this.meta == null || + this.contexts == null || + this.timers == null || + this.friends == null || + this.moduleData == null + ) { + throw new TypeError('Cannot set ready'); + } + + this.ready = true; + } + + public requireReady(): asserts this is 藍 { + if (!this.ready) { + throw new TypeError('Ai am not ready!'); + } + } + /** * ユーザーから話しかけられたとき * (メンション、リプライ、トークのメッセージ) */ @bindThis private async onReceiveMessage(msg: Message): Promise { + this.requireReady(); + this.log(chalk.gray(`<<< An message received: ${chalk.underline(msg.id)}`)); // Ignore message if the user is a bot @@ -222,7 +266,7 @@ export default class 藍 { const isNoContext = msg.replyId == null; // Look up the context - const context = isNoContext ? null : this.contexts.findOne({ + const context = isNoContext ? null : this.contexts!.findOne({ noteId: msg.replyId }); @@ -278,6 +322,8 @@ export default class 藍 { @bindThis private onNotification(notification: any) { + this.requireReady(); + switch (notification.type) { // リアクションされたら親愛度を少し上げる // TODO: リアクション取り消しをよしなにハンドリングする @@ -294,12 +340,14 @@ export default class 藍 { @bindThis private crawleTimer() { - const timers = this.timers.find(); + this.requireReady(); + + const timers = this.timers!.find(); for (const timer of timers) { // タイマーが時間切れかどうか if (Date.now() - (timer.insertedAt + timer.delay) >= 0) { this.log(`Timer expired: ${timer.module} ${timer.id}`); - this.timers.remove(timer); + this.timers!.remove(timer); this.timeoutCallbacks[timer.module](timer.data); } } @@ -330,6 +378,8 @@ export default class 藍 { @bindThis public lookupFriend(userId: User['id']): Friend | null { + this.requireReady(); + const doc = this.friends.findOne({ userId: userId }); @@ -399,7 +449,8 @@ export default class 藍 { */ @bindThis public subscribeReply(module: Module, key: string | null, id: string, data?: any) { - this.contexts.insertOne({ + this.requireReady(); + this.contexts!.insertOne({ noteId: id, module: module.name, key: key, @@ -414,7 +465,8 @@ export default class 藍 { */ @bindThis public unsubscribeReply(module: Module, key: string | null) { - this.contexts.findAndRemove({ + this.requireReady(); + this.contexts!.findAndRemove({ key: key, module: module.name }); @@ -429,8 +481,10 @@ export default class 藍 { */ @bindThis public setTimeoutWithPersistence(module: Module, delay: number, data?: any) { + this.requireReady(); + const id = uuid(); - this.timers.insertOne({ + this.timers!.insertOne({ id: id, module: module.name, insertedAt: Date.now(), @@ -443,6 +497,10 @@ export default class 藍 { @bindThis public getMeta() { + if (this.meta == null) { + throw new TypeError('meta has not been set'); + } + const rec = this.meta.findOne(); if (rec) { @@ -465,6 +523,11 @@ export default class 藍 { rec[k] = v; } - this.meta.update(rec); + this.meta!.update(rec); } } + +// FIXME: +// JS にコンパイルされたコードでインターフェイスであるはずの藍がインポートされてしまうので、 +// 同名のクラスを定義することで実行時エラーが出ないようにしている +export default class 藍 {} diff --git a/src/index.ts b/src/index.ts index 9ca82e2..7a01452 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,7 +5,7 @@ import chalk from 'chalk'; import got from 'got'; import promiseRetry from 'promise-retry'; -import 藍 from './ai.js'; +import { Ai } from './ai.js'; import config from './config.js'; import _log from './utils/log.js'; import pkg from '../package.json' assert { type: 'json' }; @@ -72,7 +72,7 @@ promiseRetry(retry => { log('Starting AiOS...'); // 藍起動 - new 藍(account, [ + new Ai(account, [ new CoreModule(), new EmojiModule(), new EmojiReactModule(),