Compare commits

..

No commits in common. "c81e5b79743714ed91f6ca4b2b0f73e62bb29df6" and "296b63c6cfad1a1e0af65817501e45ac632615e4" have entirely different histories.

44 changed files with 470 additions and 809 deletions

View file

@ -1,48 +1,48 @@
{ {
"_v": "2.0.0", "_v": "1.5.0",
"type": "module",
"main": "./built/index.js", "main": "./built/index.js",
"scripts": { "scripts": {
"start": "node ./built", "start": "node ./built",
"start-daemon": "nodemon ./built", "build": "tsc",
"build": "tspc",
"test": "jest" "test": "jest"
}, },
"dependencies": { "dependencies": {
"@types/chalk": "2.2.0", "@types/chalk": "2.2.0",
"@types/lokijs": "1.5.14", "@types/lokijs": "1.5.4",
"@types/node": "20.11.5", "@types/node": "16.0.1",
"@types/promise-retry": "1.1.6", "@types/promise-retry": "1.1.3",
"@types/random-seed": "0.3.5", "@types/random-seed": "0.3.3",
"@types/seedrandom": "3.0.8", "@types/request-promise-native": "1.0.18",
"@types/twemoji-parser": "13.1.4", "@types/seedrandom": "2.4.28",
"@types/uuid": "9.0.7", "@types/twemoji-parser": "13.1.1",
"@types/ws": "8.5.10", "@types/uuid": "8.3.1",
"canvas": "2.11.2", "@types/ws": "7.4.6",
"chalk": "5.3.0", "autobind-decorator": "2.4.0",
"got": "14.0.0", "canvas": "2.10.2",
"chalk": "4.1.1",
"lokijs": "1.5.12", "lokijs": "1.5.12",
"memory-streams": "0.1.3", "memory-streams": "0.1.3",
"misskey-reversi": "0.0.5", "misskey-reversi": "0.0.5",
"nodemon": "3.0.3", "module-alias": "2.2.2",
"promise-retry": "2.0.1", "promise-retry": "2.0.1",
"random-seed": "0.3.0", "random-seed": "0.3.0",
"reconnecting-websocket": "4.4.0", "reconnecting-websocket": "4.4.0",
"request": "2.88.2", "request": "2.88.2",
"request-promise-native": "1.0.9",
"seedrandom": "3.0.5", "seedrandom": "3.0.5",
"ts-patch": "3.1.2", "timeout-as-promise": "1.0.0",
"twemoji-parser": "14.0.0", "ts-node": "10.0.0",
"typescript": "5.3.3", "twemoji-parser": "13.1.0",
"typescript-transform-paths": "3.4.6", "typescript": "4.3.5",
"uuid": "9.0.1", "uuid": "8.3.2",
"ws": "8.16.0" "ws": "7.5.2"
}, },
"devDependencies": { "devDependencies": {
"@koa/router": "9.4.0", "@koa/router": "9.4.0",
"@types/jest": "26.0.23", "@types/jest": "26.0.23",
"@types/koa": "2.13.1", "@types/koa": "2.13.1",
"@types/koa__router": "8.0.4", "@types/koa__router": "8.0.4",
"@types/websocket": "1.0.10", "@types/websocket": "1.0.2",
"jest": "26.6.3", "jest": "26.6.3",
"koa": "2.13.1", "koa": "2.13.1",
"koa-json-body": "5.3.0", "koa-json-body": "5.3.0",
@ -70,8 +70,5 @@
"^@/(.+)": "<rootDir>/src/$1", "^@/(.+)": "<rootDir>/src/$1",
"^#/(.+)": "<rootDir>/test/$1" "^#/(.+)": "<rootDir>/test/$1"
} }
},
"nodemonConfig": {
"ignore": ["memory.json"]
} }
} }

View file

@ -1,21 +1,21 @@
// AI CORE // AI CORE
import * as fs from 'fs'; import * as fs from 'fs';
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import loki from 'lokijs'; import * as loki from 'lokijs';
import got from 'got'; import * as request from 'request-promise-native';
import chalk from 'chalk'; import * as chalk from 'chalk';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
const delay = require('timeout-as-promise');
import config from '@/config.js'; import config from '@/config';
import Module from '@/module.js'; import Module from '@/module';
import Message from '@/message.js'; import Message from '@/message';
import Friend, { FriendDoc } from '@/friend.js'; import Friend, { FriendDoc } from '@/friend';
import { User } from '@/misskey/user.js'; import { User } from '@/misskey/user';
import Stream from '@/stream.js'; import Stream from '@/stream';
import log from '@/utils/log.js'; import log from '@/utils/log';
import { sleep } from './utils/sleep.js'; const pkg = require('../package.json');
import pkg from '../package.json' assert { type: 'json' };
type MentionHook = (msg: Message) => Promise<boolean | HandlerResult>; type MentionHook = (msg: Message) => Promise<boolean | HandlerResult>;
type ContextHook = (key: any, msg: Message, data?: any) => Promise<void | boolean | HandlerResult>; type ContextHook = (key: any, msg: Message, data?: any) => Promise<void | boolean | HandlerResult>;
@ -103,12 +103,12 @@ export default class 藍 {
}); });
} }
@bindThis @autobind
public log(msg: string) { public log(msg: string) {
log(chalk`[{magenta AiOS}]: ${msg}`); log(chalk`[{magenta AiOS}]: ${msg}`);
} }
@bindThis @autobind
private run() { private run() {
//#region Init DB //#region Init DB
this.meta = this.getCollection('meta', {}); this.meta = this.getCollection('meta', {});
@ -207,7 +207,7 @@ export default class 藍 {
* *
* () * ()
*/ */
@bindThis @autobind
private async onReceiveMessage(msg: Message): Promise<void> { private async onReceiveMessage(msg: Message): Promise<void> {
this.log(chalk.gray(`<<< An message received: ${chalk.underline(msg.id)}`)); this.log(chalk.gray(`<<< An message received: ${chalk.underline(msg.id)}`));
@ -262,7 +262,7 @@ export default class 藍 {
//#endregion //#endregion
if (!immediate) { if (!immediate) {
await sleep(1000); await delay(1000);
} }
// リアクションする // リアクションする
@ -274,7 +274,7 @@ export default class 藍 {
} }
} }
@bindThis @autobind
private onNotification(notification: any) { private onNotification(notification: any) {
switch (notification.type) { switch (notification.type) {
// リアクションされたら親愛度を少し上げる // リアクションされたら親愛度を少し上げる
@ -290,7 +290,7 @@ export default class 藍 {
} }
} }
@bindThis @autobind
private crawleTimer() { private crawleTimer() {
const timers = this.timers.find(); const timers = this.timers.find();
for (const timer of timers) { for (const timer of timers) {
@ -303,7 +303,7 @@ export default class 藍 {
} }
} }
@bindThis @autobind
private logWaking() { private logWaking() {
this.setMeta({ this.setMeta({
lastWakingAt: Date.now(), lastWakingAt: Date.now(),
@ -313,7 +313,7 @@ export default class 藍 {
/** /**
* *
*/ */
@bindThis @autobind
public getCollection(name: string, opts?: any): loki.Collection { public getCollection(name: string, opts?: any): loki.Collection {
let collection: loki.Collection; let collection: loki.Collection;
@ -326,7 +326,7 @@ export default class 藍 {
return collection; return collection;
} }
@bindThis @autobind
public lookupFriend(userId: User['id']): Friend | null { public lookupFriend(userId: User['id']): Friend | null {
const doc = this.friends.findOne({ const doc = this.friends.findOne({
userId: userId userId: userId
@ -342,9 +342,9 @@ export default class 藍 {
/** /**
* *
*/ */
@bindThis @autobind
public async upload(file: Buffer | fs.ReadStream, meta: any) { public async upload(file: Buffer | fs.ReadStream, meta: any) {
const res = await got.post({ const res = await request.post({
url: `${config.apiUrl}/drive/files/create`, url: `${config.apiUrl}/drive/files/create`,
formData: { formData: {
i: config.i, i: config.i,
@ -354,14 +354,14 @@ export default class 藍 {
} }
}, },
json: true json: true
}).json(); });
return res; return res;
} }
/** /**
* 稿 * 稿
*/ */
@bindThis @autobind
public async post(param: any) { public async post(param: any) {
const res = await this.api('notes/create', param); const res = await this.api('notes/create', param);
return res.createdNote; return res.createdNote;
@ -370,7 +370,7 @@ export default class 藍 {
/** /**
* *
*/ */
@bindThis @autobind
public sendMessage(userId: any, param: any) { public sendMessage(userId: any, param: any) {
return this.post(Object.assign({ return this.post(Object.assign({
visibility: 'specified', visibility: 'specified',
@ -381,14 +381,13 @@ export default class 藍 {
/** /**
* APIを呼び出します * APIを呼び出します
*/ */
@bindThis @autobind
public api(endpoint: string, param?: any) { public api(endpoint: string, param?: any) {
this.log(`API: ${endpoint}`); return request.post(`${config.apiUrl}/${endpoint}`, {
return got.post(`${config.apiUrl}/${endpoint}`, {
json: Object.assign({ json: Object.assign({
i: config.i i: config.i
}, param) }, param)
}).json(); });
}; };
/** /**
@ -398,7 +397,7 @@ export default class 藍 {
* @param id ID稿ID * @param id ID稿ID
* @param data * @param data
*/ */
@bindThis @autobind
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.contexts.insertOne({
noteId: id, noteId: id,
@ -413,7 +412,7 @@ export default class 藍 {
* @param module 解除するモジュール名 * @param module 解除するモジュール名
* @param key * @param key
*/ */
@bindThis @autobind
public unsubscribeReply(module: Module, key: string | null) { public unsubscribeReply(module: Module, key: string | null) {
this.contexts.findAndRemove({ this.contexts.findAndRemove({
key: key, key: key,
@ -428,7 +427,7 @@ export default class 藍 {
* @param delay * @param delay
* @param data * @param data
*/ */
@bindThis @autobind
public setTimeoutWithPersistence(module: Module, delay: number, data?: any) { public setTimeoutWithPersistence(module: Module, delay: number, data?: any) {
const id = uuid(); const id = uuid();
this.timers.insertOne({ this.timers.insertOne({
@ -442,7 +441,7 @@ export default class 藍 {
this.log(`Timer persisted: ${module.name} ${id} ${delay}ms`); this.log(`Timer persisted: ${module.name} ${id} ${delay}ms`);
} }
@bindThis @autobind
public getMeta() { public getMeta() {
const rec = this.meta.findOne(); const rec = this.meta.findOne();
@ -458,7 +457,7 @@ export default class 藍 {
} }
} }
@bindThis @autobind
public setMeta(meta: Partial<Meta>) { public setMeta(meta: Partial<Meta>) {
const rec = this.getMeta(); const rec = this.getMeta();

View file

@ -17,7 +17,7 @@ type Config = {
memoryDir?: string; memoryDir?: string;
}; };
import config from '../config.json' assert { type: 'json' }; const config = require('../config.json');
config.wsUrl = config.host.replace('http', 'ws'); config.wsUrl = config.host.replace('http', 'ws');
config.apiUrl = config.host + '/api'; config.apiUrl = config.host + '/api';

View file

@ -1,41 +0,0 @@
// https://github.com/andreypopp/autobind-decorator
/**
* Return a descriptor removing the value and returning a getter
* The getter will return a .bind version of the function
* and memoize the result against a symbol on the instance
*/
export function bindThis(target: any, key: string, descriptor: any) {
let fn = descriptor.value;
if (typeof fn !== 'function') {
throw new TypeError(`@bindThis decorator can only be applied to methods not: ${typeof fn}`);
}
return {
configurable: true,
get() {
// eslint-disable-next-line no-prototype-builtins
if (this === target.prototype || this.hasOwnProperty(key) ||
typeof fn !== 'function') {
return fn;
}
const boundFn = fn.bind(this);
Object.defineProperty(this, key, {
configurable: true,
get() {
return boundFn;
},
set(value) {
fn = value;
delete this[key];
},
});
return boundFn;
},
set(value: any) {
fn = value;
},
};
}

View file

@ -1,9 +1,9 @@
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import from '@/ai.js'; import from '@/ai';
import IModule from '@/module.js'; import IModule from '@/module';
import getDate from '@/utils/get-date.js'; import getDate from '@/utils/get-date';
import { User } from '@/misskey/user.js'; import { User } from '@/misskey/user';
import { genItem } from '@/vocabulary.js'; import { genItem } from '@/vocabulary';
export type FriendDoc = { export type FriendDoc = {
userId: string; userId: string;
@ -15,7 +15,6 @@ export type FriendDoc = {
perModulesData?: any; perModulesData?: any;
married?: boolean; married?: boolean;
transferCode?: string; transferCode?: string;
reversiStrength?: number | null;
}; };
export default class Friend { export default class Friend {
@ -70,7 +69,7 @@ export default class Friend {
} }
} }
@bindThis @autobind
public updateUser(user: Partial<User>) { public updateUser(user: Partial<User>) {
this.doc.user = { this.doc.user = {
...this.doc.user, ...this.doc.user,
@ -79,7 +78,7 @@ export default class Friend {
this.save(); this.save();
} }
@bindThis @autobind
public getPerModulesData(module: IModule) { public getPerModulesData(module: IModule) {
if (this.doc.perModulesData == null) { if (this.doc.perModulesData == null) {
this.doc.perModulesData = {}; this.doc.perModulesData = {};
@ -93,7 +92,7 @@ export default class Friend {
return this.doc.perModulesData[module.name]; return this.doc.perModulesData[module.name];
} }
@bindThis @autobind
public setPerModulesData(module: IModule, data: any) { public setPerModulesData(module: IModule, data: any) {
if (this.doc.perModulesData == null) { if (this.doc.perModulesData == null) {
this.doc.perModulesData = {}; this.doc.perModulesData = {};
@ -104,7 +103,7 @@ export default class Friend {
this.save(); this.save();
} }
@bindThis @autobind
public incLove(amount = 1) { public incLove(amount = 1) {
const today = getDate(); const today = getDate();
@ -128,7 +127,7 @@ export default class Friend {
this.ai.log(`💗 ${this.userId} +${amount}`); this.ai.log(`💗 ${this.userId} +${amount}`);
} }
@bindThis @autobind
public decLove(amount = 1) { public decLove(amount = 1) {
// 親愛度MAXなら下げない // 親愛度MAXなら下げない
if (this.doc.love === 100) return; if (this.doc.love === 100) return;
@ -149,32 +148,18 @@ export default class Friend {
this.ai.log(`💢 ${this.userId} -${amount}`); this.ai.log(`💢 ${this.userId} -${amount}`);
} }
@bindThis @autobind
public updateName(name: string) { public updateName(name: string) {
this.doc.name = name; this.doc.name = name;
this.save(); this.save();
} }
@bindThis @autobind
public updateReversiStrength(strength: number | null) {
if (strength == null) {
this.doc.reversiStrength = null;
this.save();
return;
}
if (strength < 0) strength = 0;
if (strength > 5) strength = 5;
this.doc.reversiStrength = strength;
this.save();
}
@bindThis
public save() { public save() {
this.ai.friends.update(this.doc); this.ai.friends.update(this.doc);
} }
@bindThis @autobind
public generateTransferCode(): string { public generateTransferCode(): string {
const code = genItem(); const code = genItem();
@ -184,7 +169,7 @@ export default class Friend {
return code; return code;
} }
@bindThis @autobind
public transferMemory(code: string): boolean { public transferMemory(code: string): boolean {
const src = this.ai.friends.findOne({ const src = this.ai.friends.findOne({
transferCode: code transferCode: code

View file

@ -1,38 +1,40 @@
// AiOS bootstrapper // AiOS bootstrapper
import chalk from 'chalk'; import 'module-alias/register';
import got from 'got';
import promiseRetry from 'promise-retry';
import from './ai.js'; import * as chalk from 'chalk';
import config from './config.js'; import * as request from 'request-promise-native';
import _log from './utils/log.js'; const promiseRetry = require('promise-retry');
import pkg from '../package.json' assert { type: 'json' };
import CoreModule from './modules/core/index.js'; import from './ai';
import TalkModule from './modules/talk/index.js'; import config from './config';
import BirthdayModule from './modules/birthday/index.js'; import _log from './utils/log';
import ReversiModule from './modules/reversi/index.js'; const pkg = require('../package.json');
import PingModule from './modules/ping/index.js';
import EmojiModule from './modules/emoji/index.js'; import CoreModule from './modules/core';
import EmojiReactModule from './modules/emoji-react/index.js'; import TalkModule from './modules/talk';
import FortuneModule from './modules/fortune/index.js'; import BirthdayModule from './modules/birthday';
import GuessingGameModule from './modules/guessing-game/index.js'; import ReversiModule from './modules/reversi';
import KazutoriModule from './modules/kazutori/index.js'; import PingModule from './modules/ping';
import KeywordModule from './modules/keyword/index.js'; import EmojiModule from './modules/emoji';
import WelcomeModule from './modules/welcome/index.js'; import EmojiReactModule from './modules/emoji-react';
import TimerModule from './modules/timer/index.js'; import FortuneModule from './modules/fortune';
import DiceModule from './modules/dice/index.js'; import GuessingGameModule from './modules/guessing-game';
import ServerModule from './modules/server/index.js'; import KazutoriModule from './modules/kazutori';
import FollowModule from './modules/follow/index.js'; import KeywordModule from './modules/keyword';
import ValentineModule from './modules/valentine/index.js'; import WelcomeModule from './modules/welcome';
import MazeModule from './modules/maze/index.js'; import TimerModule from './modules/timer';
import ChartModule from './modules/chart/index.js'; import DiceModule from './modules/dice';
import SleepReportModule from './modules/sleep-report/index.js'; import ServerModule from './modules/server';
import NotingModule from './modules/noting/index.js'; import FollowModule from './modules/follow';
import PollModule from './modules/poll/index.js'; import ValentineModule from './modules/valentine';
import ReminderModule from './modules/reminder/index.js'; import MazeModule from './modules/maze';
import CheckCustomEmojisModule from './modules/check-custom-emojis/index.js'; import ChartModule from './modules/chart';
import SleepReportModule from './modules/sleep-report';
import NotingModule from './modules/noting';
import PollModule from './modules/poll';
import ReminderModule from './modules/reminder';
import CheckCustomEmojisModule from './modules/check-custom-emojis';
console.log(' __ ____ _____ ___ '); console.log(' __ ____ _____ ___ ');
console.log(' /__\\ (_ _)( _ )/ __)'); console.log(' /__\\ (_ _)( _ )/ __)');
@ -49,11 +51,11 @@ promiseRetry(retry => {
log(`Account fetching... ${chalk.gray(config.host)}`); log(`Account fetching... ${chalk.gray(config.host)}`);
// アカウントをフェッチ // アカウントをフェッチ
return got.post(`${config.apiUrl}/i`, { return request.post(`${config.apiUrl}/i`, {
json: { json: {
i: config.i i: config.i
} }
}).json().catch(retry); }).catch(retry);
}, { }, {
retries: 3 retries: 3
}).then(account => { }).then(account => {

View file

@ -1,13 +1,13 @@
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import chalk from 'chalk'; import * as chalk from 'chalk';
const delay = require('timeout-as-promise');
import from '@/ai.js'; import from '@/ai';
import Friend from '@/friend.js'; import Friend from '@/friend';
import { User } from '@/misskey/user.js'; import { User } from '@/misskey/user';
import includes from '@/utils/includes.js'; import includes from '@/utils/includes';
import or from '@/utils/or.js'; import or from '@/utils/or';
import config from '@/config.js'; import config from '@/config';
import { sleep } from '@/utils/sleep.js';
export default class Message { export default class Message {
private ai: ; private ai: ;
@ -68,7 +68,7 @@ export default class Message {
}); });
} }
@bindThis @autobind
public async reply(text: string | null, opts?: { public async reply(text: string | null, opts?: {
file?: any; file?: any;
cw?: string; cw?: string;
@ -80,7 +80,7 @@ export default class Message {
this.ai.log(`>>> Sending reply to ${chalk.underline(this.id)}`); this.ai.log(`>>> Sending reply to ${chalk.underline(this.id)}`);
if (!opts?.immediate) { if (!opts?.immediate) {
await sleep(2000); await delay(2000);
} }
return await this.ai.post({ return await this.ai.post({
@ -92,12 +92,12 @@ export default class Message {
}); });
} }
@bindThis @autobind
public includes(words: string[]): boolean { public includes(words: string[]): boolean {
return includes(this.text, words); return includes(this.text, words);
} }
@bindThis @autobind
public or(words: (string | RegExp)[]): boolean { public or(words: (string | RegExp)[]): boolean {
return or(this.text, words); return or(this.text, words);
} }

View file

@ -1,5 +1,5 @@
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import , { InstallerResult } from '@/ai.js'; import , { InstallerResult } from '@/ai';
export default abstract class Module { export default abstract class Module {
public abstract readonly name: string; public abstract readonly name: string;
@ -24,7 +24,7 @@ export default abstract class Module {
public abstract install(): InstallerResult; public abstract install(): InstallerResult;
@bindThis @autobind
protected log(msg: string) { protected log(msg: string) {
this.ai.log(`[${this.name}]: ${msg}`); this.ai.log(`[${this.name}]: ${msg}`);
} }
@ -35,7 +35,7 @@ export default abstract class Module {
* @param id ID稿ID * @param id ID稿ID
* @param data * @param data
*/ */
@bindThis @autobind
protected subscribeReply(key: string | null, id: string, data?: any) { protected subscribeReply(key: string | null, id: string, data?: any) {
this.ai.subscribeReply(this, key, id, data); this.ai.subscribeReply(this, key, id, data);
} }
@ -44,7 +44,7 @@ export default abstract class Module {
* *
* @param key * @param key
*/ */
@bindThis @autobind
protected unsubscribeReply(key: string | null) { protected unsubscribeReply(key: string | null) {
this.ai.unsubscribeReply(this, key); this.ai.unsubscribeReply(this, key);
} }
@ -55,17 +55,17 @@ export default abstract class Module {
* @param delay * @param delay
* @param data * @param data
*/ */
@bindThis @autobind
public setTimeoutWithPersistence(delay: number, data?: any) { public setTimeoutWithPersistence(delay: number, data?: any) {
this.ai.setTimeoutWithPersistence(this, delay, data); this.ai.setTimeoutWithPersistence(this, delay, data);
} }
@bindThis @autobind
protected getData() { protected getData() {
return this.doc.data; return this.doc.data;
} }
@bindThis @autobind
protected setData(data: any) { protected setData(data: any) {
this.doc.data = data; this.doc.data = data;
this.ai.moduleData.update(this.doc); this.ai.moduleData.update(this.doc);

View file

@ -1,7 +1,7 @@
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import Module from '@/module.js'; import Module from '@/module';
import Friend from '@/friend.js'; import Friend from '@/friend';
import serifs from '@/serifs.js'; import serifs from '@/serifs';
function zeroPadding(num: number, length: number): string { function zeroPadding(num: number, length: number): string {
return ('0000000000' + num).slice(-length); return ('0000000000' + num).slice(-length);
@ -10,7 +10,7 @@ function zeroPadding(num: number, length: number): string {
export default class extends Module { export default class extends Module {
public readonly name = 'birthday'; public readonly name = 'birthday';
@bindThis @autobind
public install() { public install() {
this.crawleBirthday(); this.crawleBirthday();
setInterval(this.crawleBirthday, 1000 * 60 * 3); setInterval(this.crawleBirthday, 1000 * 60 * 3);
@ -21,7 +21,7 @@ export default class extends Module {
/** /**
* () * ()
*/ */
@bindThis @autobind
private crawleBirthday() { private crawleBirthday() {
const now = new Date(); const now = new Date();
const m = now.getMonth(); const m = now.getMonth();

View file

@ -1,15 +1,15 @@
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import Module from '@/module.js'; import Module from '@/module';
import serifs from '@/serifs.js'; import serifs from '@/serifs';
import Message from '@/message.js'; import Message from '@/message';
import { renderChart } from './render-chart.js'; import { renderChart } from './render-chart';
import { items } from '@/vocabulary.js'; import { items } from '@/vocabulary';
import config from '@/config.js'; import config from '@/config';
export default class extends Module { export default class extends Module {
public readonly name = 'chart'; public readonly name = 'chart';
@bindThis @autobind
public install() { public install() {
if (config.chartEnabled === false) return {}; if (config.chartEnabled === false) return {};
@ -21,7 +21,7 @@ export default class extends Module {
}; };
} }
@bindThis @autobind
private async post() { private async post() {
const now = new Date(); const now = new Date();
if (now.getHours() !== 23) return; if (now.getHours() !== 23) return;
@ -41,7 +41,7 @@ export default class extends Module {
}); });
} }
@bindThis @autobind
private async genChart(type, params?): Promise<any> { private async genChart(type, params?): Promise<any> {
this.log('Chart data fetching...'); this.log('Chart data fetching...');
@ -134,7 +134,7 @@ export default class extends Module {
return file; return file;
} }
@bindThis @autobind
private async mentionHook(msg: Message) { private async mentionHook(msg: Message) {
if (!msg.includes(['チャート'])) { if (!msg.includes(['チャート'])) {
return false; return false;

View file

@ -1,9 +1,9 @@
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import loki from 'lokijs'; import * as loki from 'lokijs';
import Module from '@/module.js'; import Module from '@/module';
import serifs from '@/serifs.js'; import serifs from '@/serifs';
import config from '@/config.js'; import config from '@/config';
import Message from '@/message.js'; import Message from '@/message';
export default class extends Module { export default class extends Module {
public readonly name = 'checkCustomEmojis'; public readonly name = 'checkCustomEmojis';
@ -13,7 +13,7 @@ export default class extends Module {
updatedAt: number; updatedAt: number;
}>; }>;
@bindThis @autobind
public install() { public install() {
if (!config.checkEmojisEnabled) return {}; if (!config.checkEmojisEnabled) return {};
this.lastEmoji = this.ai.getCollection('lastEmoji', { this.lastEmoji = this.ai.getCollection('lastEmoji', {
@ -28,7 +28,7 @@ export default class extends Module {
}; };
} }
@bindThis @autobind
private timeCheck() { private timeCheck() {
const now = new Date(); const now = new Date();
if (now.getHours() !== 23) return; if (now.getHours() !== 23) return;
@ -42,7 +42,7 @@ export default class extends Module {
this.post(); this.post();
} }
@bindThis @autobind
private async post() { private async post() {
this.log('Start to Check CustomEmojis.'); this.log('Start to Check CustomEmojis.');
const lastEmoji = this.lastEmoji.find({}); const lastEmoji = this.lastEmoji.find({});
@ -99,7 +99,7 @@ export default class extends Module {
this.log('Check CustomEmojis finished!'); this.log('Check CustomEmojis finished!');
} }
@bindThis @autobind
private async checkCumstomEmojis(lastId : any) { private async checkCumstomEmojis(lastId : any) {
this.log('CustomEmojis fetching...'); this.log('CustomEmojis fetching...');
let emojisData; let emojisData;
@ -140,7 +140,7 @@ export default class extends Module {
return emojisData; return emojisData;
} }
@bindThis @autobind
private async mentionHook(msg: Message) { private async mentionHook(msg: Message) {
if (!msg.includes(['カスタムえもじチェック','カスタムえもじを調べて','カスタムえもじを確認'])) { if (!msg.includes(['カスタムえもじチェック','カスタムえもじを調べて','カスタムえもじを確認'])) {
return false; return false;
@ -155,7 +155,7 @@ export default class extends Module {
}; };
} }
@bindThis @autobind
private async sleep(ms: number) { private async sleep(ms: number) {
return new Promise((res) => setTimeout(res, ms)); return new Promise((res) => setTimeout(res, ms));
} }

View file

@ -1,15 +1,15 @@
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import Module from '@/module.js'; import Module from '@/module';
import Message from '@/message.js'; import Message from '@/message';
import serifs from '@/serifs.js'; import serifs from '@/serifs';
import { safeForInterpolate } from '@/utils/safe-for-interpolate.js'; import { safeForInterpolate } from '@/utils/safe-for-interpolate';
const titles = ['さん', 'くん', '君', 'ちゃん', '様', '先生']; const titles = ['さん', 'くん', '君', 'ちゃん', '様', '先生'];
export default class extends Module { export default class extends Module {
public readonly name = 'core'; public readonly name = 'core';
@bindThis @autobind
public install() { public install() {
return { return {
mentionHook: this.mentionHook, mentionHook: this.mentionHook,
@ -17,7 +17,7 @@ export default class extends Module {
}; };
} }
@bindThis @autobind
private async mentionHook(msg: Message) { private async mentionHook(msg: Message) {
if (!msg.text) return false; if (!msg.text) return false;
@ -30,7 +30,7 @@ export default class extends Module {
); );
} }
@bindThis @autobind
private transferBegin(msg: Message): boolean { private transferBegin(msg: Message): boolean {
if (!msg.text) return false; if (!msg.text) return false;
if (!msg.includes(['引継', '引き継ぎ', '引越', '引っ越し'])) return false; if (!msg.includes(['引継', '引き継ぎ', '引越', '引っ越し'])) return false;
@ -42,7 +42,7 @@ export default class extends Module {
return true; return true;
} }
@bindThis @autobind
private transferEnd(msg: Message): boolean { private transferEnd(msg: Message): boolean {
if (!msg.text) return false; if (!msg.text) return false;
if (!msg.text.startsWith('「') || !msg.text.endsWith('」')) return false; if (!msg.text.startsWith('「') || !msg.text.endsWith('」')) return false;
@ -60,13 +60,13 @@ export default class extends Module {
return true; return true;
} }
@bindThis @autobind
private setName(msg: Message): boolean { private setName(msg: Message): boolean {
if (!msg.text) return false; if (!msg.text) return false;
if (!msg.text.includes('って呼んで')) return false; if (!msg.text.includes('って呼んで')) return false;
if (msg.text.startsWith('って呼んで')) return false; if (msg.text.startsWith('って呼んで')) return false;
const name = msg.text.match(/^(.+?)って呼んで/g)![1]; const name = msg.text.match(/^(.+?)って呼んで/)![1];
if (name.length > 10) { if (name.length > 10) {
msg.reply(serifs.core.tooLong); msg.reply(serifs.core.tooLong);
@ -94,7 +94,7 @@ export default class extends Module {
return true; return true;
} }
@bindThis @autobind
private modules(msg: Message): boolean { private modules(msg: Message): boolean {
if (!msg.text) return false; if (!msg.text) return false;
if (!msg.or(['modules'])) return false; if (!msg.or(['modules'])) return false;
@ -114,7 +114,7 @@ export default class extends Module {
return true; return true;
} }
@bindThis @autobind
private version(msg: Message): boolean { private version(msg: Message): boolean {
if (!msg.text) return false; if (!msg.text) return false;
if (!msg.or(['v', 'version', 'バージョン'])) return false; if (!msg.or(['v', 'version', 'バージョン'])) return false;
@ -126,7 +126,7 @@ export default class extends Module {
return true; return true;
} }
@bindThis @autobind
private async contextHook(key: any, msg: Message, data: any) { private async contextHook(key: any, msg: Message, data: any) {
if (msg.text == null) return; if (msg.text == null) return;

View file

@ -1,19 +1,19 @@
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import Module from '@/module.js'; import Module from '@/module';
import Message from '@/message.js'; import Message from '@/message';
import serifs from '@/serifs.js'; import serifs from '@/serifs';
export default class extends Module { export default class extends Module {
public readonly name = 'dice'; public readonly name = 'dice';
@bindThis @autobind
public install() { public install() {
return { return {
mentionHook: this.mentionHook mentionHook: this.mentionHook
}; };
} }
@bindThis @autobind
private async mentionHook(msg: Message) { private async mentionHook(msg: Message) {
if (msg.text == null) return false; if (msg.text == null) return false;

View file

@ -1,18 +1,18 @@
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import { parse } from 'twemoji-parser'; import { parse } from 'twemoji-parser';
const delay = require('timeout-as-promise');
import { Note } from '@/misskey/note.js'; import { Note } from '@/misskey/note';
import Module from '@/module.js'; import Module from '@/module';
import Stream from '@/stream.js'; import Stream from '@/stream';
import includes from '@/utils/includes.js'; import includes from '@/utils/includes';
import { sleep } from '../../utils/sleep.js';
export default class extends Module { export default class extends Module {
public readonly name = 'emoji-react'; public readonly name = 'emoji-react';
private htl: ReturnType<Stream['useSharedConnection']>; private htl: ReturnType<Stream['useSharedConnection']>;
@bindThis @autobind
public install() { public install() {
this.htl = this.ai.connection.useSharedConnection('homeTimeline'); this.htl = this.ai.connection.useSharedConnection('homeTimeline');
this.htl.on('note', this.onNote); this.htl.on('note', this.onNote);
@ -20,7 +20,7 @@ export default class extends Module {
return {}; return {};
} }
@bindThis @autobind
private async onNote(note: Note) { private async onNote(note: Note) {
if (note.reply != null) return; if (note.reply != null) return;
if (note.text == null) return; if (note.text == null) return;
@ -28,7 +28,7 @@ export default class extends Module {
const react = async (reaction: string, immediate = false) => { const react = async (reaction: string, immediate = false) => {
if (!immediate) { if (!immediate) {
await sleep(1500); await delay(1500);
} }
this.ai.api('notes/reactions/create', { this.ai.api('notes/reactions/create', {
noteId: note.id, noteId: note.id,

View file

@ -1,7 +1,7 @@
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import Module from '@/module.js'; import Module from '@/module';
import Message from '@/message.js'; import Message from '@/message';
import serifs from '@/serifs.js'; import serifs from '@/serifs';
const hands = [ const hands = [
'👏', '👏',
@ -129,14 +129,14 @@ const faces = [
export default class extends Module { export default class extends Module {
public readonly name = 'emoji'; public readonly name = 'emoji';
@bindThis @autobind
public install() { public install() {
return { return {
mentionHook: this.mentionHook mentionHook: this.mentionHook
}; };
} }
@bindThis @autobind
private async mentionHook(msg: Message) { private async mentionHook(msg: Message) {
if (msg.includes(['顔文字', '絵文字', 'emoji', '福笑い'])) { if (msg.includes(['顔文字', '絵文字', 'emoji', '福笑い'])) {
const hand = hands[Math.floor(Math.random() * hands.length)]; const hand = hands[Math.floor(Math.random() * hands.length)];

View file

@ -1,18 +1,18 @@
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import Module from '@/module.js'; import Module from '@/module';
import Message from '@/message.js'; import Message from '@/message';
export default class extends Module { export default class extends Module {
public readonly name = 'follow'; public readonly name = 'follow';
@bindThis @autobind
public install() { public install() {
return { return {
mentionHook: this.mentionHook mentionHook: this.mentionHook
}; };
} }
@bindThis @autobind
private async mentionHook(msg: Message) { private async mentionHook(msg: Message) {
if (msg.text && msg.includes(['フォロー', 'フォロバ', 'follow me'])) { if (msg.text && msg.includes(['フォロー', 'フォロバ', 'follow me'])) {
if (!msg.user.isFollowing) { if (!msg.user.isFollowing) {

View file

@ -1,9 +1,9 @@
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import Module from '@/module.js'; import Module from '@/module';
import Message from '@/message.js'; import Message from '@/message';
import serifs from '@/serifs.js'; import serifs from '@/serifs';
import seedrandom from 'seedrandom'; import * as seedrandom from 'seedrandom';
import { genItem } from '@/vocabulary.js'; import { genItem } from '@/vocabulary';
export const blessing = [ export const blessing = [
'藍吉', '藍吉',
@ -40,14 +40,14 @@ export const blessing = [
export default class extends Module { export default class extends Module {
public readonly name = 'fortune'; public readonly name = 'fortune';
@bindThis @autobind
public install() { public install() {
return { return {
mentionHook: this.mentionHook mentionHook: this.mentionHook
}; };
} }
@bindThis @autobind
private async mentionHook(msg: Message) { private async mentionHook(msg: Message) {
if (msg.includes(['占', 'うらな', '運勢', 'おみくじ'])) { if (msg.includes(['占', 'うらな', '運勢', 'おみくじ'])) {
const date = new Date(); const date = new Date();

View file

@ -1,8 +1,8 @@
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import loki from 'lokijs'; import * as loki from 'lokijs';
import Module from '@/module.js'; import Module from '@/module';
import Message from '@/message.js'; import Message from '@/message';
import serifs from '@/serifs.js'; import serifs from '@/serifs';
export default class extends Module { export default class extends Module {
public readonly name = 'guessingGame'; public readonly name = 'guessingGame';
@ -16,7 +16,7 @@ export default class extends Module {
endedAt: number | null; endedAt: number | null;
}>; }>;
@bindThis @autobind
public install() { public install() {
this.guesses = this.ai.getCollection('guessingGame', { this.guesses = this.ai.getCollection('guessingGame', {
indices: ['userId'] indices: ['userId']
@ -28,7 +28,7 @@ export default class extends Module {
}; };
} }
@bindThis @autobind
private async mentionHook(msg: Message) { private async mentionHook(msg: Message) {
if (!msg.includes(['数当て', '数あて'])) return false; if (!msg.includes(['数当て', '数あて'])) return false;
@ -55,7 +55,7 @@ export default class extends Module {
return true; return true;
} }
@bindThis @autobind
private async contextHook(key: any, msg: Message) { private async contextHook(key: any, msg: Message) {
if (msg.text == null) return; if (msg.text == null) return;

View file

@ -1,10 +1,10 @@
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import loki from 'lokijs'; import * as loki from 'lokijs';
import Module from '@/module.js'; import Module from '@/module';
import Message from '@/message.js'; import Message from '@/message';
import serifs from '@/serifs.js'; import serifs from '@/serifs';
import { User } from '@/misskey/user.js'; import { User } from '@/misskey/user';
import { acct } from '@/utils/acct.js'; import { acct } from '@/utils/acct';
type Game = { type Game = {
votes: { votes: {
@ -27,7 +27,7 @@ export default class extends Module {
private games: loki.Collection<Game>; private games: loki.Collection<Game>;
@bindThis @autobind
public install() { public install() {
this.games = this.ai.getCollection('kazutori'); this.games = this.ai.getCollection('kazutori');
@ -40,7 +40,7 @@ export default class extends Module {
}; };
} }
@bindThis @autobind
private async mentionHook(msg: Message) { private async mentionHook(msg: Message) {
if (!msg.includes(['数取り'])) return false; if (!msg.includes(['数取り'])) return false;
@ -82,7 +82,7 @@ export default class extends Module {
return true; return true;
} }
@bindThis @autobind
private async contextHook(key: any, msg: Message) { private async contextHook(key: any, msg: Message) {
if (msg.text == null) return { if (msg.text == null) return {
reaction: 'hmm' reaction: 'hmm'
@ -139,7 +139,7 @@ export default class extends Module {
/** /**
* *
*/ */
@bindThis @autobind
private crawleGameEnd() { private crawleGameEnd() {
const game = this.games.findOne({ const game = this.games.findOne({
isEnded: false isEnded: false
@ -156,7 +156,7 @@ export default class extends Module {
/** /**
* *
*/ */
@bindThis @autobind
private finish(game: Game) { private finish(game: Game) {
game.isEnded = true; game.isEnded = true;
this.games.update(game); this.games.update(game);

View file

@ -1,9 +1,9 @@
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import loki from 'lokijs'; import * as loki from 'lokijs';
import Module from '@/module.js'; import Module from '@/module';
import config from '@/config.js'; import config from '@/config';
import serifs from '@/serifs.js'; import serifs from '@/serifs';
import { mecab } from './mecab.js'; import { mecab } from './mecab';
function kanaToHira(str: string) { function kanaToHira(str: string) {
return str.replace(/[\u30a1-\u30f6]/g, match => { return str.replace(/[\u30a1-\u30f6]/g, match => {
@ -20,7 +20,7 @@ export default class extends Module {
learnedAt: number; learnedAt: number;
}>; }>;
@bindThis @autobind
public install() { public install() {
if (!config.keywordEnabled) return {}; if (!config.keywordEnabled) return {};
@ -33,7 +33,7 @@ export default class extends Module {
return {}; return {};
} }
@bindThis @autobind
private async learn() { private async learn() {
const tl = await this.ai.api('notes/local-timeline', { const tl = await this.ai.api('notes/local-timeline', {
limit: 30 limit: 30

View file

@ -1,5 +1,5 @@
import gen from 'random-seed'; import * as gen from 'random-seed';
import { CellType } from './maze.js'; import { CellType } from './maze';
const cellVariants = { const cellVariants = {
void: { void: {

View file

@ -1,14 +1,14 @@
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import Module from '@/module.js'; import Module from '@/module';
import serifs from '@/serifs.js'; import serifs from '@/serifs';
import { genMaze } from './gen-maze.js'; import { genMaze } from './gen-maze';
import { renderMaze } from './render-maze.js'; import { renderMaze } from './render-maze';
import Message from '@/message.js'; import Message from '@/message';
export default class extends Module { export default class extends Module {
public readonly name = 'maze'; public readonly name = 'maze';
@bindThis @autobind
public install() { public install() {
this.post(); this.post();
setInterval(this.post, 1000 * 60 * 3); setInterval(this.post, 1000 * 60 * 3);
@ -18,7 +18,7 @@ export default class extends Module {
}; };
} }
@bindThis @autobind
private async post() { private async post() {
const now = new Date(); const now = new Date();
if (now.getHours() !== 22) return; if (now.getHours() !== 22) return;
@ -38,7 +38,7 @@ export default class extends Module {
}); });
} }
@bindThis @autobind
private async genMazeFile(seed, size?): Promise<any> { private async genMazeFile(seed, size?): Promise<any> {
this.log('Maze generating...'); this.log('Maze generating...');
const maze = genMaze(seed, size); const maze = genMaze(seed, size);
@ -55,7 +55,7 @@ export default class extends Module {
return file; return file;
} }
@bindThis @autobind
private async mentionHook(msg: Message) { private async mentionHook(msg: Message) {
if (msg.includes(['迷路'])) { if (msg.includes(['迷路'])) {
let size: string | null = null; let size: string | null = null;

View file

@ -1,8 +1,8 @@
import gen from 'random-seed'; import * as gen from 'random-seed';
import { createCanvas } from 'canvas'; import { createCanvas } from 'canvas';
import { CellType } from './maze.js'; import { CellType } from './maze';
import { themes } from './themes.js'; import { themes } from './themes';
const imageSize = 4096; // px const imageSize = 4096; // px
const margin = 96 * 4; const margin = 96 * 4;

View file

@ -1,13 +1,13 @@
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import Module from '@/module.js'; import Module from '@/module';
import serifs from '@/serifs.js'; import serifs from '@/serifs';
import { genItem } from '@/vocabulary.js'; import { genItem } from '@/vocabulary';
import config from '@/config.js'; import config from '@/config';
export default class extends Module { export default class extends Module {
public readonly name = 'noting'; public readonly name = 'noting';
@bindThis @autobind
public install() { public install() {
if (config.notingEnabled === false) return {}; if (config.notingEnabled === false) return {};
@ -20,7 +20,7 @@ export default class extends Module {
return {}; return {};
} }
@bindThis @autobind
private post() { private post() {
const notes = [ const notes = [
...serifs.noting.notes, ...serifs.noting.notes,

View file

@ -1,18 +1,18 @@
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import Module from '@/module.js'; import Module from '@/module';
import Message from '@/message.js'; import Message from '@/message';
export default class extends Module { export default class extends Module {
public readonly name = 'ping'; public readonly name = 'ping';
@bindThis @autobind
public install() { public install() {
return { return {
mentionHook: this.mentionHook mentionHook: this.mentionHook
}; };
} }
@bindThis @autobind
private async mentionHook(msg: Message) { private async mentionHook(msg: Message) {
if (msg.text && msg.text.includes('ping')) { if (msg.text && msg.text.includes('ping')) {
msg.reply('PONG!', { msg.reply('PONG!', {

View file

@ -1,15 +1,15 @@
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import Message from '@/message.js'; import Message from '@/message';
import Module from '@/module.js'; import Module from '@/module';
import serifs from '@/serifs.js'; import serifs from '@/serifs';
import { genItem } from '@/vocabulary.js'; import { genItem } from '@/vocabulary';
import config from '@/config.js'; import config from '@/config';
import { Note } from '@/misskey/note.js'; import { Note } from '@/misskey/note';
export default class extends Module { export default class extends Module {
public readonly name = 'poll'; public readonly name = 'poll';
@bindThis @autobind
public install() { public install() {
setInterval(() => { setInterval(() => {
if (Math.random() < 0.1) { if (Math.random() < 0.1) {
@ -23,7 +23,7 @@ export default class extends Module {
}; };
} }
@bindThis @autobind
private async post() { private async post() {
const duration = 1000 * 60 * 15; const duration = 1000 * 60 * 15;
@ -89,7 +89,7 @@ export default class extends Module {
}); });
} }
@bindThis @autobind
private async mentionHook(msg: Message) { private async mentionHook(msg: Message) {
if (!msg.or(['/poll']) || msg.user.username !== config.master) { if (!msg.or(['/poll']) || msg.user.username !== config.master) {
return false; return false;
@ -102,7 +102,7 @@ export default class extends Module {
return true; return true;
} }
@bindThis @autobind
private async timeoutCallback({ title, noteId }) { private async timeoutCallback({ title, noteId }) {
const note: Note = await this.ai.api('notes/show', { noteId }); const note: Note = await this.ai.api('notes/show', { noteId });

View file

@ -1,10 +1,10 @@
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import loki from 'lokijs'; import * as loki from 'lokijs';
import Module from '@/module.js'; import Module from '@/module';
import Message from '@/message.js'; import Message from '@/message';
import serifs, { getSerif } from '@/serifs.js'; import serifs, { getSerif } from '@/serifs';
import { acct } from '@/utils/acct.js'; import { acct } from '@/utils/acct';
import config from '@/config.js'; import config from '@/config';
const NOTIFY_INTERVAL = 1000 * 60 * 60 * 12; const NOTIFY_INTERVAL = 1000 * 60 * 60 * 12;
@ -20,7 +20,7 @@ export default class extends Module {
createdAt: number; createdAt: number;
}>; }>;
@bindThis @autobind
public install() { public install() {
this.reminds = this.ai.getCollection('reminds', { this.reminds = this.ai.getCollection('reminds', {
indices: ['userId', 'id'] indices: ['userId', 'id']
@ -33,7 +33,7 @@ export default class extends Module {
}; };
} }
@bindThis @autobind
private async mentionHook(msg: Message) { private async mentionHook(msg: Message) {
let text = msg.extractedText.toLowerCase(); let text = msg.extractedText.toLowerCase();
if (!text.startsWith('remind') && !text.startsWith('todo')) return false; if (!text.startsWith('remind') && !text.startsWith('todo')) return false;
@ -98,7 +98,7 @@ export default class extends Module {
}; };
} }
@bindThis @autobind
private async contextHook(key: any, msg: Message, data: any) { private async contextHook(key: any, msg: Message, data: any) {
if (msg.text == null) return; if (msg.text == null) return;
@ -128,7 +128,7 @@ export default class extends Module {
} }
} }
@bindThis @autobind
private async timeoutCallback(data) { private async timeoutCallback(data) {
const remind = this.reminds.findOne({ const remind = this.reminds.findOne({
id: data.id id: data.id

View file

@ -6,11 +6,13 @@
* *
*/ */
import got from 'got'; import 'module-alias/register';
import * as Reversi from './engine.js';
import config from '@/config.js'; import * as request from 'request-promise-native';
import serifs from '@/serifs.js'; import Reversi, { Color } from 'misskey-reversi';
import { User } from '@/misskey/user.js'; import config from '@/config';
import serifs from '@/serifs';
import { User } from '@/misskey/user';
function getUserName(user) { function getUserName(user) {
return user.name || user.username; return user.name || user.username;
@ -27,10 +29,8 @@ class Session {
private account: User; private account: User;
private game: any; private game: any;
private form: any; private form: any;
private engine: Reversi.Game; private o: Reversi;
private botColor: Reversi.Color; private botColor: Color;
private appliedOps: string[] = [];
/** /**
* () * ()
@ -79,7 +79,7 @@ class Session {
} }
private get url(): string { private get url(): string {
return `${config.host}/reversi/g/${this.game.id}`; return `${config.host}/games/reversi/${this.game.id}`;
} }
constructor() { constructor() {
@ -89,9 +89,10 @@ class Session {
private onMessage = async (msg: any) => { private onMessage = async (msg: any) => {
switch (msg.type) { switch (msg.type) {
case '_init_': this.onInit(msg.body); break; case '_init_': this.onInit(msg.body); break;
case 'updateForm': this.onUpdateForn(msg.body); break;
case 'started': this.onStarted(msg.body); break; case 'started': this.onStarted(msg.body); break;
case 'ended': this.onEnded(msg.body); break; case 'ended': this.onEnded(msg.body); break;
case 'log': this.onLog(msg.body); break; case 'set': this.onSet(msg.body); break;
} }
} }
@ -102,17 +103,18 @@ class Session {
this.account = msg.account; this.account = msg.account;
} }
/**
*
*/
private onUpdateForn = (msg: any) => {
this.form.find(i => i.id == msg.id).value = msg.value;
}
/** /**
* *
*/ */
private onStarted = (msg: any) => { private onStarted = (msg: any) => {
this.game = msg.game; this.game = msg;
if (this.game.canPutEverywhere) { // 対応してない
process.send!({
type: 'ended'
});
process.exit();
}
// TLに投稿する // TLに投稿する
this.postGameStarted().then(note => { this.postGameStarted().then(note => {
@ -120,24 +122,24 @@ class Session {
}); });
// リバーシエンジン初期化 // リバーシエンジン初期化
this.engine = new Reversi.Game(this.game.map, { this.o = new Reversi(this.game.map, {
isLlotheo: this.game.isLlotheo, isLlotheo: this.game.isLlotheo,
canPutEverywhere: this.game.canPutEverywhere, canPutEverywhere: this.game.canPutEverywhere,
loopedBoard: this.game.loopedBoard loopedBoard: this.game.loopedBoard
}); });
this.maxTurn = this.engine.map.filter(p => p === 'empty').length - this.engine.board.filter(x => x != null).length; this.maxTurn = this.o.map.filter(p => p === 'empty').length - this.o.board.filter(x => x != null).length;
//#region 隅の位置計算など //#region 隅の位置計算など
//#region 隅 //#region 隅
this.engine.map.forEach((pix, i) => { this.o.map.forEach((pix, i) => {
if (pix == 'null') return; if (pix == 'null') return;
const [x, y] = this.engine.posToXy(i); const [x, y] = this.o.transformPosToXy(i);
const get = (x, y) => { const get = (x, y) => {
if (x < 0 || y < 0 || x >= this.engine.mapWidth || y >= this.engine.mapHeight) return 'null'; if (x < 0 || y < 0 || x >= this.o.mapWidth || y >= this.o.mapHeight) return 'null';
return this.engine.mapDataGet(this.engine.xyToPos(x, y)); return this.o.mapDataGet(this.o.transformXyToPos(x, y));
}; };
const isNotSumi = ( const isNotSumi = (
@ -169,15 +171,15 @@ class Session {
//#endregion //#endregion
//#region 隅の隣 //#region 隅の隣
this.engine.map.forEach((pix, i) => { this.o.map.forEach((pix, i) => {
if (pix == 'null') return; if (pix == 'null') return;
if (this.sumiIndexes.includes(i)) return; if (this.sumiIndexes.includes(i)) return;
const [x, y] = this.engine.posToXy(i); const [x, y] = this.o.transformPosToXy(i);
const check = (x, y) => { const check = (x, y) => {
if (x < 0 || y < 0 || x >= this.engine.mapWidth || y >= this.engine.mapHeight) return 0; if (x < 0 || y < 0 || x >= this.o.mapWidth || y >= this.o.mapHeight) return 0;
return this.sumiIndexes.includes(this.engine.xyToPos(x, y)); return this.sumiIndexes.includes(this.o.transformXyToPos(x, y));
}; };
const isSumiNear = ( const isSumiNear = (
@ -251,23 +253,13 @@ class Session {
/** /**
* *
*/ */
private onLog = (log: any) => { private onSet = (msg: any) => {
if (log.id == null || !this.appliedOps.includes(log.id)) { this.o.put(msg.color, msg.pos);
switch (log.operation) {
case 'put': {
this.engine.putStone(log.pos);
this.currentTurn++; this.currentTurn++;
if (this.engine.turn === this.botColor) { if (msg.next === this.botColor) {
this.think(); this.think();
} }
break;
}
default:
break;
}
}
} }
/** /**
@ -276,10 +268,10 @@ class Session {
* TODO: 接待時はまるっと処理の中身を変え * TODO: 接待時はまるっと処理の中身を変え
*/ */
private staticEval = () => { private staticEval = () => {
let score = this.engine.getPuttablePlaces(this.botColor).length; let score = this.o.canPutSomewhere(this.botColor).length;
for (const index of this.sumiIndexes) { for (const index of this.sumiIndexes) {
const stone = this.engine.board[index]; const stone = this.o.board[index];
if (stone === this.botColor) { if (stone === this.botColor) {
score += 1000; // 自分が隅を取っていたらスコアプラス score += 1000; // 自分が隅を取っていたらスコアプラス
@ -291,7 +283,7 @@ class Session {
// TODO: ここに (隅以外の確定石の数 * 100) をスコアに加算する処理を入れる // TODO: ここに (隅以外の確定石の数 * 100) をスコアに加算する処理を入れる
for (const index of this.sumiNearIndexes) { for (const index of this.sumiNearIndexes) {
const stone = this.engine.board[index]; const stone = this.o.board[index];
if (stone === this.botColor) { if (stone === this.botColor) {
score -= 10; // 自分が隅の周辺を取っていたらスコアマイナス(危険なので) score -= 10; // 自分が隅の周辺を取っていたらスコアマイナス(危険なので)
@ -327,13 +319,13 @@ class Session {
*/ */
const dive = (pos: number, alpha = -Infinity, beta = Infinity, depth = 0): number => { const dive = (pos: number, alpha = -Infinity, beta = Infinity, depth = 0): number => {
// 試し打ち // 試し打ち
this.engine.putStone(pos); this.o.put(this.o.turn, pos);
const isBotTurn = this.engine.turn === this.botColor; const isBotTurn = this.o.turn === this.botColor;
// 勝った // 勝った
if (this.engine.turn === null) { if (this.o.turn === null) {
const winner = this.engine.winner; const winner = this.o.winner;
// 勝つことによる基本スコア // 勝つことによる基本スコア
const base = 10000; const base = 10000;
@ -342,14 +334,14 @@ class Session {
if (this.game.isLlotheo) { if (this.game.isLlotheo) {
// 勝ちは勝ちでも、より自分の石を少なくした方が美しい勝ちだと判定する // 勝ちは勝ちでも、より自分の石を少なくした方が美しい勝ちだと判定する
score = this.engine.winner ? base - (this.engine.blackCount * 100) : base - (this.engine.whiteCount * 100); score = this.o.winner ? base - (this.o.blackCount * 100) : base - (this.o.whiteCount * 100);
} else { } else {
// 勝ちは勝ちでも、より相手の石を少なくした方が美しい勝ちだと判定する // 勝ちは勝ちでも、より相手の石を少なくした方が美しい勝ちだと判定する
score = this.engine.winner ? base + (this.engine.blackCount * 100) : base + (this.engine.whiteCount * 100); score = this.o.winner ? base + (this.o.blackCount * 100) : base + (this.o.whiteCount * 100);
} }
// 巻き戻し // 巻き戻し
this.engine.undo(); this.o.undo();
// 接待なら自分が負けた方が高スコア // 接待なら自分が負けた方が高スコア
return this.isSettai return this.isSettai
@ -362,11 +354,11 @@ class Session {
const score = this.staticEval(); const score = this.staticEval();
// 巻き戻し // 巻き戻し
this.engine.undo(); this.o.undo();
return score; return score;
} else { } else {
const cans = this.engine.getPuttablePlaces(this.engine.turn); const cans = this.o.canPutSomewhere(this.o.turn);
let value = isBotTurn ? -Infinity : Infinity; let value = isBotTurn ? -Infinity : Infinity;
let a = alpha; let a = alpha;
@ -392,34 +384,24 @@ class Session {
} }
// 巻き戻し // 巻き戻し
this.engine.undo(); this.o.undo();
return value; return value;
} }
}; };
const cans = this.engine.getPuttablePlaces(this.botColor); const cans = this.o.canPutSomewhere(this.botColor);
const scores = cans.map(p => dive(p)); const scores = cans.map(p => dive(p));
const pos = cans[scores.indexOf(Math.max(...scores))]; const pos = cans[scores.indexOf(Math.max(...scores))];
console.log('Thinked:', pos); console.log('Thinked:', pos);
console.timeEnd('think'); console.timeEnd('think');
this.engine.putStone(pos);
this.currentTurn++;
setTimeout(() => { setTimeout(() => {
const id = Math.random().toString(36).slice(2);
process.send!({ process.send!({
type: 'putStone', type: 'put',
pos, pos
id
}); });
this.appliedOps.push(id);
if (this.engine.turn === this.botColor) {
this.think();
}
}, 500); }, 500);
} }
@ -451,9 +433,9 @@ class Session {
} }
try { try {
const res = await got.post(`${config.host}/api/notes/create`, { const res = await request.post(`${config.host}/api/notes/create`, {
json: body json: body
}).json(); });
return res.createdNote; return res.createdNote;
} catch (e) { } catch (e) {

View file

@ -1,212 +0,0 @@
/**
* true ...
* false ...
*/
export type Color = boolean;
const BLACK = true;
const WHITE = false;
export type MapCell = 'null' | 'empty';
export type Options = {
isLlotheo: boolean;
canPutEverywhere: boolean;
loopedBoard: boolean;
};
export type Undo = {
color: Color;
pos: number;
/**
*
*/
effects: number[];
turn: Color | null;
};
export class Game {
public map: MapCell[];
public mapWidth: number;
public mapHeight: number;
public board: (Color | null | undefined)[];
public turn: Color | null = BLACK;
public opts: Options;
public prevPos = -1;
public prevColor: Color | null = null;
private logs: Undo[] = [];
constructor(map: string[], opts: Options) {
//#region binds
this.putStone = this.putStone.bind(this);
//#endregion
//#region Options
this.opts = opts;
if (this.opts.isLlotheo == null) this.opts.isLlotheo = false;
if (this.opts.canPutEverywhere == null) this.opts.canPutEverywhere = false;
if (this.opts.loopedBoard == null) this.opts.loopedBoard = false;
//#endregion
//#region Parse map data
this.mapWidth = map[0].length;
this.mapHeight = map.length;
const mapData = map.join('');
this.board = mapData.split('').map(d => d === '-' ? null : d === 'b' ? BLACK : d === 'w' ? WHITE : undefined);
this.map = mapData.split('').map(d => d === '-' || d === 'b' || d === 'w' ? 'empty' : 'null');
//#endregion
// ゲームが始まった時点で片方の色の石しかないか、始まった時点で勝敗が決定するようなマップの場合がある
if (!this.canPutSomewhere(BLACK)) this.turn = this.canPutSomewhere(WHITE) ? WHITE : null;
}
public get blackCount() {
return this.board.filter(x => x === BLACK).length;
}
public get whiteCount() {
return this.board.filter(x => x === WHITE).length;
}
public posToXy(pos: number): number[] {
const x = pos % this.mapWidth;
const y = Math.floor(pos / this.mapWidth);
return [x, y];
}
public xyToPos(x: number, y: number): number {
return x + (y * this.mapWidth);
}
public putStone(pos: number) {
const color = this.turn;
if (color == null) return;
this.prevPos = pos;
this.prevColor = color;
this.board[pos] = color;
// 反転させられる石を取得
const effects = this.effects(color, pos);
// 反転させる
for (const pos of effects) {
this.board[pos] = color;
}
const turn = this.turn;
this.logs.push({
color,
pos,
effects,
turn,
});
this.calcTurn();
}
private calcTurn() {
// ターン計算
this.turn =
this.canPutSomewhere(!this.prevColor) ? !this.prevColor :
this.canPutSomewhere(this.prevColor!) ? this.prevColor :
null;
}
public undo() {
const undo = this.logs.pop()!;
this.prevColor = undo.color;
this.prevPos = undo.pos;
this.board[undo.pos] = null;
for (const pos of undo.effects) {
const color = this.board[pos];
this.board[pos] = !color;
}
this.turn = undo.turn;
}
public mapDataGet(pos: number): MapCell {
const [x, y] = this.posToXy(pos);
return x < 0 || y < 0 || x >= this.mapWidth || y >= this.mapHeight ? 'null' : this.map[pos];
}
public getPuttablePlaces(color: Color): number[] {
return Array.from(this.board.keys()).filter(i => this.canPut(color, i));
}
public canPutSomewhere(color: Color): boolean {
return this.getPuttablePlaces(color).length > 0;
}
public canPut(color: Color, pos: number): boolean {
return (
this.board[pos] !== null ? false : // 既に石が置いてある場所には打てない
this.opts.canPutEverywhere ? this.mapDataGet(pos) === 'empty' : // 挟んでなくても置けるモード
this.effects(color, pos).length !== 0); // 相手の石を1つでも反転させられるか
}
/**
*
* @param color
* @param initPos
*/
public effects(color: Color, initPos: number): number[] {
const enemyColor = !color;
const diffVectors: [number, number][] = [
[0, -1], // 上
[+1, -1], // 右上
[+1, 0], // 右
[+1, +1], // 右下
[0, +1], // 下
[-1, +1], // 左下
[-1, 0], // 左
[-1, -1], // 左上
];
const effectsInLine = ([dx, dy]: [number, number]): number[] => {
const nextPos = (x: number, y: number): [number, number] => [x + dx, y + dy];
const found: number[] = []; // 挟めるかもしれない相手の石を入れておく配列
let [x, y] = this.posToXy(initPos);
while (true) {
[x, y] = nextPos(x, y);
// 座標が指し示す位置がボード外に出たとき
if (this.opts.loopedBoard && this.xyToPos(
(x = ((x % this.mapWidth) + this.mapWidth) % this.mapWidth),
(y = ((y % this.mapHeight) + this.mapHeight) % this.mapHeight)) === initPos) {
// 盤面の境界でループし、自分が石を置く位置に戻ってきたとき、挟めるようにしている (ref: Test4のマップ)
return found;
} else if (x === -1 || y === -1 || x === this.mapWidth || y === this.mapHeight) return []; // 挟めないことが確定 (盤面外に到達)
const pos = this.xyToPos(x, y);
if (this.mapDataGet(pos) === 'null') return []; // 挟めないことが確定 (配置不可能なマスに到達)
const stone = this.board[pos];
if (stone === null) return []; // 挟めないことが確定 (石が置かれていないマスに到達)
if (stone === enemyColor) found.push(pos); // 挟めるかもしれない (相手の石を発見)
if (stone === color) return found; // 挟めることが確定 (対となる自分の石を発見)
}
};
return ([] as number[]).concat(...diffVectors.map(effectsInLine));
}
public get isEnded(): boolean {
return this.turn === null;
}
public get winner(): Color | null {
return this.isEnded ?
this.blackCount === this.whiteCount ? null :
this.opts.isLlotheo === this.blackCount > this.whiteCount ? WHITE : BLACK :
undefined as never;
}
}

View file

@ -1,16 +1,11 @@
import * as childProcess from 'child_process'; import * as childProcess from 'child_process';
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import Module from '@/module.js'; import Module from '@/module';
import serifs from '@/serifs.js'; import serifs from '@/serifs';
import config from '@/config.js'; import config from '@/config';
import Message from '@/message.js'; import Message from '@/message';
import Friend from '@/friend.js'; import Friend from '@/friend';
import getDate from '@/utils/get-date.js'; import getDate from '@/utils/get-date';
import { fileURLToPath } from 'node:url';
import { dirname, resolve } from 'node:path';
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
export default class extends Module { export default class extends Module {
public readonly name = 'reversi'; public readonly name = 'reversi';
@ -20,17 +15,17 @@ export default class extends Module {
*/ */
private reversiConnection?: any; private reversiConnection?: any;
@bindThis @autobind
public install() { public install() {
if (!config.reversiEnabled) return {}; if (!config.reversiEnabled) return {};
this.reversiConnection = this.ai.connection.useSharedConnection('reversi'); this.reversiConnection = this.ai.connection.useSharedConnection('gamesReversi');
// 招待されたとき // 招待されたとき
this.reversiConnection.on('invited', msg => this.onReversiInviteMe(msg.user)); this.reversiConnection.on('invited', msg => this.onReversiInviteMe(msg.parent));
// マッチしたとき // マッチしたとき
this.reversiConnection.on('matched', msg => this.onReversiGameStart(msg.game)); this.reversiConnection.on('matched', msg => this.onReversiGameStart(msg));
if (config.reversiEnabled) { if (config.reversiEnabled) {
const mainStream = this.ai.connection.useSharedConnection('main'); const mainStream = this.ai.connection.useSharedConnection('main');
@ -48,17 +43,13 @@ export default class extends Module {
}; };
} }
@bindThis @autobind
private async mentionHook(msg: Message) { private async mentionHook(msg: Message) {
if (msg.includes(['リバーシ', 'オセロ', 'reversi', 'othello'])) { if (msg.includes(['リバーシ', 'オセロ', 'reversi', 'othello'])) {
if (config.reversiEnabled) { if (config.reversiEnabled) {
msg.reply(serifs.reversi.ok); msg.reply(serifs.reversi.ok);
if (msg.includes(['接待'])) { this.ai.api('games/reversi/match', {
msg.friend.updateReversiStrength(0);
}
this.ai.api('reversi/match', {
userId: msg.userId userId: msg.userId
}); });
} else { } else {
@ -71,13 +62,13 @@ export default class extends Module {
} }
} }
@bindThis @autobind
private async onReversiInviteMe(inviter: any) { private async onReversiInviteMe(inviter: any) {
this.log(`Someone invited me: @${inviter.username}`); this.log(`Someone invited me: @${inviter.username}`);
if (config.reversiEnabled) { if (config.reversiEnabled) {
// 承認 // 承認
const game = await this.ai.api('reversi/match', { const game = await this.ai.api('games/reversi/match', {
userId: inviter.id userId: inviter.id
}); });
@ -87,19 +78,12 @@ export default class extends Module {
} }
} }
@bindThis @autobind
private onReversiGameStart(game: any) { private onReversiGameStart(game: any) {
let strength = 4; this.log('enter reversi game room');
const friend = this.ai.lookupFriend(game.user1Id !== this.ai.account.id ? game.user1Id : game.user2Id)!;
if (friend != null) {
strength = friend.doc.reversiStrength ?? 4;
friend.updateReversiStrength(null);
}
this.log(`enter reversi game room: ${game.id}`);
// ゲームストリームに接続 // ゲームストリームに接続
const gw = this.ai.connection.connectToChannel('reversiGame', { const gw = this.ai.connection.connectToChannel('gamesReversiGame', {
gameId: game.id gameId: game.id
}); });
@ -108,12 +92,12 @@ export default class extends Module {
id: 'publish', id: 'publish',
type: 'switch', type: 'switch',
label: '藍が対局情報を投稿するのを許可', label: '藍が対局情報を投稿するのを許可',
value: true, value: true
}, { }, {
id: 'strength', id: 'strength',
type: 'radio', type: 'radio',
label: '強さ', label: '強さ',
value: strength, value: 3,
items: [{ items: [{
label: '接待', label: '接待',
value: 0 value: 0
@ -133,7 +117,7 @@ export default class extends Module {
}]; }];
//#region バックエンドプロセス開始 //#region バックエンドプロセス開始
const ai = childProcess.fork(_dirname + '/back.js'); const ai = childProcess.fork(__dirname + '/back.js');
// バックエンドプロセスに情報を渡す // バックエンドプロセスに情報を渡す
ai.send({ ai.send({
@ -146,10 +130,9 @@ export default class extends Module {
}); });
ai.on('message', (msg: Record<string, any>) => { ai.on('message', (msg: Record<string, any>) => {
if (msg.type == 'putStone') { if (msg.type == 'put') {
gw.send('putStone', { gw.send('set', {
pos: msg.pos, pos: msg.pos
id: msg.id,
}); });
} else if (msg.type == 'ended') { } else if (msg.type == 'ended') {
gw.dispose(); gw.dispose();
@ -161,26 +144,21 @@ export default class extends Module {
// ゲームストリームから情報が流れてきたらそのままバックエンドプロセスに伝える // ゲームストリームから情報が流れてきたらそのままバックエンドプロセスに伝える
gw.addListener('*', message => { gw.addListener('*', message => {
ai.send(message); ai.send(message);
if (message.type === 'updateSettings') {
if (message.body.key === 'canPutEverywhere') {
if (message.body.value === true) {
gw.send('ready', false);
} else {
gw.send('ready', true);
}
}
}
}); });
//#endregion //#endregion
// フォーム初期化
setTimeout(() => {
gw.send('initForm', form);
}, 1000);
// どんな設定内容の対局でも受け入れる // どんな設定内容の対局でも受け入れる
setTimeout(() => { setTimeout(() => {
gw.send('ready', true); gw.send('accept', {});
}, 1000); }, 2000);
} }
@bindThis @autobind
private onGameEnded(game: any) { private onGameEnded(game: any) {
const user = game.user1Id == this.ai.account.id ? game.user2 : game.user1; const user = game.user1Id == this.ai.account.id ? game.user2 : game.user1;

View file

@ -1,7 +1,7 @@
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import Module from '@/module.js'; import Module from '@/module';
import serifs from '@/serifs.js'; import serifs from '@/serifs';
import config from '@/config.js'; import config from '@/config';
export default class extends Module { export default class extends Module {
public readonly name = 'server'; public readonly name = 'server';
@ -16,7 +16,7 @@ export default class extends Module {
*/ */
private statsLogs: any[] = []; private statsLogs: any[] = [];
@bindThis @autobind
public install() { public install() {
if (!config.serverMonitoring) return {}; if (!config.serverMonitoring) return {};
@ -35,7 +35,7 @@ export default class extends Module {
return {}; return {};
} }
@bindThis @autobind
private check() { private check() {
const average = (arr) => arr.reduce((a, b) => a + b) / arr.length; const average = (arr) => arr.reduce((a, b) => a + b) / arr.length;
@ -48,12 +48,12 @@ export default class extends Module {
} }
} }
@bindThis @autobind
private async onStats(stats: any) { private async onStats(stats: any) {
this.recentStat = stats; this.recentStat = stats;
} }
@bindThis @autobind
private warn() { private warn() {
//#region 前に警告したときから一旦落ち着いた状態を経験していなければ警告しない //#region 前に警告したときから一旦落ち着いた状態を経験していなければ警告しない
// 常に負荷が高いようなサーバーで無限に警告し続けるのを防ぐため // 常に負荷が高いようなサーバーで無限に警告し続けるのを防ぐため

View file

@ -1,18 +1,18 @@
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import Module from '@/module.js'; import Module from '@/module';
import serifs from '@/serifs.js'; import serifs from '@/serifs';
export default class extends Module { export default class extends Module {
public readonly name = 'sleepReport'; public readonly name = 'sleepReport';
@bindThis @autobind
public install() { public install() {
this.report(); this.report();
return {}; return {};
} }
@bindThis @autobind
private report() { private report() {
const now = Date.now(); const now = Date.now();

View file

@ -1,21 +1,21 @@
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import { HandlerResult } from '@/ai.js'; import { HandlerResult } from '@/ai';
import Module from '@/module.js'; import Module from '@/module';
import Message from '@/message.js'; import Message from '@/message';
import serifs, { getSerif } from '@/serifs.js'; import serifs, { getSerif } from '@/serifs';
import getDate from '@/utils/get-date.js'; import getDate from '@/utils/get-date';
export default class extends Module { export default class extends Module {
public readonly name = 'talk'; public readonly name = 'talk';
@bindThis @autobind
public install() { public install() {
return { return {
mentionHook: this.mentionHook, mentionHook: this.mentionHook,
}; };
} }
@bindThis @autobind
private async mentionHook(msg: Message) { private async mentionHook(msg: Message) {
if (!msg.text) return false; if (!msg.text) return false;
@ -37,7 +37,7 @@ export default class extends Module {
); );
} }
@bindThis @autobind
private greet(msg: Message): boolean { private greet(msg: Message): boolean {
if (msg.text == null) return false; if (msg.text == null) return false;
@ -106,7 +106,7 @@ export default class extends Module {
return false; return false;
} }
@bindThis @autobind
private erait(msg: Message): boolean { private erait(msg: Message): boolean {
const match = msg.extractedText.match(/(.+?)た(から|ので)(褒|ほ)めて/); const match = msg.extractedText.match(/(.+?)た(から|ので)(褒|ほ)めて/);
if (match) { if (match) {
@ -133,7 +133,7 @@ export default class extends Module {
return true; return true;
} }
@bindThis @autobind
private omedeto(msg: Message): boolean { private omedeto(msg: Message): boolean {
if (!msg.includes(['おめでと'])) return false; if (!msg.includes(['おめでと'])) return false;
@ -142,7 +142,7 @@ export default class extends Module {
return true; return true;
} }
@bindThis @autobind
private nadenade(msg: Message): boolean { private nadenade(msg: Message): boolean {
if (!msg.includes(['なでなで'])) return false; if (!msg.includes(['なでなで'])) return false;
@ -174,7 +174,7 @@ export default class extends Module {
return true; return true;
} }
@bindThis @autobind
private kawaii(msg: Message): boolean { private kawaii(msg: Message): boolean {
if (!msg.includes(['かわいい', '可愛い'])) return false; if (!msg.includes(['かわいい', '可愛い'])) return false;
@ -186,7 +186,7 @@ export default class extends Module {
return true; return true;
} }
@bindThis @autobind
private suki(msg: Message): boolean { private suki(msg: Message): boolean {
if (!msg.or(['好き', 'すき'])) return false; if (!msg.or(['好き', 'すき'])) return false;
@ -198,7 +198,7 @@ export default class extends Module {
return true; return true;
} }
@bindThis @autobind
private hug(msg: Message): boolean { private hug(msg: Message): boolean {
if (!msg.or(['ぎゅ', 'むぎゅ', /^はぐ(し(て|よ|よう)?)?$/])) return false; if (!msg.or(['ぎゅ', 'むぎゅ', /^はぐ(し(て|よ|よう)?)?$/])) return false;
@ -229,7 +229,7 @@ export default class extends Module {
return true; return true;
} }
@bindThis @autobind
private humu(msg: Message): boolean { private humu(msg: Message): boolean {
if (!msg.includes(['踏んで'])) return false; if (!msg.includes(['踏んで'])) return false;
@ -241,7 +241,7 @@ export default class extends Module {
return true; return true;
} }
@bindThis @autobind
private batou(msg: Message): boolean { private batou(msg: Message): boolean {
if (!msg.includes(['罵倒して', '罵って'])) return false; if (!msg.includes(['罵倒して', '罵って'])) return false;
@ -253,7 +253,7 @@ export default class extends Module {
return true; return true;
} }
@bindThis @autobind
private itai(msg: Message): boolean { private itai(msg: Message): boolean {
if (!msg.or(['痛い', 'いたい']) && !msg.extractedText.endsWith('痛い')) return false; if (!msg.or(['痛い', 'いたい']) && !msg.extractedText.endsWith('痛い')) return false;
@ -262,7 +262,7 @@ export default class extends Module {
return true; return true;
} }
@bindThis @autobind
private ote(msg: Message): boolean { private ote(msg: Message): boolean {
if (!msg.or(['お手'])) return false; if (!msg.or(['お手'])) return false;
@ -274,7 +274,7 @@ export default class extends Module {
return true; return true;
} }
@bindThis @autobind
private ponkotu(msg: Message): boolean | HandlerResult { private ponkotu(msg: Message): boolean | HandlerResult {
if (!msg.includes(['ぽんこつ'])) return false; if (!msg.includes(['ぽんこつ'])) return false;
@ -285,7 +285,7 @@ export default class extends Module {
}; };
} }
@bindThis @autobind
private rmrf(msg: Message): boolean | HandlerResult { private rmrf(msg: Message): boolean | HandlerResult {
if (!msg.includes(['rm -rf'])) return false; if (!msg.includes(['rm -rf'])) return false;
@ -296,7 +296,7 @@ export default class extends Module {
}; };
} }
@bindThis @autobind
private shutdown(msg: Message): boolean | HandlerResult { private shutdown(msg: Message): boolean | HandlerResult {
if (!msg.includes(['shutdown'])) return false; if (!msg.includes(['shutdown'])) return false;

View file

@ -1,12 +1,12 @@
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import Module from '@/module.js'; import Module from '@/module';
import Message from '@/message.js'; import Message from '@/message';
import serifs from '@/serifs.js'; import serifs from '@/serifs';
export default class extends Module { export default class extends Module {
public readonly name = 'timer'; public readonly name = 'timer';
@bindThis @autobind
public install() { public install() {
return { return {
mentionHook: this.mentionHook, mentionHook: this.mentionHook,
@ -14,7 +14,7 @@ export default class extends Module {
}; };
} }
@bindThis @autobind
private async mentionHook(msg: Message) { private async mentionHook(msg: Message) {
const secondsQuery = (msg.text || '').match(/([0-9]+)秒/); const secondsQuery = (msg.text || '').match(/([0-9]+)秒/);
const minutesQuery = (msg.text || '').match(/([0-9]+)分/); const minutesQuery = (msg.text || '').match(/([0-9]+)分/);
@ -55,7 +55,7 @@ export default class extends Module {
return true; return true;
} }
@bindThis @autobind
private timeoutCallback(data) { private timeoutCallback(data) {
const friend = this.ai.lookupFriend(data.userId); const friend = this.ai.lookupFriend(data.userId);
if (friend == null) return; // 処理の流れ上、実際にnullになることは無さそうだけど一応 if (friend == null) return; // 処理の流れ上、実際にnullになることは無さそうだけど一応

View file

@ -1,12 +1,12 @@
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import Module from '@/module.js'; import Module from '@/module';
import Friend from '@/friend.js'; import Friend from '@/friend';
import serifs from '@/serifs.js'; import serifs from '@/serifs';
export default class extends Module { export default class extends Module {
public readonly name = 'valentine'; public readonly name = 'valentine';
@bindThis @autobind
public install() { public install() {
this.crawleValentine(); this.crawleValentine();
setInterval(this.crawleValentine, 1000 * 60 * 3); setInterval(this.crawleValentine, 1000 * 60 * 3);
@ -17,7 +17,7 @@ export default class extends Module {
/** /**
* *
*/ */
@bindThis @autobind
private crawleValentine() { private crawleValentine() {
const now = new Date(); const now = new Date();

View file

@ -1,10 +1,10 @@
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import Module from '@/module.js'; import Module from '@/module';
export default class extends Module { export default class extends Module {
public readonly name = 'welcome'; public readonly name = 'welcome';
@bindThis @autobind
public install() { public install() {
const tl = this.ai.connection.useSharedConnection('localTimeline'); const tl = this.ai.connection.useSharedConnection('localTimeline');
@ -13,7 +13,7 @@ export default class extends Module {
return {}; return {};
} }
@bindThis @autobind
private onLocalNote(note: any) { private onLocalNote(note: any) {
if (note.isFirstNote) { if (note.isFirstNote) {
setTimeout(() => { setTimeout(() => {

View file

@ -1,10 +1,8 @@
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import { EventEmitter } from 'events'; import { EventEmitter } from 'events';
import WebSocket from 'ws'; import * as WebSocket from 'ws';
import _ReconnectingWebsocket from 'reconnecting-websocket'; const ReconnectingWebsocket = require('reconnecting-websocket');
import config from './config.js'; import config from './config';
const ReconnectingWebsocket = _ReconnectingWebsocket as unknown as typeof _ReconnectingWebsocket['default'];
/** /**
* Misskey stream connection * Misskey stream connection
@ -31,7 +29,7 @@ export default class Stream extends EventEmitter {
this.stream.addEventListener('message', this.onMessage); this.stream.addEventListener('message', this.onMessage);
} }
@bindThis @autobind
public useSharedConnection(channel: string): SharedConnection { public useSharedConnection(channel: string): SharedConnection {
let pool = this.sharedConnectionPools.find(p => p.channel === channel); let pool = this.sharedConnectionPools.find(p => p.channel === channel);
@ -45,19 +43,19 @@ export default class Stream extends EventEmitter {
return connection; return connection;
} }
@bindThis @autobind
public removeSharedConnection(connection: SharedConnection) { public removeSharedConnection(connection: SharedConnection) {
this.sharedConnections = this.sharedConnections.filter(c => c !== connection); this.sharedConnections = this.sharedConnections.filter(c => c !== connection);
} }
@bindThis @autobind
public connectToChannel(channel: string, params?: any): NonSharedConnection { public connectToChannel(channel: string, params?: any): NonSharedConnection {
const connection = new NonSharedConnection(this, channel, params); const connection = new NonSharedConnection(this, channel, params);
this.nonSharedConnections.push(connection); this.nonSharedConnections.push(connection);
return connection; return connection;
} }
@bindThis @autobind
public disconnectToChannel(connection: NonSharedConnection) { public disconnectToChannel(connection: NonSharedConnection) {
this.nonSharedConnections = this.nonSharedConnections.filter(c => c !== connection); this.nonSharedConnections = this.nonSharedConnections.filter(c => c !== connection);
} }
@ -65,7 +63,7 @@ export default class Stream extends EventEmitter {
/** /**
* Callback of when open connection * Callback of when open connection
*/ */
@bindThis @autobind
private onOpen() { private onOpen() {
const isReconnect = this.state == 'reconnecting'; const isReconnect = this.state == 'reconnecting';
@ -93,7 +91,7 @@ export default class Stream extends EventEmitter {
/** /**
* Callback of when close connection * Callback of when close connection
*/ */
@bindThis @autobind
private onClose() { private onClose() {
this.state = 'reconnecting'; this.state = 'reconnecting';
this.emit('_disconnected_'); this.emit('_disconnected_');
@ -102,7 +100,7 @@ export default class Stream extends EventEmitter {
/** /**
* Callback of when received a message from connection * Callback of when received a message from connection
*/ */
@bindThis @autobind
private onMessage(message) { private onMessage(message) {
const { type, body } = JSON.parse(message.data); const { type, body } = JSON.parse(message.data);
@ -130,7 +128,7 @@ export default class Stream extends EventEmitter {
/** /**
* Send a message to connection * Send a message to connection
*/ */
@bindThis @autobind
public send(typeOrPayload, payload?) { public send(typeOrPayload, payload?) {
const data = payload === undefined ? typeOrPayload : { const data = payload === undefined ? typeOrPayload : {
type: typeOrPayload, type: typeOrPayload,
@ -149,7 +147,7 @@ export default class Stream extends EventEmitter {
/** /**
* Close this connection * Close this connection
*/ */
@bindThis @autobind
public close() { public close() {
this.stream.removeEventListener('open', this.onOpen); this.stream.removeEventListener('open', this.onOpen);
this.stream.removeEventListener('message', this.onMessage); this.stream.removeEventListener('message', this.onMessage);
@ -171,7 +169,7 @@ class Pool {
this.id = Math.random().toString(); this.id = Math.random().toString();
} }
@bindThis @autobind
public inc() { public inc() {
if (this.users === 0 && !this.isConnected) { if (this.users === 0 && !this.isConnected) {
this.connect(); this.connect();
@ -186,7 +184,7 @@ class Pool {
} }
} }
@bindThis @autobind
public dec() { public dec() {
this.users--; this.users--;
@ -200,7 +198,7 @@ class Pool {
} }
} }
@bindThis @autobind
public connect() { public connect() {
this.isConnected = true; this.isConnected = true;
this.stream.send('connect', { this.stream.send('connect', {
@ -209,7 +207,7 @@ class Pool {
}); });
} }
@bindThis @autobind
private disconnect() { private disconnect() {
this.isConnected = false; this.isConnected = false;
this.disposeTimerId = null; this.disposeTimerId = null;
@ -229,7 +227,7 @@ abstract class Connection extends EventEmitter {
this.channel = channel; this.channel = channel;
} }
@bindThis @autobind
public send(id: string, typeOrPayload, payload?) { public send(id: string, typeOrPayload, payload?) {
const type = payload === undefined ? typeOrPayload.type : typeOrPayload; const type = payload === undefined ? typeOrPayload.type : typeOrPayload;
const body = payload === undefined ? typeOrPayload.body : payload; const body = payload === undefined ? typeOrPayload.body : payload;
@ -258,12 +256,12 @@ class SharedConnection extends Connection {
this.pool.inc(); this.pool.inc();
} }
@bindThis @autobind
public send(typeOrPayload, payload?) { public send(typeOrPayload, payload?) {
super.send(this.pool.id, typeOrPayload, payload); super.send(this.pool.id, typeOrPayload, payload);
} }
@bindThis @autobind
public dispose() { public dispose() {
this.pool.dec(); this.pool.dec();
this.removeAllListeners(); this.removeAllListeners();
@ -284,7 +282,7 @@ class NonSharedConnection extends Connection {
this.connect(); this.connect();
} }
@bindThis @autobind
public connect() { public connect() {
this.stream.send('connect', { this.stream.send('connect', {
channel: this.channel, channel: this.channel,
@ -293,12 +291,12 @@ class NonSharedConnection extends Connection {
}); });
} }
@bindThis @autobind
public send(typeOrPayload, payload?) { public send(typeOrPayload, payload?) {
super.send(this.id, typeOrPayload, payload); super.send(this.id, typeOrPayload, payload);
} }
@bindThis @autobind
public dispose() { public dispose() {
this.removeAllListeners(); this.removeAllListeners();
this.stream.send('disconnect', { id: this.id }); this.stream.send('disconnect', { id: this.id });

View file

@ -1,4 +1,4 @@
import { katakanaToHiragana, hankakuToZenkaku } from './japanese.js'; import { katakanaToHiragana, hankakuToZenkaku } from './japanese';
export default function(text: string, words: string[]): boolean { export default function(text: string, words: string[]): boolean {
if (text == null) return false; if (text == null) return false;

View file

@ -1,4 +1,4 @@
import chalk from 'chalk'; import * as chalk from 'chalk';
export default function(msg: string) { export default function(msg: string) {
const now = new Date(); const now = new Date();

View file

@ -1,4 +1,4 @@
import { hankakuToZenkaku, katakanaToHiragana } from './japanese.js'; import { hankakuToZenkaku, katakanaToHiragana } from './japanese';
export default function(text: string, words: (string | RegExp)[]): boolean { export default function(text: string, words: (string | RegExp)[]): boolean {
if (text == null) return false; if (text == null) return false;

View file

@ -1,7 +0,0 @@
export function sleep(msec: number) {
return new Promise<void>(res => {
setTimeout(() => {
res();
}, msec);
});
}

View file

@ -1,4 +1,4 @@
import seedrandom from 'seedrandom'; import * as seedrandom from 'seedrandom';
export const itemPrefixes = [ export const itemPrefixes = [
'プラチナ製', 'プラチナ製',

View file

@ -1,18 +1,18 @@
import { bindThis } from '@/decorators.js'; import autobind from 'autobind-decorator';
import Module from '@/module.js'; import Module from '@/module';
import Message from '@/message'; import Message from '@/message';
export default class extends Module { export default class extends Module {
public readonly name = 'test'; public readonly name = 'test';
@bindThis @autobind
public install() { public install() {
return { return {
mentionHook: this.mentionHook mentionHook: this.mentionHook
}; };
} }
@bindThis @autobind
private async mentionHook(msg: Message) { private async mentionHook(msg: Message) {
if (msg.text && msg.text.includes('ping')) { if (msg.text && msg.text.includes('ping')) {
msg.reply('PONG!', { msg.reply('PONG!', {

View file

@ -1,46 +1,26 @@
{ {
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": { "compilerOptions": {
"target": "ES2022", "noEmitOnError": true,
"module": "nodenext", "noImplicitAny": false,
"moduleResolution": "nodenext", "noImplicitReturns": true,
"declaration": true, "noImplicitThis": true,
"declarationMap": true, "noFallthroughCasesInSwitch": true,
"sourceMap": true,
"rootDir": "./src",
"outDir": "./built/",
"removeComments": true,
"resolveJsonModule": true,
"strict": true,
"strictFunctionTypes": true,
"strictNullChecks": true, "strictNullChecks": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"noImplicitReturns": true, "sourceMap": false,
"noImplicitAny": false, "target": "es2020",
"esModuleInterop": true, "module": "commonjs",
"typeRoots": [ "removeComments": false,
"./node_modules/@types" "noLib": false,
], "outDir": "built",
"lib": [ "rootDir": "src",
"esnext", "baseUrl": ".",
"dom"
],
"paths": { "paths": {
"@/*": ["./src/*"] "@/*": ["src/*"]
}, },
"plugins": [
// Transform paths in output .js files
{ "transform": "typescript-transform-paths" },
// Transform paths in output .d.ts files (Include this line if you output declarations files)
{ "transform": "typescript-transform-paths", "afterDeclarations": true }
]
}, },
"compileOnSave": false,
"include": [ "include": [
"src/**/*" "./src/**/*.ts"
], ]
"exclude": [
"node_modules",
"test/**/*"
],
} }