mirror of
https://github.com/syuilo/ai.git
synced 2024-11-14 01:37:59 +00:00
Compare commits
3 commits
296b63c6cf
...
c81e5b7974
Author | SHA1 | Date | |
---|---|---|---|
c81e5b7974 | |||
37f94bc0c1 | |||
fd50dc790f |
49
package.json
49
package.json
|
@ -1,48 +1,48 @@
|
||||||
{
|
{
|
||||||
"_v": "1.5.0",
|
"_v": "2.0.0",
|
||||||
|
"type": "module",
|
||||||
"main": "./built/index.js",
|
"main": "./built/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node ./built",
|
"start": "node ./built",
|
||||||
"build": "tsc",
|
"start-daemon": "nodemon ./built",
|
||||||
|
"build": "tspc",
|
||||||
"test": "jest"
|
"test": "jest"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/chalk": "2.2.0",
|
"@types/chalk": "2.2.0",
|
||||||
"@types/lokijs": "1.5.4",
|
"@types/lokijs": "1.5.14",
|
||||||
"@types/node": "16.0.1",
|
"@types/node": "20.11.5",
|
||||||
"@types/promise-retry": "1.1.3",
|
"@types/promise-retry": "1.1.6",
|
||||||
"@types/random-seed": "0.3.3",
|
"@types/random-seed": "0.3.5",
|
||||||
"@types/request-promise-native": "1.0.18",
|
"@types/seedrandom": "3.0.8",
|
||||||
"@types/seedrandom": "2.4.28",
|
"@types/twemoji-parser": "13.1.4",
|
||||||
"@types/twemoji-parser": "13.1.1",
|
"@types/uuid": "9.0.7",
|
||||||
"@types/uuid": "8.3.1",
|
"@types/ws": "8.5.10",
|
||||||
"@types/ws": "7.4.6",
|
"canvas": "2.11.2",
|
||||||
"autobind-decorator": "2.4.0",
|
"chalk": "5.3.0",
|
||||||
"canvas": "2.10.2",
|
"got": "14.0.0",
|
||||||
"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",
|
||||||
"module-alias": "2.2.2",
|
"nodemon": "3.0.3",
|
||||||
"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",
|
||||||
"timeout-as-promise": "1.0.0",
|
"ts-patch": "3.1.2",
|
||||||
"ts-node": "10.0.0",
|
"twemoji-parser": "14.0.0",
|
||||||
"twemoji-parser": "13.1.0",
|
"typescript": "5.3.3",
|
||||||
"typescript": "4.3.5",
|
"typescript-transform-paths": "3.4.6",
|
||||||
"uuid": "8.3.2",
|
"uuid": "9.0.1",
|
||||||
"ws": "7.5.2"
|
"ws": "8.16.0"
|
||||||
},
|
},
|
||||||
"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.2",
|
"@types/websocket": "1.0.10",
|
||||||
"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,5 +70,8 @@
|
||||||
"^@/(.+)": "<rootDir>/src/$1",
|
"^@/(.+)": "<rootDir>/src/$1",
|
||||||
"^#/(.+)": "<rootDir>/test/$1"
|
"^#/(.+)": "<rootDir>/test/$1"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"nodemonConfig": {
|
||||||
|
"ignore": ["memory.json"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
71
src/ai.ts
71
src/ai.ts
|
@ -1,21 +1,21 @@
|
||||||
// AI CORE
|
// AI CORE
|
||||||
|
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import * as loki from 'lokijs';
|
import loki from 'lokijs';
|
||||||
import * as request from 'request-promise-native';
|
import got from 'got';
|
||||||
import * as chalk from 'chalk';
|
import 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';
|
import config from '@/config.js';
|
||||||
import Module from '@/module';
|
import Module from '@/module.js';
|
||||||
import Message from '@/message';
|
import Message from '@/message.js';
|
||||||
import Friend, { FriendDoc } from '@/friend';
|
import Friend, { FriendDoc } from '@/friend.js';
|
||||||
import { User } from '@/misskey/user';
|
import { User } from '@/misskey/user.js';
|
||||||
import Stream from '@/stream';
|
import Stream from '@/stream.js';
|
||||||
import log from '@/utils/log';
|
import log from '@/utils/log.js';
|
||||||
const pkg = require('../package.json');
|
import { sleep } from './utils/sleep.js';
|
||||||
|
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 藍 {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public log(msg: string) {
|
public log(msg: string) {
|
||||||
log(chalk`[{magenta AiOS}]: ${msg}`);
|
log(chalk`[{magenta AiOS}]: ${msg}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 藍 {
|
||||||
* ユーザーから話しかけられたとき
|
* ユーザーから話しかけられたとき
|
||||||
* (メンション、リプライ、トークのメッセージ)
|
* (メンション、リプライ、トークのメッセージ)
|
||||||
*/
|
*/
|
||||||
@autobind
|
@bindThis
|
||||||
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 delay(1000);
|
await sleep(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// リアクションする
|
// リアクションする
|
||||||
|
@ -274,7 +274,7 @@ export default class 藍 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
private onNotification(notification: any) {
|
private onNotification(notification: any) {
|
||||||
switch (notification.type) {
|
switch (notification.type) {
|
||||||
// リアクションされたら親愛度を少し上げる
|
// リアクションされたら親愛度を少し上げる
|
||||||
|
@ -290,7 +290,7 @@ export default class 藍 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 藍 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
private logWaking() {
|
private logWaking() {
|
||||||
this.setMeta({
|
this.setMeta({
|
||||||
lastWakingAt: Date.now(),
|
lastWakingAt: Date.now(),
|
||||||
|
@ -313,7 +313,7 @@ export default class 藍 {
|
||||||
/**
|
/**
|
||||||
* データベースのコレクションを取得します
|
* データベースのコレクションを取得します
|
||||||
*/
|
*/
|
||||||
@autobind
|
@bindThis
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 藍 {
|
||||||
/**
|
/**
|
||||||
* ファイルをドライブにアップロードします
|
* ファイルをドライブにアップロードします
|
||||||
*/
|
*/
|
||||||
@autobind
|
@bindThis
|
||||||
public async upload(file: Buffer | fs.ReadStream, meta: any) {
|
public async upload(file: Buffer | fs.ReadStream, meta: any) {
|
||||||
const res = await request.post({
|
const res = await got.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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 投稿します
|
* 投稿します
|
||||||
*/
|
*/
|
||||||
@autobind
|
@bindThis
|
||||||
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 藍 {
|
||||||
/**
|
/**
|
||||||
* 指定ユーザーにトークメッセージを送信します
|
* 指定ユーザーにトークメッセージを送信します
|
||||||
*/
|
*/
|
||||||
@autobind
|
@bindThis
|
||||||
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,13 +381,14 @@ export default class 藍 {
|
||||||
/**
|
/**
|
||||||
* APIを呼び出します
|
* APIを呼び出します
|
||||||
*/
|
*/
|
||||||
@autobind
|
@bindThis
|
||||||
public api(endpoint: string, param?: any) {
|
public api(endpoint: string, param?: any) {
|
||||||
return request.post(`${config.apiUrl}/${endpoint}`, {
|
this.log(`API: ${endpoint}`);
|
||||||
|
return got.post(`${config.apiUrl}/${endpoint}`, {
|
||||||
json: Object.assign({
|
json: Object.assign({
|
||||||
i: config.i
|
i: config.i
|
||||||
}, param)
|
}, param)
|
||||||
});
|
}).json();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -397,7 +398,7 @@ export default class 藍 {
|
||||||
* @param id トークメッセージ上のコンテキストならばトーク相手のID、そうでないなら待ち受ける投稿のID
|
* @param id トークメッセージ上のコンテキストならばトーク相手のID、そうでないなら待ち受ける投稿のID
|
||||||
* @param data コンテキストに保存するオプションのデータ
|
* @param data コンテキストに保存するオプションのデータ
|
||||||
*/
|
*/
|
||||||
@autobind
|
@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.contexts.insertOne({
|
||||||
noteId: id,
|
noteId: id,
|
||||||
|
@ -412,7 +413,7 @@ export default class 藍 {
|
||||||
* @param module 解除するモジュール名
|
* @param module 解除するモジュール名
|
||||||
* @param key コンテキストを識別するためのキー
|
* @param key コンテキストを識別するためのキー
|
||||||
*/
|
*/
|
||||||
@autobind
|
@bindThis
|
||||||
public unsubscribeReply(module: Module, key: string | null) {
|
public unsubscribeReply(module: Module, key: string | null) {
|
||||||
this.contexts.findAndRemove({
|
this.contexts.findAndRemove({
|
||||||
key: key,
|
key: key,
|
||||||
|
@ -427,7 +428,7 @@ export default class 藍 {
|
||||||
* @param delay ミリ秒
|
* @param delay ミリ秒
|
||||||
* @param data オプションのデータ
|
* @param data オプションのデータ
|
||||||
*/
|
*/
|
||||||
@autobind
|
@bindThis
|
||||||
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({
|
||||||
|
@ -441,7 +442,7 @@ export default class 藍 {
|
||||||
this.log(`Timer persisted: ${module.name} ${id} ${delay}ms`);
|
this.log(`Timer persisted: ${module.name} ${id} ${delay}ms`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public getMeta() {
|
public getMeta() {
|
||||||
const rec = this.meta.findOne();
|
const rec = this.meta.findOne();
|
||||||
|
|
||||||
|
@ -457,7 +458,7 @@ export default class 藍 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public setMeta(meta: Partial<Meta>) {
|
public setMeta(meta: Partial<Meta>) {
|
||||||
const rec = this.getMeta();
|
const rec = this.getMeta();
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ type Config = {
|
||||||
memoryDir?: string;
|
memoryDir?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const config = require('../config.json');
|
import config from '../config.json' assert { type: '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';
|
||||||
|
|
41
src/decorators.ts
Normal file
41
src/decorators.ts
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
// 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;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import 藍 from '@/ai';
|
import 藍 from '@/ai.js';
|
||||||
import IModule from '@/module';
|
import IModule from '@/module.js';
|
||||||
import getDate from '@/utils/get-date';
|
import getDate from '@/utils/get-date.js';
|
||||||
import { User } from '@/misskey/user';
|
import { User } from '@/misskey/user.js';
|
||||||
import { genItem } from '@/vocabulary';
|
import { genItem } from '@/vocabulary.js';
|
||||||
|
|
||||||
export type FriendDoc = {
|
export type FriendDoc = {
|
||||||
userId: string;
|
userId: string;
|
||||||
|
@ -15,6 +15,7 @@ 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 {
|
||||||
|
@ -69,7 +70,7 @@ export default class Friend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public updateUser(user: Partial<User>) {
|
public updateUser(user: Partial<User>) {
|
||||||
this.doc.user = {
|
this.doc.user = {
|
||||||
...this.doc.user,
|
...this.doc.user,
|
||||||
|
@ -78,7 +79,7 @@ export default class Friend {
|
||||||
this.save();
|
this.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public getPerModulesData(module: IModule) {
|
public getPerModulesData(module: IModule) {
|
||||||
if (this.doc.perModulesData == null) {
|
if (this.doc.perModulesData == null) {
|
||||||
this.doc.perModulesData = {};
|
this.doc.perModulesData = {};
|
||||||
|
@ -92,7 +93,7 @@ export default class Friend {
|
||||||
return this.doc.perModulesData[module.name];
|
return this.doc.perModulesData[module.name];
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 = {};
|
||||||
|
@ -103,7 +104,7 @@ export default class Friend {
|
||||||
this.save();
|
this.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public incLove(amount = 1) {
|
public incLove(amount = 1) {
|
||||||
const today = getDate();
|
const today = getDate();
|
||||||
|
|
||||||
|
@ -127,7 +128,7 @@ export default class Friend {
|
||||||
this.ai.log(`💗 ${this.userId} +${amount}`);
|
this.ai.log(`💗 ${this.userId} +${amount}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public decLove(amount = 1) {
|
public decLove(amount = 1) {
|
||||||
// 親愛度MAXなら下げない
|
// 親愛度MAXなら下げない
|
||||||
if (this.doc.love === 100) return;
|
if (this.doc.love === 100) return;
|
||||||
|
@ -148,18 +149,32 @@ export default class Friend {
|
||||||
this.ai.log(`💢 ${this.userId} -${amount}`);
|
this.ai.log(`💢 ${this.userId} -${amount}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public updateName(name: string) {
|
public updateName(name: string) {
|
||||||
this.doc.name = name;
|
this.doc.name = name;
|
||||||
this.save();
|
this.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public generateTransferCode(): string {
|
public generateTransferCode(): string {
|
||||||
const code = genItem();
|
const code = genItem();
|
||||||
|
|
||||||
|
@ -169,7 +184,7 @@ export default class Friend {
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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
|
||||||
|
|
68
src/index.ts
68
src/index.ts
|
@ -1,40 +1,38 @@
|
||||||
// AiOS bootstrapper
|
// AiOS bootstrapper
|
||||||
|
|
||||||
import 'module-alias/register';
|
import chalk from 'chalk';
|
||||||
|
import got from 'got';
|
||||||
|
import promiseRetry from 'promise-retry';
|
||||||
|
|
||||||
import * as chalk from 'chalk';
|
import 藍 from './ai.js';
|
||||||
import * as request from 'request-promise-native';
|
import config from './config.js';
|
||||||
const promiseRetry = require('promise-retry');
|
import _log from './utils/log.js';
|
||||||
|
import pkg from '../package.json' assert { type: 'json' };
|
||||||
|
|
||||||
import 藍 from './ai';
|
import CoreModule from './modules/core/index.js';
|
||||||
import config from './config';
|
import TalkModule from './modules/talk/index.js';
|
||||||
import _log from './utils/log';
|
import BirthdayModule from './modules/birthday/index.js';
|
||||||
const pkg = require('../package.json');
|
import ReversiModule from './modules/reversi/index.js';
|
||||||
|
import PingModule from './modules/ping/index.js';
|
||||||
import CoreModule from './modules/core';
|
import EmojiModule from './modules/emoji/index.js';
|
||||||
import TalkModule from './modules/talk';
|
import EmojiReactModule from './modules/emoji-react/index.js';
|
||||||
import BirthdayModule from './modules/birthday';
|
import FortuneModule from './modules/fortune/index.js';
|
||||||
import ReversiModule from './modules/reversi';
|
import GuessingGameModule from './modules/guessing-game/index.js';
|
||||||
import PingModule from './modules/ping';
|
import KazutoriModule from './modules/kazutori/index.js';
|
||||||
import EmojiModule from './modules/emoji';
|
import KeywordModule from './modules/keyword/index.js';
|
||||||
import EmojiReactModule from './modules/emoji-react';
|
import WelcomeModule from './modules/welcome/index.js';
|
||||||
import FortuneModule from './modules/fortune';
|
import TimerModule from './modules/timer/index.js';
|
||||||
import GuessingGameModule from './modules/guessing-game';
|
import DiceModule from './modules/dice/index.js';
|
||||||
import KazutoriModule from './modules/kazutori';
|
import ServerModule from './modules/server/index.js';
|
||||||
import KeywordModule from './modules/keyword';
|
import FollowModule from './modules/follow/index.js';
|
||||||
import WelcomeModule from './modules/welcome';
|
import ValentineModule from './modules/valentine/index.js';
|
||||||
import TimerModule from './modules/timer';
|
import MazeModule from './modules/maze/index.js';
|
||||||
import DiceModule from './modules/dice';
|
import ChartModule from './modules/chart/index.js';
|
||||||
import ServerModule from './modules/server';
|
import SleepReportModule from './modules/sleep-report/index.js';
|
||||||
import FollowModule from './modules/follow';
|
import NotingModule from './modules/noting/index.js';
|
||||||
import ValentineModule from './modules/valentine';
|
import PollModule from './modules/poll/index.js';
|
||||||
import MazeModule from './modules/maze';
|
import ReminderModule from './modules/reminder/index.js';
|
||||||
import ChartModule from './modules/chart';
|
import CheckCustomEmojisModule from './modules/check-custom-emojis/index.js';
|
||||||
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(' /__\\ (_ _)( _ )/ __)');
|
||||||
|
@ -51,11 +49,11 @@ promiseRetry(retry => {
|
||||||
log(`Account fetching... ${chalk.gray(config.host)}`);
|
log(`Account fetching... ${chalk.gray(config.host)}`);
|
||||||
|
|
||||||
// アカウントをフェッチ
|
// アカウントをフェッチ
|
||||||
return request.post(`${config.apiUrl}/i`, {
|
return got.post(`${config.apiUrl}/i`, {
|
||||||
json: {
|
json: {
|
||||||
i: config.i
|
i: config.i
|
||||||
}
|
}
|
||||||
}).catch(retry);
|
}).json().catch(retry);
|
||||||
}, {
|
}, {
|
||||||
retries: 3
|
retries: 3
|
||||||
}).then(account => {
|
}).then(account => {
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import * as chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
const delay = require('timeout-as-promise');
|
|
||||||
|
|
||||||
import 藍 from '@/ai';
|
import 藍 from '@/ai.js';
|
||||||
import Friend from '@/friend';
|
import Friend from '@/friend.js';
|
||||||
import { User } from '@/misskey/user';
|
import { User } from '@/misskey/user.js';
|
||||||
import includes from '@/utils/includes';
|
import includes from '@/utils/includes.js';
|
||||||
import or from '@/utils/or';
|
import or from '@/utils/or.js';
|
||||||
import config from '@/config';
|
import config from '@/config.js';
|
||||||
|
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 {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 delay(2000);
|
await sleep(2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.ai.post({
|
return await this.ai.post({
|
||||||
|
@ -92,12 +92,12 @@ export default class Message {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public includes(words: string[]): boolean {
|
public includes(words: string[]): boolean {
|
||||||
return includes(this.text, words);
|
return includes(this.text, words);
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public or(words: (string | RegExp)[]): boolean {
|
public or(words: (string | RegExp)[]): boolean {
|
||||||
return or(this.text, words);
|
return or(this.text, words);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import 藍, { InstallerResult } from '@/ai';
|
import 藍, { InstallerResult } from '@/ai.js';
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 コンテキストに保存するオプションのデータ
|
||||||
*/
|
*/
|
||||||
@autobind
|
@bindThis
|
||||||
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 コンテキストを識別するためのキー
|
||||||
*/
|
*/
|
||||||
@autobind
|
@bindThis
|
||||||
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 オプションのデータ
|
||||||
*/
|
*/
|
||||||
@autobind
|
@bindThis
|
||||||
public setTimeoutWithPersistence(delay: number, data?: any) {
|
public setTimeoutWithPersistence(delay: number, data?: any) {
|
||||||
this.ai.setTimeoutWithPersistence(this, delay, data);
|
this.ai.setTimeoutWithPersistence(this, delay, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
protected getData() {
|
protected getData() {
|
||||||
return this.doc.data;
|
return this.doc.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import Module from '@/module';
|
import Module from '@/module.js';
|
||||||
import Friend from '@/friend';
|
import Friend from '@/friend.js';
|
||||||
import serifs from '@/serifs';
|
import serifs from '@/serifs.js';
|
||||||
|
|
||||||
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';
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 {
|
||||||
/**
|
/**
|
||||||
* 誕生日のユーザーがいないかチェック(いたら祝う)
|
* 誕生日のユーザーがいないかチェック(いたら祝う)
|
||||||
*/
|
*/
|
||||||
@autobind
|
@bindThis
|
||||||
private crawleBirthday() {
|
private crawleBirthday() {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const m = now.getMonth();
|
const m = now.getMonth();
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import Module from '@/module';
|
import Module from '@/module.js';
|
||||||
import serifs from '@/serifs';
|
import serifs from '@/serifs.js';
|
||||||
import Message from '@/message';
|
import Message from '@/message.js';
|
||||||
import { renderChart } from './render-chart';
|
import { renderChart } from './render-chart.js';
|
||||||
import { items } from '@/vocabulary';
|
import { items } from '@/vocabulary.js';
|
||||||
import config from '@/config';
|
import config from '@/config.js';
|
||||||
|
|
||||||
export default class extends Module {
|
export default class extends Module {
|
||||||
public readonly name = 'chart';
|
public readonly name = 'chart';
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public install() {
|
public install() {
|
||||||
if (config.chartEnabled === false) return {};
|
if (config.chartEnabled === false) return {};
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ export default class extends Module {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
private async mentionHook(msg: Message) {
|
private async mentionHook(msg: Message) {
|
||||||
if (!msg.includes(['チャート'])) {
|
if (!msg.includes(['チャート'])) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import * as loki from 'lokijs';
|
import loki from 'lokijs';
|
||||||
import Module from '@/module';
|
import Module from '@/module.js';
|
||||||
import serifs from '@/serifs';
|
import serifs from '@/serifs.js';
|
||||||
import config from '@/config';
|
import config from '@/config.js';
|
||||||
import Message from '@/message';
|
import Message from '@/message.js';
|
||||||
|
|
||||||
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;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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!');
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
private async sleep(ms: number) {
|
private async sleep(ms: number) {
|
||||||
return new Promise((res) => setTimeout(res, ms));
|
return new Promise((res) => setTimeout(res, ms));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import Module from '@/module';
|
import Module from '@/module.js';
|
||||||
import Message from '@/message';
|
import Message from '@/message.js';
|
||||||
import serifs from '@/serifs';
|
import serifs from '@/serifs.js';
|
||||||
import { safeForInterpolate } from '@/utils/safe-for-interpolate';
|
import { safeForInterpolate } from '@/utils/safe-for-interpolate.js';
|
||||||
|
|
||||||
const titles = ['さん', 'くん', '君', 'ちゃん', '様', '先生'];
|
const titles = ['さん', 'くん', '君', 'ちゃん', '様', '先生'];
|
||||||
|
|
||||||
export default class extends Module {
|
export default class extends Module {
|
||||||
public readonly name = 'core';
|
public readonly name = 'core';
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public install() {
|
public install() {
|
||||||
return {
|
return {
|
||||||
mentionHook: this.mentionHook,
|
mentionHook: this.mentionHook,
|
||||||
|
@ -17,7 +17,7 @@ export default class extends Module {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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(/^(.+?)って呼んで/)![1];
|
const name = msg.text.match(/^(.+?)って呼んで/g)![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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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;
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import Module from '@/module';
|
import Module from '@/module.js';
|
||||||
import Message from '@/message';
|
import Message from '@/message.js';
|
||||||
import serifs from '@/serifs';
|
import serifs from '@/serifs.js';
|
||||||
|
|
||||||
export default class extends Module {
|
export default class extends Module {
|
||||||
public readonly name = 'dice';
|
public readonly name = 'dice';
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public install() {
|
public install() {
|
||||||
return {
|
return {
|
||||||
mentionHook: this.mentionHook
|
mentionHook: this.mentionHook
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
private async mentionHook(msg: Message) {
|
private async mentionHook(msg: Message) {
|
||||||
if (msg.text == null) return false;
|
if (msg.text == null) return false;
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { parse } from 'twemoji-parser';
|
import { parse } from 'twemoji-parser';
|
||||||
const delay = require('timeout-as-promise');
|
|
||||||
|
|
||||||
import { Note } from '@/misskey/note';
|
import { Note } from '@/misskey/note.js';
|
||||||
import Module from '@/module';
|
import Module from '@/module.js';
|
||||||
import Stream from '@/stream';
|
import Stream from '@/stream.js';
|
||||||
import includes from '@/utils/includes';
|
import includes from '@/utils/includes.js';
|
||||||
|
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']>;
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 delay(1500);
|
await sleep(1500);
|
||||||
}
|
}
|
||||||
this.ai.api('notes/reactions/create', {
|
this.ai.api('notes/reactions/create', {
|
||||||
noteId: note.id,
|
noteId: note.id,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import Module from '@/module';
|
import Module from '@/module.js';
|
||||||
import Message from '@/message';
|
import Message from '@/message.js';
|
||||||
import serifs from '@/serifs';
|
import serifs from '@/serifs.js';
|
||||||
|
|
||||||
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';
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public install() {
|
public install() {
|
||||||
return {
|
return {
|
||||||
mentionHook: this.mentionHook
|
mentionHook: this.mentionHook
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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)];
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import Module from '@/module';
|
import Module from '@/module.js';
|
||||||
import Message from '@/message';
|
import Message from '@/message.js';
|
||||||
|
|
||||||
export default class extends Module {
|
export default class extends Module {
|
||||||
public readonly name = 'follow';
|
public readonly name = 'follow';
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public install() {
|
public install() {
|
||||||
return {
|
return {
|
||||||
mentionHook: this.mentionHook
|
mentionHook: this.mentionHook
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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) {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import Module from '@/module';
|
import Module from '@/module.js';
|
||||||
import Message from '@/message';
|
import Message from '@/message.js';
|
||||||
import serifs from '@/serifs';
|
import serifs from '@/serifs.js';
|
||||||
import * as seedrandom from 'seedrandom';
|
import seedrandom from 'seedrandom';
|
||||||
import { genItem } from '@/vocabulary';
|
import { genItem } from '@/vocabulary.js';
|
||||||
|
|
||||||
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';
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public install() {
|
public install() {
|
||||||
return {
|
return {
|
||||||
mentionHook: this.mentionHook
|
mentionHook: this.mentionHook
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
private async mentionHook(msg: Message) {
|
private async mentionHook(msg: Message) {
|
||||||
if (msg.includes(['占', 'うらな', '運勢', 'おみくじ'])) {
|
if (msg.includes(['占', 'うらな', '運勢', 'おみくじ'])) {
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import * as loki from 'lokijs';
|
import loki from 'lokijs';
|
||||||
import Module from '@/module';
|
import Module from '@/module.js';
|
||||||
import Message from '@/message';
|
import Message from '@/message.js';
|
||||||
import serifs from '@/serifs';
|
import serifs from '@/serifs.js';
|
||||||
|
|
||||||
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;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
private async contextHook(key: any, msg: Message) {
|
private async contextHook(key: any, msg: Message) {
|
||||||
if (msg.text == null) return;
|
if (msg.text == null) return;
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import * as loki from 'lokijs';
|
import loki from 'lokijs';
|
||||||
import Module from '@/module';
|
import Module from '@/module.js';
|
||||||
import Message from '@/message';
|
import Message from '@/message.js';
|
||||||
import serifs from '@/serifs';
|
import serifs from '@/serifs.js';
|
||||||
import { User } from '@/misskey/user';
|
import { User } from '@/misskey/user.js';
|
||||||
import { acct } from '@/utils/acct';
|
import { acct } from '@/utils/acct.js';
|
||||||
|
|
||||||
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>;
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 {
|
||||||
/**
|
/**
|
||||||
* 終了すべきゲームがないかチェック
|
* 終了すべきゲームがないかチェック
|
||||||
*/
|
*/
|
||||||
@autobind
|
@bindThis
|
||||||
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 {
|
||||||
/**
|
/**
|
||||||
* ゲームを終わらせる
|
* ゲームを終わらせる
|
||||||
*/
|
*/
|
||||||
@autobind
|
@bindThis
|
||||||
private finish(game: Game) {
|
private finish(game: Game) {
|
||||||
game.isEnded = true;
|
game.isEnded = true;
|
||||||
this.games.update(game);
|
this.games.update(game);
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import * as loki from 'lokijs';
|
import loki from 'lokijs';
|
||||||
import Module from '@/module';
|
import Module from '@/module.js';
|
||||||
import config from '@/config';
|
import config from '@/config.js';
|
||||||
import serifs from '@/serifs';
|
import serifs from '@/serifs.js';
|
||||||
import { mecab } from './mecab';
|
import { mecab } from './mecab.js';
|
||||||
|
|
||||||
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;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public install() {
|
public install() {
|
||||||
if (!config.keywordEnabled) return {};
|
if (!config.keywordEnabled) return {};
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ export default class extends Module {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as gen from 'random-seed';
|
import gen from 'random-seed';
|
||||||
import { CellType } from './maze';
|
import { CellType } from './maze.js';
|
||||||
|
|
||||||
const cellVariants = {
|
const cellVariants = {
|
||||||
void: {
|
void: {
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import Module from '@/module';
|
import Module from '@/module.js';
|
||||||
import serifs from '@/serifs';
|
import serifs from '@/serifs.js';
|
||||||
import { genMaze } from './gen-maze';
|
import { genMaze } from './gen-maze.js';
|
||||||
import { renderMaze } from './render-maze';
|
import { renderMaze } from './render-maze.js';
|
||||||
import Message from '@/message';
|
import Message from '@/message.js';
|
||||||
|
|
||||||
export default class extends Module {
|
export default class extends Module {
|
||||||
public readonly name = 'maze';
|
public readonly name = 'maze';
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import * as gen from 'random-seed';
|
import gen from 'random-seed';
|
||||||
import { createCanvas } from 'canvas';
|
import { createCanvas } from 'canvas';
|
||||||
|
|
||||||
import { CellType } from './maze';
|
import { CellType } from './maze.js';
|
||||||
import { themes } from './themes';
|
import { themes } from './themes.js';
|
||||||
|
|
||||||
const imageSize = 4096; // px
|
const imageSize = 4096; // px
|
||||||
const margin = 96 * 4;
|
const margin = 96 * 4;
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import Module from '@/module';
|
import Module from '@/module.js';
|
||||||
import serifs from '@/serifs';
|
import serifs from '@/serifs.js';
|
||||||
import { genItem } from '@/vocabulary';
|
import { genItem } from '@/vocabulary.js';
|
||||||
import config from '@/config';
|
import config from '@/config.js';
|
||||||
|
|
||||||
export default class extends Module {
|
export default class extends Module {
|
||||||
public readonly name = 'noting';
|
public readonly name = 'noting';
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
private post() {
|
private post() {
|
||||||
const notes = [
|
const notes = [
|
||||||
...serifs.noting.notes,
|
...serifs.noting.notes,
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import Module from '@/module';
|
import Module from '@/module.js';
|
||||||
import Message from '@/message';
|
import Message from '@/message.js';
|
||||||
|
|
||||||
export default class extends Module {
|
export default class extends Module {
|
||||||
public readonly name = 'ping';
|
public readonly name = 'ping';
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public install() {
|
public install() {
|
||||||
return {
|
return {
|
||||||
mentionHook: this.mentionHook
|
mentionHook: this.mentionHook
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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!', {
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import Message from '@/message';
|
import Message from '@/message.js';
|
||||||
import Module from '@/module';
|
import Module from '@/module.js';
|
||||||
import serifs from '@/serifs';
|
import serifs from '@/serifs.js';
|
||||||
import { genItem } from '@/vocabulary';
|
import { genItem } from '@/vocabulary.js';
|
||||||
import config from '@/config';
|
import config from '@/config.js';
|
||||||
import { Note } from '@/misskey/note';
|
import { Note } from '@/misskey/note.js';
|
||||||
|
|
||||||
export default class extends Module {
|
export default class extends Module {
|
||||||
public readonly name = 'poll';
|
public readonly name = 'poll';
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 });
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import * as loki from 'lokijs';
|
import loki from 'lokijs';
|
||||||
import Module from '@/module';
|
import Module from '@/module.js';
|
||||||
import Message from '@/message';
|
import Message from '@/message.js';
|
||||||
import serifs, { getSerif } from '@/serifs';
|
import serifs, { getSerif } from '@/serifs.js';
|
||||||
import { acct } from '@/utils/acct';
|
import { acct } from '@/utils/acct.js';
|
||||||
import config from '@/config';
|
import config from '@/config.js';
|
||||||
|
|
||||||
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;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
private async timeoutCallback(data) {
|
private async timeoutCallback(data) {
|
||||||
const remind = this.reminds.findOne({
|
const remind = this.reminds.findOne({
|
||||||
id: data.id
|
id: data.id
|
||||||
|
|
|
@ -6,13 +6,11 @@
|
||||||
* 切断されてしまうので、別々のプロセスで行うようにします
|
* 切断されてしまうので、別々のプロセスで行うようにします
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import 'module-alias/register';
|
import got from 'got';
|
||||||
|
import * as Reversi from './engine.js';
|
||||||
import * as request from 'request-promise-native';
|
import config from '@/config.js';
|
||||||
import Reversi, { Color } from 'misskey-reversi';
|
import serifs from '@/serifs.js';
|
||||||
import config from '@/config';
|
import { User } from '@/misskey/user.js';
|
||||||
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;
|
||||||
|
@ -29,8 +27,10 @@ class Session {
|
||||||
private account: User;
|
private account: User;
|
||||||
private game: any;
|
private game: any;
|
||||||
private form: any;
|
private form: any;
|
||||||
private o: Reversi;
|
private engine: Reversi.Game;
|
||||||
private botColor: Color;
|
private botColor: Reversi.Color;
|
||||||
|
|
||||||
|
private appliedOps: string[] = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 隅周辺のインデックスリスト(静的評価に利用)
|
* 隅周辺のインデックスリスト(静的評価に利用)
|
||||||
|
@ -79,7 +79,7 @@ class Session {
|
||||||
}
|
}
|
||||||
|
|
||||||
private get url(): string {
|
private get url(): string {
|
||||||
return `${config.host}/games/reversi/${this.game.id}`;
|
return `${config.host}/reversi/g/${this.game.id}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -89,10 +89,9 @@ 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 'set': this.onSet(msg.body); break;
|
case 'log': this.onLog(msg.body); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,18 +102,17 @@ 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;
|
this.game = msg.game;
|
||||||
|
if (this.game.canPutEverywhere) { // 対応してない
|
||||||
|
process.send!({
|
||||||
|
type: 'ended'
|
||||||
|
});
|
||||||
|
process.exit();
|
||||||
|
}
|
||||||
|
|
||||||
// TLに投稿する
|
// TLに投稿する
|
||||||
this.postGameStarted().then(note => {
|
this.postGameStarted().then(note => {
|
||||||
|
@ -122,24 +120,24 @@ class Session {
|
||||||
});
|
});
|
||||||
|
|
||||||
// リバーシエンジン初期化
|
// リバーシエンジン初期化
|
||||||
this.o = new Reversi(this.game.map, {
|
this.engine = new Reversi.Game(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.o.map.filter(p => p === 'empty').length - this.o.board.filter(x => x != null).length;
|
this.maxTurn = this.engine.map.filter(p => p === 'empty').length - this.engine.board.filter(x => x != null).length;
|
||||||
|
|
||||||
//#region 隅の位置計算など
|
//#region 隅の位置計算など
|
||||||
|
|
||||||
//#region 隅
|
//#region 隅
|
||||||
this.o.map.forEach((pix, i) => {
|
this.engine.map.forEach((pix, i) => {
|
||||||
if (pix == 'null') return;
|
if (pix == 'null') return;
|
||||||
|
|
||||||
const [x, y] = this.o.transformPosToXy(i);
|
const [x, y] = this.engine.posToXy(i);
|
||||||
const get = (x, y) => {
|
const get = (x, y) => {
|
||||||
if (x < 0 || y < 0 || x >= this.o.mapWidth || y >= this.o.mapHeight) return 'null';
|
if (x < 0 || y < 0 || x >= this.engine.mapWidth || y >= this.engine.mapHeight) return 'null';
|
||||||
return this.o.mapDataGet(this.o.transformXyToPos(x, y));
|
return this.engine.mapDataGet(this.engine.xyToPos(x, y));
|
||||||
};
|
};
|
||||||
|
|
||||||
const isNotSumi = (
|
const isNotSumi = (
|
||||||
|
@ -171,15 +169,15 @@ class Session {
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region 隅の隣
|
//#region 隅の隣
|
||||||
this.o.map.forEach((pix, i) => {
|
this.engine.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.o.transformPosToXy(i);
|
const [x, y] = this.engine.posToXy(i);
|
||||||
|
|
||||||
const check = (x, y) => {
|
const check = (x, y) => {
|
||||||
if (x < 0 || y < 0 || x >= this.o.mapWidth || y >= this.o.mapHeight) return 0;
|
if (x < 0 || y < 0 || x >= this.engine.mapWidth || y >= this.engine.mapHeight) return 0;
|
||||||
return this.sumiIndexes.includes(this.o.transformXyToPos(x, y));
|
return this.sumiIndexes.includes(this.engine.xyToPos(x, y));
|
||||||
};
|
};
|
||||||
|
|
||||||
const isSumiNear = (
|
const isSumiNear = (
|
||||||
|
@ -253,13 +251,23 @@ class Session {
|
||||||
/**
|
/**
|
||||||
* 打たれたとき
|
* 打たれたとき
|
||||||
*/
|
*/
|
||||||
private onSet = (msg: any) => {
|
private onLog = (log: any) => {
|
||||||
this.o.put(msg.color, msg.pos);
|
if (log.id == null || !this.appliedOps.includes(log.id)) {
|
||||||
|
switch (log.operation) {
|
||||||
|
case 'put': {
|
||||||
|
this.engine.putStone(log.pos);
|
||||||
this.currentTurn++;
|
this.currentTurn++;
|
||||||
|
|
||||||
if (msg.next === this.botColor) {
|
if (this.engine.turn === this.botColor) {
|
||||||
this.think();
|
this.think();
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -268,10 +276,10 @@ class Session {
|
||||||
* TODO: 接待時はまるっと処理の中身を変え、とにかく相手が隅を取っていること優先な評価にする
|
* TODO: 接待時はまるっと処理の中身を変え、とにかく相手が隅を取っていること優先な評価にする
|
||||||
*/
|
*/
|
||||||
private staticEval = () => {
|
private staticEval = () => {
|
||||||
let score = this.o.canPutSomewhere(this.botColor).length;
|
let score = this.engine.getPuttablePlaces(this.botColor).length;
|
||||||
|
|
||||||
for (const index of this.sumiIndexes) {
|
for (const index of this.sumiIndexes) {
|
||||||
const stone = this.o.board[index];
|
const stone = this.engine.board[index];
|
||||||
|
|
||||||
if (stone === this.botColor) {
|
if (stone === this.botColor) {
|
||||||
score += 1000; // 自分が隅を取っていたらスコアプラス
|
score += 1000; // 自分が隅を取っていたらスコアプラス
|
||||||
|
@ -283,7 +291,7 @@ class Session {
|
||||||
// TODO: ここに (隅以外の確定石の数 * 100) をスコアに加算する処理を入れる
|
// TODO: ここに (隅以外の確定石の数 * 100) をスコアに加算する処理を入れる
|
||||||
|
|
||||||
for (const index of this.sumiNearIndexes) {
|
for (const index of this.sumiNearIndexes) {
|
||||||
const stone = this.o.board[index];
|
const stone = this.engine.board[index];
|
||||||
|
|
||||||
if (stone === this.botColor) {
|
if (stone === this.botColor) {
|
||||||
score -= 10; // 自分が隅の周辺を取っていたらスコアマイナス(危険なので)
|
score -= 10; // 自分が隅の周辺を取っていたらスコアマイナス(危険なので)
|
||||||
|
@ -319,13 +327,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.o.put(this.o.turn, pos);
|
this.engine.putStone(pos);
|
||||||
|
|
||||||
const isBotTurn = this.o.turn === this.botColor;
|
const isBotTurn = this.engine.turn === this.botColor;
|
||||||
|
|
||||||
// 勝った
|
// 勝った
|
||||||
if (this.o.turn === null) {
|
if (this.engine.turn === null) {
|
||||||
const winner = this.o.winner;
|
const winner = this.engine.winner;
|
||||||
|
|
||||||
// 勝つことによる基本スコア
|
// 勝つことによる基本スコア
|
||||||
const base = 10000;
|
const base = 10000;
|
||||||
|
@ -334,14 +342,14 @@ class Session {
|
||||||
|
|
||||||
if (this.game.isLlotheo) {
|
if (this.game.isLlotheo) {
|
||||||
// 勝ちは勝ちでも、より自分の石を少なくした方が美しい勝ちだと判定する
|
// 勝ちは勝ちでも、より自分の石を少なくした方が美しい勝ちだと判定する
|
||||||
score = this.o.winner ? base - (this.o.blackCount * 100) : base - (this.o.whiteCount * 100);
|
score = this.engine.winner ? base - (this.engine.blackCount * 100) : base - (this.engine.whiteCount * 100);
|
||||||
} else {
|
} else {
|
||||||
// 勝ちは勝ちでも、より相手の石を少なくした方が美しい勝ちだと判定する
|
// 勝ちは勝ちでも、より相手の石を少なくした方が美しい勝ちだと判定する
|
||||||
score = this.o.winner ? base + (this.o.blackCount * 100) : base + (this.o.whiteCount * 100);
|
score = this.engine.winner ? base + (this.engine.blackCount * 100) : base + (this.engine.whiteCount * 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 巻き戻し
|
// 巻き戻し
|
||||||
this.o.undo();
|
this.engine.undo();
|
||||||
|
|
||||||
// 接待なら自分が負けた方が高スコア
|
// 接待なら自分が負けた方が高スコア
|
||||||
return this.isSettai
|
return this.isSettai
|
||||||
|
@ -354,11 +362,11 @@ class Session {
|
||||||
const score = this.staticEval();
|
const score = this.staticEval();
|
||||||
|
|
||||||
// 巻き戻し
|
// 巻き戻し
|
||||||
this.o.undo();
|
this.engine.undo();
|
||||||
|
|
||||||
return score;
|
return score;
|
||||||
} else {
|
} else {
|
||||||
const cans = this.o.canPutSomewhere(this.o.turn);
|
const cans = this.engine.getPuttablePlaces(this.engine.turn);
|
||||||
|
|
||||||
let value = isBotTurn ? -Infinity : Infinity;
|
let value = isBotTurn ? -Infinity : Infinity;
|
||||||
let a = alpha;
|
let a = alpha;
|
||||||
|
@ -384,24 +392,34 @@ class Session {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 巻き戻し
|
// 巻き戻し
|
||||||
this.o.undo();
|
this.engine.undo();
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const cans = this.o.canPutSomewhere(this.botColor);
|
const cans = this.engine.getPuttablePlaces(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: 'put',
|
type: 'putStone',
|
||||||
pos
|
pos,
|
||||||
|
id
|
||||||
});
|
});
|
||||||
|
this.appliedOps.push(id);
|
||||||
|
|
||||||
|
if (this.engine.turn === this.botColor) {
|
||||||
|
this.think();
|
||||||
|
}
|
||||||
}, 500);
|
}, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,9 +451,9 @@ class Session {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await request.post(`${config.host}/api/notes/create`, {
|
const res = await got.post(`${config.host}/api/notes/create`, {
|
||||||
json: body
|
json: body
|
||||||
});
|
}).json();
|
||||||
|
|
||||||
return res.createdNote;
|
return res.createdNote;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
212
src/modules/reversi/engine.ts
Normal file
212
src/modules/reversi/engine.ts
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,16 @@
|
||||||
import * as childProcess from 'child_process';
|
import * as childProcess from 'child_process';
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import Module from '@/module';
|
import Module from '@/module.js';
|
||||||
import serifs from '@/serifs';
|
import serifs from '@/serifs.js';
|
||||||
import config from '@/config';
|
import config from '@/config.js';
|
||||||
import Message from '@/message';
|
import Message from '@/message.js';
|
||||||
import Friend from '@/friend';
|
import Friend from '@/friend.js';
|
||||||
import getDate from '@/utils/get-date';
|
import getDate from '@/utils/get-date.js';
|
||||||
|
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';
|
||||||
|
@ -15,17 +20,17 @@ export default class extends Module {
|
||||||
*/
|
*/
|
||||||
private reversiConnection?: any;
|
private reversiConnection?: any;
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public install() {
|
public install() {
|
||||||
if (!config.reversiEnabled) return {};
|
if (!config.reversiEnabled) return {};
|
||||||
|
|
||||||
this.reversiConnection = this.ai.connection.useSharedConnection('gamesReversi');
|
this.reversiConnection = this.ai.connection.useSharedConnection('reversi');
|
||||||
|
|
||||||
// 招待されたとき
|
// 招待されたとき
|
||||||
this.reversiConnection.on('invited', msg => this.onReversiInviteMe(msg.parent));
|
this.reversiConnection.on('invited', msg => this.onReversiInviteMe(msg.user));
|
||||||
|
|
||||||
// マッチしたとき
|
// マッチしたとき
|
||||||
this.reversiConnection.on('matched', msg => this.onReversiGameStart(msg));
|
this.reversiConnection.on('matched', msg => this.onReversiGameStart(msg.game));
|
||||||
|
|
||||||
if (config.reversiEnabled) {
|
if (config.reversiEnabled) {
|
||||||
const mainStream = this.ai.connection.useSharedConnection('main');
|
const mainStream = this.ai.connection.useSharedConnection('main');
|
||||||
|
@ -43,13 +48,17 @@ export default class extends Module {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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);
|
||||||
|
|
||||||
this.ai.api('games/reversi/match', {
|
if (msg.includes(['接待'])) {
|
||||||
|
msg.friend.updateReversiStrength(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ai.api('reversi/match', {
|
||||||
userId: msg.userId
|
userId: msg.userId
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -62,13 +71,13 @@ export default class extends Module {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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('games/reversi/match', {
|
const game = await this.ai.api('reversi/match', {
|
||||||
userId: inviter.id
|
userId: inviter.id
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -78,12 +87,19 @@ export default class extends Module {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
private onReversiGameStart(game: any) {
|
private onReversiGameStart(game: any) {
|
||||||
this.log('enter reversi game room');
|
let strength = 4;
|
||||||
|
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('gamesReversiGame', {
|
const gw = this.ai.connection.connectToChannel('reversiGame', {
|
||||||
gameId: game.id
|
gameId: game.id
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -92,12 +108,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: 3,
|
value: strength,
|
||||||
items: [{
|
items: [{
|
||||||
label: '接待',
|
label: '接待',
|
||||||
value: 0
|
value: 0
|
||||||
|
@ -117,7 +133,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({
|
||||||
|
@ -130,9 +146,10 @@ export default class extends Module {
|
||||||
});
|
});
|
||||||
|
|
||||||
ai.on('message', (msg: Record<string, any>) => {
|
ai.on('message', (msg: Record<string, any>) => {
|
||||||
if (msg.type == 'put') {
|
if (msg.type == 'putStone') {
|
||||||
gw.send('set', {
|
gw.send('putStone', {
|
||||||
pos: msg.pos
|
pos: msg.pos,
|
||||||
|
id: msg.id,
|
||||||
});
|
});
|
||||||
} else if (msg.type == 'ended') {
|
} else if (msg.type == 'ended') {
|
||||||
gw.dispose();
|
gw.dispose();
|
||||||
|
@ -144,21 +161,26 @@ 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('accept', {});
|
gw.send('ready', true);
|
||||||
}, 2000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import Module from '@/module';
|
import Module from '@/module.js';
|
||||||
import serifs from '@/serifs';
|
import serifs from '@/serifs.js';
|
||||||
import config from '@/config';
|
import config from '@/config.js';
|
||||||
|
|
||||||
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[] = [];
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public install() {
|
public install() {
|
||||||
if (!config.serverMonitoring) return {};
|
if (!config.serverMonitoring) return {};
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ export default class extends Module {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
private async onStats(stats: any) {
|
private async onStats(stats: any) {
|
||||||
this.recentStat = stats;
|
this.recentStat = stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
private warn() {
|
private warn() {
|
||||||
//#region 前に警告したときから一旦落ち着いた状態を経験していなければ警告しない
|
//#region 前に警告したときから一旦落ち着いた状態を経験していなければ警告しない
|
||||||
// 常に負荷が高いようなサーバーで無限に警告し続けるのを防ぐため
|
// 常に負荷が高いようなサーバーで無限に警告し続けるのを防ぐため
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import Module from '@/module';
|
import Module from '@/module.js';
|
||||||
import serifs from '@/serifs';
|
import serifs from '@/serifs.js';
|
||||||
|
|
||||||
export default class extends Module {
|
export default class extends Module {
|
||||||
public readonly name = 'sleepReport';
|
public readonly name = 'sleepReport';
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public install() {
|
public install() {
|
||||||
this.report();
|
this.report();
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
private report() {
|
private report() {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { HandlerResult } from '@/ai';
|
import { HandlerResult } from '@/ai.js';
|
||||||
import Module from '@/module';
|
import Module from '@/module.js';
|
||||||
import Message from '@/message';
|
import Message from '@/message.js';
|
||||||
import serifs, { getSerif } from '@/serifs';
|
import serifs, { getSerif } from '@/serifs.js';
|
||||||
import getDate from '@/utils/get-date';
|
import getDate from '@/utils/get-date.js';
|
||||||
|
|
||||||
export default class extends Module {
|
export default class extends Module {
|
||||||
public readonly name = 'talk';
|
public readonly name = 'talk';
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public install() {
|
public install() {
|
||||||
return {
|
return {
|
||||||
mentionHook: this.mentionHook,
|
mentionHook: this.mentionHook,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
private shutdown(msg: Message): boolean | HandlerResult {
|
private shutdown(msg: Message): boolean | HandlerResult {
|
||||||
if (!msg.includes(['shutdown'])) return false;
|
if (!msg.includes(['shutdown'])) return false;
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import Module from '@/module';
|
import Module from '@/module.js';
|
||||||
import Message from '@/message';
|
import Message from '@/message.js';
|
||||||
import serifs from '@/serifs';
|
import serifs from '@/serifs.js';
|
||||||
|
|
||||||
export default class extends Module {
|
export default class extends Module {
|
||||||
public readonly name = 'timer';
|
public readonly name = 'timer';
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public install() {
|
public install() {
|
||||||
return {
|
return {
|
||||||
mentionHook: this.mentionHook,
|
mentionHook: this.mentionHook,
|
||||||
|
@ -14,7 +14,7 @@ export default class extends Module {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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になることは無さそうだけど一応
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import Module from '@/module';
|
import Module from '@/module.js';
|
||||||
import Friend from '@/friend';
|
import Friend from '@/friend.js';
|
||||||
import serifs from '@/serifs';
|
import serifs from '@/serifs.js';
|
||||||
|
|
||||||
export default class extends Module {
|
export default class extends Module {
|
||||||
public readonly name = 'valentine';
|
public readonly name = 'valentine';
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 {
|
||||||
/**
|
/**
|
||||||
* チョコ配り
|
* チョコ配り
|
||||||
*/
|
*/
|
||||||
@autobind
|
@bindThis
|
||||||
private crawleValentine() {
|
private crawleValentine() {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import Module from '@/module';
|
import Module from '@/module.js';
|
||||||
|
|
||||||
export default class extends Module {
|
export default class extends Module {
|
||||||
public readonly name = 'welcome';
|
public readonly name = 'welcome';
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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 {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
private onLocalNote(note: any) {
|
private onLocalNote(note: any) {
|
||||||
if (note.isFirstNote) {
|
if (note.isFirstNote) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import * as WebSocket from 'ws';
|
import WebSocket from 'ws';
|
||||||
const ReconnectingWebsocket = require('reconnecting-websocket');
|
import _ReconnectingWebsocket from 'reconnecting-websocket';
|
||||||
import config from './config';
|
import config from './config.js';
|
||||||
|
|
||||||
|
const ReconnectingWebsocket = _ReconnectingWebsocket as unknown as typeof _ReconnectingWebsocket['default'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Misskey stream connection
|
* Misskey stream connection
|
||||||
|
@ -29,7 +31,7 @@ export default class Stream extends EventEmitter {
|
||||||
this.stream.addEventListener('message', this.onMessage);
|
this.stream.addEventListener('message', this.onMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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);
|
||||||
|
|
||||||
|
@ -43,19 +45,19 @@ export default class Stream extends EventEmitter {
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public removeSharedConnection(connection: SharedConnection) {
|
public removeSharedConnection(connection: SharedConnection) {
|
||||||
this.sharedConnections = this.sharedConnections.filter(c => c !== connection);
|
this.sharedConnections = this.sharedConnections.filter(c => c !== connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public disconnectToChannel(connection: NonSharedConnection) {
|
public disconnectToChannel(connection: NonSharedConnection) {
|
||||||
this.nonSharedConnections = this.nonSharedConnections.filter(c => c !== connection);
|
this.nonSharedConnections = this.nonSharedConnections.filter(c => c !== connection);
|
||||||
}
|
}
|
||||||
|
@ -63,7 +65,7 @@ export default class Stream extends EventEmitter {
|
||||||
/**
|
/**
|
||||||
* Callback of when open connection
|
* Callback of when open connection
|
||||||
*/
|
*/
|
||||||
@autobind
|
@bindThis
|
||||||
private onOpen() {
|
private onOpen() {
|
||||||
const isReconnect = this.state == 'reconnecting';
|
const isReconnect = this.state == 'reconnecting';
|
||||||
|
|
||||||
|
@ -91,7 +93,7 @@ export default class Stream extends EventEmitter {
|
||||||
/**
|
/**
|
||||||
* Callback of when close connection
|
* Callback of when close connection
|
||||||
*/
|
*/
|
||||||
@autobind
|
@bindThis
|
||||||
private onClose() {
|
private onClose() {
|
||||||
this.state = 'reconnecting';
|
this.state = 'reconnecting';
|
||||||
this.emit('_disconnected_');
|
this.emit('_disconnected_');
|
||||||
|
@ -100,7 +102,7 @@ export default class Stream extends EventEmitter {
|
||||||
/**
|
/**
|
||||||
* Callback of when received a message from connection
|
* Callback of when received a message from connection
|
||||||
*/
|
*/
|
||||||
@autobind
|
@bindThis
|
||||||
private onMessage(message) {
|
private onMessage(message) {
|
||||||
const { type, body } = JSON.parse(message.data);
|
const { type, body } = JSON.parse(message.data);
|
||||||
|
|
||||||
|
@ -128,7 +130,7 @@ export default class Stream extends EventEmitter {
|
||||||
/**
|
/**
|
||||||
* Send a message to connection
|
* Send a message to connection
|
||||||
*/
|
*/
|
||||||
@autobind
|
@bindThis
|
||||||
public send(typeOrPayload, payload?) {
|
public send(typeOrPayload, payload?) {
|
||||||
const data = payload === undefined ? typeOrPayload : {
|
const data = payload === undefined ? typeOrPayload : {
|
||||||
type: typeOrPayload,
|
type: typeOrPayload,
|
||||||
|
@ -147,7 +149,7 @@ export default class Stream extends EventEmitter {
|
||||||
/**
|
/**
|
||||||
* Close this connection
|
* Close this connection
|
||||||
*/
|
*/
|
||||||
@autobind
|
@bindThis
|
||||||
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);
|
||||||
|
@ -169,7 +171,7 @@ class Pool {
|
||||||
this.id = Math.random().toString();
|
this.id = Math.random().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public inc() {
|
public inc() {
|
||||||
if (this.users === 0 && !this.isConnected) {
|
if (this.users === 0 && !this.isConnected) {
|
||||||
this.connect();
|
this.connect();
|
||||||
|
@ -184,7 +186,7 @@ class Pool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public dec() {
|
public dec() {
|
||||||
this.users--;
|
this.users--;
|
||||||
|
|
||||||
|
@ -198,7 +200,7 @@ class Pool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public connect() {
|
public connect() {
|
||||||
this.isConnected = true;
|
this.isConnected = true;
|
||||||
this.stream.send('connect', {
|
this.stream.send('connect', {
|
||||||
|
@ -207,7 +209,7 @@ class Pool {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
private disconnect() {
|
private disconnect() {
|
||||||
this.isConnected = false;
|
this.isConnected = false;
|
||||||
this.disposeTimerId = null;
|
this.disposeTimerId = null;
|
||||||
|
@ -227,7 +229,7 @@ abstract class Connection extends EventEmitter {
|
||||||
this.channel = channel;
|
this.channel = channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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;
|
||||||
|
@ -256,12 +258,12 @@ class SharedConnection extends Connection {
|
||||||
this.pool.inc();
|
this.pool.inc();
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public send(typeOrPayload, payload?) {
|
public send(typeOrPayload, payload?) {
|
||||||
super.send(this.pool.id, typeOrPayload, payload);
|
super.send(this.pool.id, typeOrPayload, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public dispose() {
|
public dispose() {
|
||||||
this.pool.dec();
|
this.pool.dec();
|
||||||
this.removeAllListeners();
|
this.removeAllListeners();
|
||||||
|
@ -282,7 +284,7 @@ class NonSharedConnection extends Connection {
|
||||||
this.connect();
|
this.connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public connect() {
|
public connect() {
|
||||||
this.stream.send('connect', {
|
this.stream.send('connect', {
|
||||||
channel: this.channel,
|
channel: this.channel,
|
||||||
|
@ -291,12 +293,12 @@ class NonSharedConnection extends Connection {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public send(typeOrPayload, payload?) {
|
public send(typeOrPayload, payload?) {
|
||||||
super.send(this.id, typeOrPayload, payload);
|
super.send(this.id, typeOrPayload, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public dispose() {
|
public dispose() {
|
||||||
this.removeAllListeners();
|
this.removeAllListeners();
|
||||||
this.stream.send('disconnect', { id: this.id });
|
this.stream.send('disconnect', { id: this.id });
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { katakanaToHiragana, hankakuToZenkaku } from './japanese';
|
import { katakanaToHiragana, hankakuToZenkaku } from './japanese.js';
|
||||||
|
|
||||||
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;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import * as chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
|
|
||||||
export default function(msg: string) {
|
export default function(msg: string) {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { hankakuToZenkaku, katakanaToHiragana } from './japanese';
|
import { hankakuToZenkaku, katakanaToHiragana } from './japanese.js';
|
||||||
|
|
||||||
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;
|
||||||
|
|
7
src/utils/sleep.ts
Normal file
7
src/utils/sleep.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
export function sleep(msec: number) {
|
||||||
|
return new Promise<void>(res => {
|
||||||
|
setTimeout(() => {
|
||||||
|
res();
|
||||||
|
}, msec);
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import * as seedrandom from 'seedrandom';
|
import seedrandom from 'seedrandom';
|
||||||
|
|
||||||
export const itemPrefixes = [
|
export const itemPrefixes = [
|
||||||
'プラチナ製',
|
'プラチナ製',
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
import autobind from 'autobind-decorator';
|
import { bindThis } from '@/decorators.js';
|
||||||
import Module from '@/module';
|
import Module from '@/module.js';
|
||||||
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';
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
public install() {
|
public install() {
|
||||||
return {
|
return {
|
||||||
mentionHook: this.mentionHook
|
mentionHook: this.mentionHook
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@bindThis
|
||||||
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!', {
|
||||||
|
|
|
@ -1,26 +1,46 @@
|
||||||
{
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/tsconfig",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"noEmitOnError": true,
|
"target": "ES2022",
|
||||||
"noImplicitAny": false,
|
"module": "nodenext",
|
||||||
"noImplicitReturns": true,
|
"moduleResolution": "nodenext",
|
||||||
"noImplicitThis": true,
|
"declaration": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"declarationMap": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"rootDir": "./src",
|
||||||
|
"outDir": "./built/",
|
||||||
|
"removeComments": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"strict": true,
|
||||||
|
"strictFunctionTypes": true,
|
||||||
"strictNullChecks": true,
|
"strictNullChecks": true,
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"sourceMap": false,
|
"noImplicitReturns": true,
|
||||||
"target": "es2020",
|
"noImplicitAny": false,
|
||||||
"module": "commonjs",
|
"esModuleInterop": true,
|
||||||
"removeComments": false,
|
"typeRoots": [
|
||||||
"noLib": false,
|
"./node_modules/@types"
|
||||||
"outDir": "built",
|
],
|
||||||
"rootDir": "src",
|
"lib": [
|
||||||
"baseUrl": ".",
|
"esnext",
|
||||||
|
"dom"
|
||||||
|
],
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["src/*"]
|
"@/*": ["./src/*"]
|
||||||
},
|
},
|
||||||
},
|
"plugins": [
|
||||||
"compileOnSave": false,
|
// Transform paths in output .js files
|
||||||
"include": [
|
{ "transform": "typescript-transform-paths" },
|
||||||
"./src/**/*.ts"
|
|
||||||
|
// Transform paths in output .d.ts files (Include this line if you output declarations files)
|
||||||
|
{ "transform": "typescript-transform-paths", "afterDeclarations": true }
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"test/**/*"
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue