藍をインターフェイスに

This commit is contained in:
takejohn 2024-03-25 01:38:30 +09:00
parent 94d671af00
commit 1ca93c4edf
2 changed files with 80 additions and 17 deletions

View file

@ -41,20 +41,31 @@ export type Meta = {
/** /**
* *
*/ */
export default class { export default interface extends Ai {
connection: Stream;
lastSleepedAt: number;
friends: loki.Collection<FriendDoc>;
moduleData: loki.Collection<any>;
}
/**
*
*/
export class Ai {
public readonly version = pkg._v; public readonly version = pkg._v;
public account: User; public account: User;
public connection: Stream; public connection?: Stream;
public modules: Module[] = []; public modules: Module[] = [];
private mentionHooks: MentionHook[] = []; private mentionHooks: MentionHook[] = [];
private contextHooks: { [moduleName: string]: ContextHook } = {}; private contextHooks: { [moduleName: string]: ContextHook } = {};
private timeoutCallbacks: { [moduleName: string]: TimeoutCallback } = {}; private timeoutCallbacks: { [moduleName: string]: TimeoutCallback } = {};
public db: loki; public db: loki;
public lastSleepedAt: number; public lastSleepedAt?: number;
private meta: loki.Collection<Meta>; private meta?: loki.Collection<Meta>;
private contexts: loki.Collection<{ private contexts?: loki.Collection<{
noteId?: string; noteId?: string;
userId?: string; userId?: string;
module: string; module: string;
@ -62,7 +73,7 @@ export default class 藍 {
data?: any; data?: any;
}>; }>;
private timers: loki.Collection<{ private timers?: loki.Collection<{
id: string; id: string;
module: string; module: string;
insertedAt: number; insertedAt: number;
@ -70,8 +81,10 @@ export default class 藍 {
data?: any; data?: any;
}>; }>;
public friends: loki.Collection<FriendDoc>; public friends?: loki.Collection<FriendDoc>;
public moduleData: loki.Collection<any>; public moduleData?: loki.Collection<any>;
private ready: boolean = false;
/** /**
* *
@ -138,6 +151,9 @@ export default class 藍 {
// Init stream // Init stream
this.connection = new Stream(); this.connection = new Stream();
// この時点から藍インスタンスに
this.setReady();
//#region Main stream //#region Main stream
const mainStream = this.connection.useSharedConnection('main'); const mainStream = this.connection.useSharedConnection('main');
@ -205,12 +221,40 @@ export default class 藍 {
this.log(chalk.green.bold('Ai am now running!')); 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 @bindThis
private async onReceiveMessage(msg: Message): Promise<void> { private async onReceiveMessage(msg: Message): Promise<void> {
this.requireReady();
this.log(chalk.gray(`<<< An message received: ${chalk.underline(msg.id)}`)); this.log(chalk.gray(`<<< An message received: ${chalk.underline(msg.id)}`));
// Ignore message if the user is a bot // Ignore message if the user is a bot
@ -222,7 +266,7 @@ export default class 藍 {
const isNoContext = msg.replyId == null; const isNoContext = msg.replyId == null;
// Look up the context // Look up the context
const context = isNoContext ? null : this.contexts.findOne({ const context = isNoContext ? null : this.contexts!.findOne({
noteId: msg.replyId noteId: msg.replyId
}); });
@ -278,6 +322,8 @@ export default class 藍 {
@bindThis @bindThis
private onNotification(notification: any) { private onNotification(notification: any) {
this.requireReady();
switch (notification.type) { switch (notification.type) {
// リアクションされたら親愛度を少し上げる // リアクションされたら親愛度を少し上げる
// TODO: リアクション取り消しをよしなにハンドリングする // TODO: リアクション取り消しをよしなにハンドリングする
@ -294,12 +340,14 @@ export default class 藍 {
@bindThis @bindThis
private crawleTimer() { private crawleTimer() {
const timers = this.timers.find(); this.requireReady();
const timers = this.timers!.find();
for (const timer of timers) { for (const timer of timers) {
// タイマーが時間切れかどうか // タイマーが時間切れかどうか
if (Date.now() - (timer.insertedAt + timer.delay) >= 0) { if (Date.now() - (timer.insertedAt + timer.delay) >= 0) {
this.log(`Timer expired: ${timer.module} ${timer.id}`); this.log(`Timer expired: ${timer.module} ${timer.id}`);
this.timers.remove(timer); this.timers!.remove(timer);
this.timeoutCallbacks[timer.module](timer.data); this.timeoutCallbacks[timer.module](timer.data);
} }
} }
@ -330,6 +378,8 @@ export default class 藍 {
@bindThis @bindThis
public lookupFriend(userId: User['id']): Friend | null { public lookupFriend(userId: User['id']): Friend | null {
this.requireReady();
const doc = this.friends.findOne({ const doc = this.friends.findOne({
userId: userId userId: userId
}); });
@ -399,7 +449,8 @@ export default class 藍 {
*/ */
@bindThis @bindThis
public subscribeReply(module: Module, key: string | null, id: string, data?: any) { public subscribeReply(module: Module, key: string | null, id: string, data?: any) {
this.contexts.insertOne({ this.requireReady();
this.contexts!.insertOne({
noteId: id, noteId: id,
module: module.name, module: module.name,
key: key, key: key,
@ -414,7 +465,8 @@ export default class 藍 {
*/ */
@bindThis @bindThis
public unsubscribeReply(module: Module, key: string | null) { public unsubscribeReply(module: Module, key: string | null) {
this.contexts.findAndRemove({ this.requireReady();
this.contexts!.findAndRemove({
key: key, key: key,
module: module.name module: module.name
}); });
@ -429,8 +481,10 @@ export default class 藍 {
*/ */
@bindThis @bindThis
public setTimeoutWithPersistence(module: Module, delay: number, data?: any) { public setTimeoutWithPersistence(module: Module, delay: number, data?: any) {
this.requireReady();
const id = uuid(); const id = uuid();
this.timers.insertOne({ this.timers!.insertOne({
id: id, id: id,
module: module.name, module: module.name,
insertedAt: Date.now(), insertedAt: Date.now(),
@ -443,6 +497,10 @@ export default class 藍 {
@bindThis @bindThis
public getMeta() { public getMeta() {
if (this.meta == null) {
throw new TypeError('meta has not been set');
}
const rec = this.meta.findOne(); const rec = this.meta.findOne();
if (rec) { if (rec) {
@ -465,6 +523,11 @@ export default class 藍 {
rec[k] = v; rec[k] = v;
} }
this.meta.update(rec); this.meta!.update(rec);
} }
} }
// FIXME:
// JS にコンパイルされたコードでインターフェイスであるはずの藍がインポートされてしまうので、
// 同名のクラスを定義することで実行時エラーが出ないようにしている
export default class {}

View file

@ -5,7 +5,7 @@ import chalk from 'chalk';
import got from 'got'; import got from 'got';
import promiseRetry from 'promise-retry'; import promiseRetry from 'promise-retry';
import from './ai.js'; import { Ai } from './ai.js';
import config from './config.js'; import config from './config.js';
import _log from './utils/log.js'; import _log from './utils/log.js';
import pkg from '../package.json' assert { type: 'json' }; import pkg from '../package.json' assert { type: 'json' };
@ -72,7 +72,7 @@ promiseRetry(retry => {
log('Starting AiOS...'); log('Starting AiOS...');
// 藍起動 // 藍起動
new (account, [ new Ai(account, [
new CoreModule(), new CoreModule(),
new EmojiModule(), new EmojiModule(),
new EmojiReactModule(), new EmojiReactModule(),