mirror of
https://github.com/syuilo/ai.git
synced 2024-11-21 20:58:00 +00:00
Refactor
This commit is contained in:
parent
69bf95e6bf
commit
3d467c2629
34
package-lock.json
generated
34
package-lock.json
generated
|
@ -3,6 +3,14 @@
|
|||
"requires": true,
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"@types/chalk": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/chalk/-/chalk-2.2.0.tgz",
|
||||
"integrity": "sha512-1zzPV9FDe1I/WHhRkf9SNgqtRJWZqrBWgu7JGveuHmmyR9CnAPCie2N/x+iHrgnpYBIcCJWHBoMRv2TRWktsvw==",
|
||||
"requires": {
|
||||
"chalk": "*"
|
||||
}
|
||||
},
|
||||
"@types/events": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz",
|
||||
|
@ -142,9 +150,9 @@
|
|||
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
|
||||
},
|
||||
"chalk": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
|
||||
"integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
|
@ -157,17 +165,17 @@
|
|||
"integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz",
|
||||
"integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==",
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"requires": {
|
||||
"color-name": "1.1.1"
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz",
|
||||
"integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok="
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.6",
|
||||
|
@ -561,9 +569,9 @@
|
|||
"integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks="
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
|
||||
"integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"requires": {
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
|
|
|
@ -6,12 +6,14 @@
|
|||
"build": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/chalk": "2.2.0",
|
||||
"@types/lokijs": "1.5.2",
|
||||
"@types/node": "10.0.5",
|
||||
"@types/promise-retry": "1.1.2",
|
||||
"@types/seedrandom": "2.4.27",
|
||||
"@types/ws": "6.0.1",
|
||||
"autobind-decorator": "2.1.0",
|
||||
"chalk": "2.4.2",
|
||||
"lokijs": "1.5.5",
|
||||
"mecab-async": "0.1.2",
|
||||
"misskey-reversi": "0.0.5",
|
||||
|
|
82
src/ai.ts
82
src/ai.ts
|
@ -1,22 +1,38 @@
|
|||
// AI CORE
|
||||
|
||||
import autobind from 'autobind-decorator';
|
||||
import * as loki from 'lokijs';
|
||||
import * as request from 'request-promise-native';
|
||||
import chalk from 'chalk';
|
||||
import config from './config';
|
||||
import IModule from './module';
|
||||
import Module from './module';
|
||||
import MessageLike from './message-like';
|
||||
import { FriendDoc } from './friend';
|
||||
import { User } from './misskey/user';
|
||||
import getCollection from './utils/get-collection';
|
||||
import Stream from './stream';
|
||||
|
||||
type OnMentionHandler = (msg: MessageLike) => boolean | HandlerResult;
|
||||
type OnContextReplyHandler = (msg: MessageLike, data?: any) => void | HandlerResult;
|
||||
|
||||
export type HandlerResult = {
|
||||
reaction: string;
|
||||
};
|
||||
|
||||
export type InstallerResult = {
|
||||
onMention?: OnMentionHandler;
|
||||
onContextReply?: OnContextReplyHandler;
|
||||
};
|
||||
|
||||
/**
|
||||
* 藍
|
||||
*/
|
||||
export default class 藍 {
|
||||
public account: User;
|
||||
public connection: Stream;
|
||||
private modules: IModule[] = [];
|
||||
public modules: Module[] = [];
|
||||
private onMentionHandlers: OnMentionHandler[] = [];
|
||||
private onContextReplyHandlers: { [moduleName: string]: OnContextReplyHandler } = {};
|
||||
public db: loki;
|
||||
|
||||
private contexts: loki.Collection<{
|
||||
|
@ -30,19 +46,30 @@ export default class 藍 {
|
|||
|
||||
public friends: loki.Collection<FriendDoc>;
|
||||
|
||||
constructor(account: User, modules: IModule[]) {
|
||||
constructor(account: User, ready?: Function) {
|
||||
this.account = account;
|
||||
this.modules = modules;
|
||||
|
||||
this.db = new loki('memory.json', {
|
||||
autoload: true,
|
||||
autosave: true,
|
||||
autosaveInterval: 1000,
|
||||
autoloadCallback: this.init
|
||||
autoloadCallback: err => {
|
||||
if (err) {
|
||||
this.log(chalk.red(`Failed to load DB: ${err}`));
|
||||
} else {
|
||||
if (ready) ready();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private init = () => {
|
||||
@autobind
|
||||
public log(msg: string) {
|
||||
console.log(`[AiOS]: ${msg}`);
|
||||
}
|
||||
|
||||
@autobind
|
||||
public run() {
|
||||
//#region Init DB
|
||||
this.contexts = getCollection(this.db, 'contexts', {
|
||||
indices: ['key']
|
||||
|
@ -62,7 +89,7 @@ export default class 藍 {
|
|||
// メンションされたとき
|
||||
mainStream.on('mention', data => {
|
||||
if (data.userId == this.account.id) return; // 自分は弾く
|
||||
if (data.text.startsWith('@' + this.account.username)) {
|
||||
if (data.text && data.text.startsWith('@' + this.account.username)) {
|
||||
this.onMention(new MessageLike(this, data, false));
|
||||
}
|
||||
});
|
||||
|
@ -81,11 +108,21 @@ export default class 藍 {
|
|||
//#endregion
|
||||
|
||||
// Install modules
|
||||
this.modules.forEach(m => m.install(this));
|
||||
this.modules.forEach(m => {
|
||||
this.log(`Installing ${chalk.cyan.italic(m.name)}\tmodule...`);
|
||||
const res = m.install();
|
||||
if (res != null) {
|
||||
if (res.onMention) this.onMentionHandlers.push(res.onMention);
|
||||
if (res.onContextReply) this.onContextReplyHandlers[m.name] = res.onContextReply;
|
||||
}
|
||||
});
|
||||
|
||||
this.log(chalk.green.bold('Ai am now running!'));
|
||||
}
|
||||
|
||||
private onMention = (msg: MessageLike) => {
|
||||
console.log(`mention received: ${msg.id}`);
|
||||
@autobind
|
||||
private onMention(msg: MessageLike) {
|
||||
this.log(`mention received: ${msg.id}`);
|
||||
|
||||
const context = !msg.isMessage && msg.replyId == null ? null : this.contexts.findOne(msg.isMessage ? {
|
||||
isMessage: true,
|
||||
|
@ -98,17 +135,17 @@ export default class 藍 {
|
|||
let reaction = 'love';
|
||||
|
||||
if (context != null) {
|
||||
const module = this.modules.find(m => m.name == context.module);
|
||||
const res = module.onReplyThisModule(msg, context.data);
|
||||
const handler = this.onContextReplyHandlers[context.module];
|
||||
const res = handler(msg, context.data);
|
||||
|
||||
if (res != null && typeof res === 'object') {
|
||||
reaction = res.reaction;
|
||||
}
|
||||
} else {
|
||||
let res: ReturnType<IModule['onMention']>;
|
||||
let res: boolean | HandlerResult;
|
||||
|
||||
this.modules.filter(m => m.hasOwnProperty('onMention')).some(m => {
|
||||
res = m.onMention(msg);
|
||||
this.onMentionHandlers.some(handler => {
|
||||
res = handler(msg);
|
||||
return res === true || typeof res === 'object';
|
||||
});
|
||||
|
||||
|
@ -135,18 +172,21 @@ export default class 藍 {
|
|||
}, 1000);
|
||||
}
|
||||
|
||||
public post = async (param: any) => {
|
||||
@autobind
|
||||
public async post(param: any) {
|
||||
const res = await this.api('notes/create', param);
|
||||
return res.createdNote;
|
||||
}
|
||||
|
||||
public sendMessage = (userId: any, param: any) => {
|
||||
@autobind
|
||||
public sendMessage(userId: any, param: any) {
|
||||
return this.api('messaging/messages/create', Object.assign({
|
||||
userId: userId,
|
||||
}, param));
|
||||
}
|
||||
|
||||
public api = (endpoint: string, param?: any) => {
|
||||
@autobind
|
||||
public api(endpoint: string, param?: any) {
|
||||
return request.post(`${config.apiUrl}/${endpoint}`, {
|
||||
json: Object.assign({
|
||||
i: config.i
|
||||
|
@ -154,7 +194,8 @@ export default class 藍 {
|
|||
});
|
||||
};
|
||||
|
||||
public subscribeReply = (module: IModule, key: string, isMessage: boolean, id: string, data?: any) => {
|
||||
@autobind
|
||||
public subscribeReply(module: Module, key: string, isMessage: boolean, id: string, data?: any) {
|
||||
this.contexts.insertOne(isMessage ? {
|
||||
isMessage: true,
|
||||
userId: id,
|
||||
|
@ -170,7 +211,8 @@ export default class 藍 {
|
|||
});
|
||||
}
|
||||
|
||||
public unsubscribeReply = (module: IModule, key: string) => {
|
||||
@autobind
|
||||
public unsubscribeReply(module: Module, key: string) {
|
||||
this.contexts.findAndRemove({
|
||||
key: key,
|
||||
module: module.name
|
||||
|
|
50
src/index.ts
50
src/index.ts
|
@ -16,42 +16,48 @@ import ServerModule from './modules/server';
|
|||
import FollowModule from './modules/follow';
|
||||
import ValentineModule from './modules/valentine';
|
||||
|
||||
import chalk from 'chalk';
|
||||
import * as request from 'request-promise-native';
|
||||
import IModule from './module';
|
||||
const promiseRetry = require('promise-retry');
|
||||
|
||||
console.log('--- starting ai... ---');
|
||||
function log(msg: string): void {
|
||||
console.log(`[Boot]: ${msg}`);
|
||||
}
|
||||
|
||||
log(chalk.bold('Ai v1.0'));
|
||||
|
||||
promiseRetry(retry => {
|
||||
log(`Account fetching... >>> ${config.host}`);
|
||||
return request.post(`${config.apiUrl}/i`, {
|
||||
json: {
|
||||
i: config.i
|
||||
}
|
||||
}).catch(retry);
|
||||
}, {
|
||||
retries: 3
|
||||
}).then(account => {
|
||||
console.log(`account fetched: @${account.username}`);
|
||||
log(chalk.green(`Account fetched successfully: @${account.username}`));
|
||||
|
||||
const modules: IModule[] = [
|
||||
new EmojiModule(),
|
||||
new FortuneModule(),
|
||||
new GuessingGameModule(),
|
||||
new ReversiModule(),
|
||||
new TimerModule(),
|
||||
new DiceModule(),
|
||||
new CoreModule(),
|
||||
new PingModule(),
|
||||
new WelcomeModule(),
|
||||
new ServerModule(),
|
||||
new FollowModule(),
|
||||
new BirthdayModule(),
|
||||
new ValentineModule(),
|
||||
];
|
||||
log('Starting AiOS...');
|
||||
|
||||
if (config.keywordEnabled) modules.push(new KeywordModule());
|
||||
const ai = new 藍(account);
|
||||
|
||||
new 藍(account, modules);
|
||||
new EmojiModule(ai);
|
||||
new FortuneModule(ai);
|
||||
new GuessingGameModule(ai);
|
||||
new ReversiModule(ai);
|
||||
new TimerModule(ai);
|
||||
new DiceModule(ai);
|
||||
new CoreModule(ai);
|
||||
new PingModule(ai);
|
||||
new WelcomeModule(ai);
|
||||
new ServerModule(ai);
|
||||
new FollowModule(ai);
|
||||
new BirthdayModule(ai);
|
||||
new ValentineModule(ai);
|
||||
if (config.keywordEnabled) new KeywordModule(ai);
|
||||
|
||||
console.log('--- ai started! ---');
|
||||
ai.run();
|
||||
}).catch(e => {
|
||||
console.error('failed to fetch account', e);
|
||||
log(chalk.red('Failed to fetch the account'));
|
||||
});
|
||||
|
|
|
@ -50,7 +50,7 @@ export default class MessageLike {
|
|||
public reply = async (text: string, cw?: string) => {
|
||||
if (text == null) return;
|
||||
|
||||
console.log(`sending reply of ${this.id} ...`);
|
||||
this.ai.log(`sending reply of ${this.id} ...`);
|
||||
|
||||
await delay(2000);
|
||||
|
||||
|
|
|
@ -1,13 +1,30 @@
|
|||
import 藍 from './ai';
|
||||
import MessageLike from './message-like';
|
||||
import autobind from 'autobind-decorator';
|
||||
import 藍, { InstallerResult } from './ai';
|
||||
|
||||
export default interface IModule {
|
||||
name: string;
|
||||
install?: (ai: 藍) => void;
|
||||
onMention?: (msg: MessageLike) => boolean | Result;
|
||||
onReplyThisModule?: (msg: MessageLike, data?: any) => void | Result;
|
||||
export default abstract class Module {
|
||||
public abstract name: string;
|
||||
|
||||
protected ai: 藍;
|
||||
|
||||
constructor(ai: 藍) {
|
||||
this.ai = ai;
|
||||
this.ai.modules.push(this);
|
||||
}
|
||||
|
||||
public abstract install(): InstallerResult;
|
||||
|
||||
@autobind
|
||||
protected log(msg: string) {
|
||||
this.ai.log(`[module ${this.name}]: ${msg}`);
|
||||
}
|
||||
|
||||
@autobind
|
||||
public subscribeReply(key: string, isMessage: boolean, id: string, data?: any) {
|
||||
this.ai.subscribeReply(this, key, isMessage, id, data);
|
||||
}
|
||||
|
||||
@autobind
|
||||
public unsubscribeReply(key: string) {
|
||||
this.ai.unsubscribeReply(this, key);
|
||||
}
|
||||
}
|
||||
|
||||
export type Result = {
|
||||
reaction: string;
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import 藍 from '../../ai';
|
||||
import IModule from '../../module';
|
||||
import autobind from 'autobind-decorator';
|
||||
import Module from '../../module';
|
||||
import Friend from '../../friend';
|
||||
import serifs from '../../serifs';
|
||||
|
||||
|
@ -7,22 +7,22 @@ function zeroPadding(num: number, length: number): string {
|
|||
return ('0000000000' + num).slice(-length);
|
||||
}
|
||||
|
||||
export default class BirthdayModule implements IModule {
|
||||
export default class BirthdayModule extends Module {
|
||||
public readonly name = 'birthday';
|
||||
|
||||
private ai: 藍;
|
||||
|
||||
public install = (ai: 藍) => {
|
||||
this.ai = ai;
|
||||
|
||||
@autobind
|
||||
public install() {
|
||||
this.crawleBirthday();
|
||||
setInterval(this.crawleBirthday, 1000 * 60 * 3);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* 誕生日のユーザーがいないかチェック(いたら祝う)
|
||||
*/
|
||||
private crawleBirthday = () => {
|
||||
@autobind
|
||||
private crawleBirthday() {
|
||||
const now = new Date();
|
||||
const m = now.getMonth();
|
||||
const d = now.getDate();
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 藍 from '../../ai';
|
||||
import IModule, { Result } from '../../module';
|
||||
import autobind from 'autobind-decorator';
|
||||
import { HandlerResult } from '../../ai';
|
||||
import Module from '../../module';
|
||||
import MessageLike from '../../message-like';
|
||||
import serifs, { getSerif } from '../../serifs';
|
||||
import getDate from '../../utils/get-date';
|
||||
|
@ -8,15 +9,19 @@ const titles = ['さん', 'くん', '君', 'ちゃん', '様', '先生'];
|
|||
|
||||
const invalidChars = ['@', '#', '*', ':', '(', '[', ' ', ' '];
|
||||
|
||||
export default class CoreModule implements IModule {
|
||||
export default class CoreModule extends Module {
|
||||
public readonly name = 'core';
|
||||
private ai: 藍;
|
||||
|
||||
public install = (ai: 藍) => {
|
||||
this.ai = ai;
|
||||
@autobind
|
||||
public install() {
|
||||
return {
|
||||
onMention: this.onMention,
|
||||
onContextReply: this.onContextReply
|
||||
};
|
||||
}
|
||||
|
||||
public onMention = (msg: MessageLike) => {
|
||||
@autobind
|
||||
private onMention(msg: MessageLike) {
|
||||
if (!msg.text) return false;
|
||||
|
||||
return (
|
||||
|
@ -34,7 +39,8 @@ export default class CoreModule implements IModule {
|
|||
);
|
||||
}
|
||||
|
||||
private setName = (msg: MessageLike): boolean => {
|
||||
@autobind
|
||||
private setName(msg: MessageLike): boolean {
|
||||
if (!msg.text) return false;
|
||||
if (!msg.text.includes('って呼んで')) return false;
|
||||
if (msg.text.startsWith('って呼んで')) return false;
|
||||
|
@ -66,7 +72,7 @@ export default class CoreModule implements IModule {
|
|||
msg.reply(serifs.core.setNameOk(name));
|
||||
} else {
|
||||
msg.reply(serifs.core.san).then(reply => {
|
||||
this.ai.subscribeReply(this, msg.userId, msg.isMessage, msg.isMessage ? msg.userId : reply.id, {
|
||||
this.subscribeReply(msg.userId, msg.isMessage, msg.isMessage ? msg.userId : reply.id, {
|
||||
name: name
|
||||
});
|
||||
});
|
||||
|
@ -75,7 +81,8 @@ export default class CoreModule implements IModule {
|
|||
return true;
|
||||
}
|
||||
|
||||
private greet = (msg: MessageLike): boolean => {
|
||||
@autobind
|
||||
private greet(msg: MessageLike): boolean {
|
||||
if (msg.text == null) return false;
|
||||
|
||||
const incLove = () => {
|
||||
|
@ -143,7 +150,8 @@ export default class CoreModule implements IModule {
|
|||
return false;
|
||||
}
|
||||
|
||||
private nadenade = (msg: MessageLike): boolean => {
|
||||
@autobind
|
||||
private nadenade(msg: MessageLike): boolean {
|
||||
if (!msg.includes(['なでなで'])) return false;
|
||||
|
||||
// メッセージのみ
|
||||
|
@ -177,7 +185,8 @@ export default class CoreModule implements IModule {
|
|||
return true;
|
||||
}
|
||||
|
||||
private kawaii = (msg: MessageLike): boolean => {
|
||||
@autobind
|
||||
private kawaii(msg: MessageLike): boolean {
|
||||
if (!msg.includes(['かわいい', '可愛い'])) return false;
|
||||
|
||||
// メッセージのみ
|
||||
|
@ -191,7 +200,8 @@ export default class CoreModule implements IModule {
|
|||
return true;
|
||||
}
|
||||
|
||||
private suki = (msg: MessageLike): boolean => {
|
||||
@autobind
|
||||
private suki(msg: MessageLike): boolean {
|
||||
if (!msg.or(['好き', 'すき'])) return false;
|
||||
|
||||
// メッセージのみ
|
||||
|
@ -205,7 +215,8 @@ export default class CoreModule implements IModule {
|
|||
return true;
|
||||
}
|
||||
|
||||
private hug = (msg: MessageLike): boolean => {
|
||||
@autobind
|
||||
private hug(msg: MessageLike): boolean {
|
||||
if (!msg.or(['ぎゅ', 'むぎゅ', /^はぐ(し(て|よ|よう)?)?$/])) return false;
|
||||
|
||||
// メッセージのみ
|
||||
|
@ -238,7 +249,8 @@ export default class CoreModule implements IModule {
|
|||
return true;
|
||||
}
|
||||
|
||||
private humu = (msg: MessageLike): boolean => {
|
||||
@autobind
|
||||
private humu(msg: MessageLike): boolean {
|
||||
if (!msg.includes(['踏んで'])) return false;
|
||||
|
||||
// メッセージのみ
|
||||
|
@ -252,7 +264,8 @@ export default class CoreModule implements IModule {
|
|||
return true;
|
||||
}
|
||||
|
||||
private batou = (msg: MessageLike): boolean => {
|
||||
@autobind
|
||||
private batou(msg: MessageLike): boolean {
|
||||
if (!msg.includes(['罵倒して', '罵って'])) return false;
|
||||
|
||||
// メッセージのみ
|
||||
|
@ -266,7 +279,8 @@ export default class CoreModule implements IModule {
|
|||
return true;
|
||||
}
|
||||
|
||||
private ponkotu = (msg: MessageLike): boolean | Result => {
|
||||
@autobind
|
||||
private ponkotu(msg: MessageLike): boolean | HandlerResult {
|
||||
if (!msg.includes(['ぽんこつ'])) return false;
|
||||
|
||||
msg.friend.decLove();
|
||||
|
@ -276,7 +290,8 @@ export default class CoreModule implements IModule {
|
|||
};
|
||||
}
|
||||
|
||||
private rmrf = (msg: MessageLike): boolean | Result => {
|
||||
@autobind
|
||||
private rmrf(msg: MessageLike): boolean | HandlerResult {
|
||||
if (!msg.includes(['rm -rf'])) return false;
|
||||
|
||||
msg.friend.decLove();
|
||||
|
@ -286,7 +301,8 @@ export default class CoreModule implements IModule {
|
|||
};
|
||||
}
|
||||
|
||||
private shutdown = (msg: MessageLike): boolean | Result => {
|
||||
@autobind
|
||||
private shutdown(msg: MessageLike): boolean | HandlerResult {
|
||||
if (!msg.includes(['shutdown'])) return false;
|
||||
|
||||
msg.reply(serifs.core.shutdown);
|
||||
|
@ -296,12 +312,13 @@ export default class CoreModule implements IModule {
|
|||
};
|
||||
}
|
||||
|
||||
public onReplyThisModule = (msg: MessageLike, data: any) => {
|
||||
@autobind
|
||||
private onContextReply(msg: MessageLike, data: any) {
|
||||
if (msg.text == null) return;
|
||||
|
||||
const done = () => {
|
||||
msg.reply(serifs.core.setNameOk(msg.friend.name));
|
||||
this.ai.unsubscribeReply(this, msg.userId);
|
||||
this.unsubscribeReply(msg.userId);
|
||||
};
|
||||
|
||||
if (msg.text.includes('はい')) {
|
||||
|
@ -312,7 +329,7 @@ export default class CoreModule implements IModule {
|
|||
done();
|
||||
} else {
|
||||
msg.reply(serifs.core.yesOrNo).then(reply => {
|
||||
this.ai.subscribeReply(this, msg.userId, msg.isMessage, reply.id, data);
|
||||
this.subscribeReply(msg.userId, msg.isMessage, reply.id, data);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
import 藍 from '../../ai';
|
||||
import IModule from '../../module';
|
||||
import autobind from 'autobind-decorator';
|
||||
import Module from '../../module';
|
||||
import MessageLike from '../../message-like';
|
||||
import serifs from '../../serifs';
|
||||
|
||||
export default class DiceModule implements IModule {
|
||||
export default class DiceModule extends Module {
|
||||
public readonly name = 'dice';
|
||||
private ai: 藍;
|
||||
|
||||
public install = (ai: 藍) => {
|
||||
this.ai = ai;
|
||||
@autobind
|
||||
public install() {
|
||||
return {
|
||||
onMention: this.onMention
|
||||
};
|
||||
}
|
||||
|
||||
public onMention = (msg: MessageLike) => {
|
||||
@autobind
|
||||
private onMention(msg: MessageLike) {
|
||||
if (msg.text == null) return false;
|
||||
|
||||
const query = msg.text.match(/([0-9]+)[dD]([0-9]+)/);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import 藍 from '../../ai';
|
||||
import IModule from '../../module';
|
||||
import autobind from 'autobind-decorator';
|
||||
import Module from '../../module';
|
||||
import MessageLike from '../../message-like';
|
||||
import serifs from '../../serifs';
|
||||
|
||||
|
@ -126,12 +126,18 @@ const faces = [
|
|||
'👽'
|
||||
]
|
||||
|
||||
export default class EmojiModule implements IModule {
|
||||
export default class EmojiModule extends Module {
|
||||
public readonly name = 'emoji';
|
||||
|
||||
public install = (ai: 藍) => { }
|
||||
@autobind
|
||||
public install() {
|
||||
return {
|
||||
onMention: this.onMention
|
||||
};
|
||||
}
|
||||
|
||||
public onMention = (msg: MessageLike) => {
|
||||
@autobind
|
||||
private onMention(msg: MessageLike) {
|
||||
if (msg.includes(['顔文字', '絵文字', 'emoji', '福笑い'])) {
|
||||
const hand = hands[Math.floor(Math.random() * hands.length)];
|
||||
const face = faces[Math.floor(Math.random() * faces.length)];
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
import 藍 from '../../ai';
|
||||
import IModule from '../../module';
|
||||
import autobind from 'autobind-decorator';
|
||||
import Module from '../../module';
|
||||
import MessageLike from '../../message-like';
|
||||
|
||||
export default class FollowModule implements IModule {
|
||||
export default class FollowModule extends Module {
|
||||
public readonly name = 'follow';
|
||||
private ai: 藍;
|
||||
|
||||
public install = (ai: 藍) => {
|
||||
this.ai = ai;
|
||||
@autobind
|
||||
public install() {
|
||||
return {
|
||||
onMention: this.onMention
|
||||
};
|
||||
}
|
||||
|
||||
public onMention = (msg: MessageLike) => {
|
||||
@autobind
|
||||
private onMention(msg: MessageLike) {
|
||||
if (msg.text && msg.includes(['フォロー', 'フォロバ', 'follow me'])) {
|
||||
if (!msg.user.isFollowing) {
|
||||
this.ai.api('following/create', {
|
||||
|
|
|
@ -1,16 +1,22 @@
|
|||
import 藍 from '../../ai';
|
||||
import IModule from '../../module';
|
||||
import autobind from 'autobind-decorator';
|
||||
import Module from '../../module';
|
||||
import MessageLike from '../../message-like';
|
||||
import serifs from '../../serifs';
|
||||
import * as seedrandom from 'seedrandom';
|
||||
import { blessing, itemPrefixes, items } from './vocabulary';
|
||||
|
||||
export default class FortuneModule implements IModule {
|
||||
export default class FortuneModule extends Module {
|
||||
public readonly name = 'fortune';
|
||||
|
||||
public install = (ai: 藍) => { }
|
||||
@autobind
|
||||
public install() {
|
||||
return {
|
||||
onMention: this.onMention
|
||||
};
|
||||
}
|
||||
|
||||
public onMention = (msg: MessageLike) => {
|
||||
@autobind
|
||||
private onMention(msg: MessageLike) {
|
||||
if (msg.includes(['占', 'うらな', '運勢', 'おみくじ'])) {
|
||||
const date = new Date();
|
||||
const seed = `${date.getFullYear()}/${date.getMonth()}/${date.getDate()}@${msg.userId}`;
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import autobind from 'autobind-decorator';
|
||||
import * as loki from 'lokijs';
|
||||
import 藍 from '../../ai';
|
||||
import IModule from '../../module';
|
||||
import Module from '../../module';
|
||||
import MessageLike from '../../message-like';
|
||||
import serifs from '../../serifs';
|
||||
import getCollection from '../../utils/get-collection';
|
||||
|
||||
export default class GuessingGameModule implements IModule {
|
||||
export default class GuessingGameModule extends Module {
|
||||
public readonly name = 'guessingGame';
|
||||
private ai: 藍;
|
||||
private guesses: loki.Collection<{
|
||||
userId: string;
|
||||
secret: number;
|
||||
|
@ -17,17 +16,22 @@ export default class GuessingGameModule implements IModule {
|
|||
endedAt: number;
|
||||
}>;
|
||||
|
||||
public install = (ai: 藍) => {
|
||||
this.ai = ai;
|
||||
|
||||
@autobind
|
||||
public install() {
|
||||
//#region Init DB
|
||||
this.guesses = getCollection(this.ai.db, 'guessingGame', {
|
||||
indices: ['userId']
|
||||
});
|
||||
//#endregion
|
||||
|
||||
return {
|
||||
onMention: this.onMention,
|
||||
onContextReply: this.onContextReply
|
||||
};
|
||||
}
|
||||
|
||||
public onMention = (msg: MessageLike) => {
|
||||
@autobind
|
||||
private onMention(msg: MessageLike) {
|
||||
if (msg.includes(['数当て', '数あて'])) {
|
||||
const exist = this.guesses.findOne({
|
||||
userId: msg.userId,
|
||||
|
@ -56,7 +60,7 @@ export default class GuessingGameModule implements IModule {
|
|||
});
|
||||
|
||||
msg.reply(serifs.guessingGame.started).then(reply => {
|
||||
this.ai.subscribeReply(this, msg.userId, msg.isMessage, msg.isMessage ? msg.userId : reply.id);
|
||||
this.subscribeReply(msg.userId, msg.isMessage, msg.isMessage ? msg.userId : reply.id);
|
||||
});
|
||||
|
||||
return true;
|
||||
|
@ -65,7 +69,8 @@ export default class GuessingGameModule implements IModule {
|
|||
}
|
||||
}
|
||||
|
||||
public onReplyThisModule = (msg: MessageLike) => {
|
||||
@autobind
|
||||
private onContextReply(msg: MessageLike) {
|
||||
if (msg.text == null) return;
|
||||
|
||||
const exist = this.guesses.findOne({
|
||||
|
@ -78,7 +83,7 @@ export default class GuessingGameModule implements IModule {
|
|||
exist.isEnded = true;
|
||||
exist.endedAt = Date.now();
|
||||
this.guesses.update(exist);
|
||||
this.ai.unsubscribeReply(this, msg.userId);
|
||||
this.unsubscribeReply(msg.userId);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -86,7 +91,7 @@ export default class GuessingGameModule implements IModule {
|
|||
|
||||
if (guess == null) {
|
||||
msg.reply(serifs.guessingGame.nan).then(reply => {
|
||||
this.ai.subscribeReply(this, msg.userId, msg.isMessage, reply.id);
|
||||
this.subscribeReply(msg.userId, msg.isMessage, reply.id);
|
||||
});
|
||||
} else {
|
||||
if (guess.length > 3) return;
|
||||
|
@ -116,14 +121,14 @@ export default class GuessingGameModule implements IModule {
|
|||
if (end) {
|
||||
exist.isEnded = true;
|
||||
exist.endedAt = Date.now();
|
||||
this.ai.unsubscribeReply(this, msg.userId);
|
||||
this.unsubscribeReply(msg.userId);
|
||||
}
|
||||
|
||||
this.guesses.update(exist);
|
||||
|
||||
msg.reply(text).then(reply => {
|
||||
if (!end) {
|
||||
this.ai.subscribeReply(this, msg.userId, msg.isMessage, reply.id);
|
||||
this.subscribeReply(msg.userId, msg.isMessage, reply.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import autobind from 'autobind-decorator';
|
||||
import * as loki from 'lokijs';
|
||||
import 藍 from '../../ai';
|
||||
import IModule from '../../module';
|
||||
import Module from '../../module';
|
||||
import config from '../../config';
|
||||
import serifs from '../../serifs';
|
||||
import getCollection from '../../utils/get-collection';
|
||||
|
@ -13,19 +13,17 @@ function kanaToHira(str: string) {
|
|||
});
|
||||
}
|
||||
|
||||
export default class KeywordModule implements IModule {
|
||||
export default class KeywordModule extends Module {
|
||||
public readonly name = 'keyword';
|
||||
|
||||
private ai: 藍;
|
||||
private tokenizer: any;
|
||||
private learnedKeywords: loki.Collection<{
|
||||
keyword: string;
|
||||
learnedAt: number;
|
||||
}>;
|
||||
|
||||
public install = (ai: 藍) => {
|
||||
this.ai = ai;
|
||||
|
||||
@autobind
|
||||
public install() {
|
||||
//#region Init DB
|
||||
this.learnedKeywords = getCollection(this.ai.db, '_keyword_learnedKeywords', {
|
||||
indices: ['userId']
|
||||
|
@ -36,9 +34,12 @@ export default class KeywordModule implements IModule {
|
|||
this.tokenizer.command = config.mecab;
|
||||
|
||||
setInterval(this.say, 1000 * 60 * 60);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
private say = async () => {
|
||||
@autobind
|
||||
private async say() {
|
||||
const tl = await this.ai.api('notes/local-timeline', {
|
||||
limit: 30
|
||||
});
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
import 藍 from '../../ai';
|
||||
import IModule from '../../module';
|
||||
import autobind from 'autobind-decorator';
|
||||
import Module from '../../module';
|
||||
import MessageLike from '../../message-like';
|
||||
|
||||
export default class PingModule implements IModule {
|
||||
export default class PingModule extends Module {
|
||||
public readonly name = 'ping';
|
||||
|
||||
public install = (ai: 藍) => { }
|
||||
@autobind
|
||||
public install() {
|
||||
return {
|
||||
onMention: this.onMention
|
||||
};
|
||||
}
|
||||
|
||||
public onMention = (msg: MessageLike) => {
|
||||
@autobind
|
||||
private onMention(msg: MessageLike) {
|
||||
if (msg.text && msg.text.includes('ping')) {
|
||||
msg.reply('PONG!');
|
||||
return true;
|
||||
|
|
|
@ -1,27 +1,23 @@
|
|||
import * as childProcess from 'child_process';
|
||||
import 藍 from '../../ai';
|
||||
import IModule from '../../module';
|
||||
import autobind from 'autobind-decorator';
|
||||
import Module from '../../module';
|
||||
import serifs from '../../serifs';
|
||||
import config from '../../config';
|
||||
import MessageLike from '../../message-like';
|
||||
import * as WebSocket from 'ws';
|
||||
import Friend from '../../friend';
|
||||
import getDate from '../../utils/get-date';
|
||||
|
||||
export default class ReversiModule implements IModule {
|
||||
export default class ReversiModule extends Module {
|
||||
public readonly name = 'reversi';
|
||||
|
||||
private ai: 藍;
|
||||
|
||||
/**
|
||||
* リバーシストリーム
|
||||
*/
|
||||
private reversiConnection?: any;
|
||||
|
||||
public install = (ai: 藍) => {
|
||||
if (!config.reversiEnabled) return;
|
||||
|
||||
this.ai = ai;
|
||||
@autobind
|
||||
public install() {
|
||||
if (!config.reversiEnabled) return {};
|
||||
|
||||
this.reversiConnection = this.ai.connection.useSharedConnection('gamesReversi');
|
||||
|
||||
|
@ -30,9 +26,14 @@ export default class ReversiModule implements IModule {
|
|||
|
||||
// マッチしたとき
|
||||
this.reversiConnection.on('matched', msg => this.onReversiGameStart(msg));
|
||||
|
||||
return {
|
||||
onMention: this.onMention
|
||||
};
|
||||
}
|
||||
|
||||
public onMention = (msg: MessageLike) => {
|
||||
@autobind
|
||||
private onMention(msg: MessageLike) {
|
||||
if (msg.includes(['リバーシ', 'オセロ', 'reversi', 'othello'])) {
|
||||
if (config.reversiEnabled) {
|
||||
msg.reply(serifs.reversi.ok);
|
||||
|
@ -50,8 +51,9 @@ export default class ReversiModule implements IModule {
|
|||
}
|
||||
}
|
||||
|
||||
private onReversiInviteMe = async (inviter: any) => {
|
||||
console.log(`Someone invited me: @${inviter.username}`);
|
||||
@autobind
|
||||
private async onReversiInviteMe(inviter: any) {
|
||||
this.log(`Someone invited me: @${inviter.username}`);
|
||||
|
||||
if (config.reversiEnabled) {
|
||||
// 承認
|
||||
|
@ -65,8 +67,9 @@ export default class ReversiModule implements IModule {
|
|||
}
|
||||
}
|
||||
|
||||
private onReversiGameStart = (game: any) => {
|
||||
console.log('enter reversi game room');
|
||||
@autobind
|
||||
private onReversiGameStart(game: any) {
|
||||
this.log('enter reversi game room');
|
||||
|
||||
// ゲームストリームに接続
|
||||
const gw = this.ai.connection.connectToChannel('gamesReversiGame', {
|
||||
|
@ -144,6 +147,7 @@ export default class ReversiModule implements IModule {
|
|||
}, 2000);
|
||||
}
|
||||
|
||||
@autobind
|
||||
private onGameEnded(game: any) {
|
||||
const user = game.user1Id == this.ai.account.id ? game.user2 : game.user1;
|
||||
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import 藍 from '../../ai';
|
||||
import IModule from '../../module';
|
||||
import autobind from 'autobind-decorator';
|
||||
import Module from '../../module';
|
||||
import serifs from '../../serifs';
|
||||
import config from '../../config';
|
||||
|
||||
export default class ServerModule implements IModule {
|
||||
export default class ServerModule extends Module {
|
||||
public readonly name = 'server';
|
||||
|
||||
private ai: 藍;
|
||||
private connection?: any;
|
||||
private recentStat: any;
|
||||
private warned = false;
|
||||
|
@ -17,10 +16,9 @@ export default class ServerModule implements IModule {
|
|||
*/
|
||||
private statsLogs: any[] = [];
|
||||
|
||||
public install = (ai: 藍) => {
|
||||
if (!config.serverMonitoring) return;
|
||||
|
||||
this.ai = ai;
|
||||
@autobind
|
||||
public install() {
|
||||
if (!config.serverMonitoring) return {};
|
||||
|
||||
this.connection = this.ai.connection.useSharedConnection('serverStats');
|
||||
this.connection.on('stats', this.onStats);
|
||||
|
@ -33,9 +31,12 @@ export default class ServerModule implements IModule {
|
|||
setInterval(() => {
|
||||
this.check();
|
||||
}, 3000);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
private check = () => {
|
||||
@autobind
|
||||
private check() {
|
||||
const average = (arr) => arr.reduce((a, b) => a + b) / arr.length;
|
||||
|
||||
const cpuPercentages = this.statsLogs.map(s => s && s.cpu_usage * 100 || 0);
|
||||
|
@ -47,11 +48,13 @@ export default class ServerModule implements IModule {
|
|||
}
|
||||
}
|
||||
|
||||
private onStats = async (stats: any) => {
|
||||
@autobind
|
||||
private async onStats(stats: any) {
|
||||
this.recentStat = stats.body;
|
||||
}
|
||||
|
||||
private warn = () => {
|
||||
@autobind
|
||||
private warn() {
|
||||
//#region 前に警告したときから一旦落ち着いた状態を経験していなければ警告しない
|
||||
// 常に負荷が高いようなサーバーで無限に警告し続けるのを防ぐため
|
||||
if (this.warned) return;
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
import 藍 from '../../ai';
|
||||
import IModule from '../../module';
|
||||
import autobind from 'autobind-decorator';
|
||||
import Module from '../../module';
|
||||
import MessageLike from '../../message-like';
|
||||
import serifs from '../../serifs';
|
||||
|
||||
export default class TimerModule implements IModule {
|
||||
export default class TimerModule extends Module {
|
||||
public readonly name = 'timer';
|
||||
private ai: 藍;
|
||||
|
||||
public install = (ai: 藍) => {
|
||||
this.ai = ai;
|
||||
@autobind
|
||||
public install() {
|
||||
return {
|
||||
onMention: this.onMention
|
||||
};
|
||||
}
|
||||
|
||||
public onMention = (msg: MessageLike) => {
|
||||
@autobind
|
||||
private onMention(msg: MessageLike) {
|
||||
const secondsQuery = (msg.text || '').match(/([0-9]+)秒/);
|
||||
const minutesQuery = (msg.text || '').match(/([0-9]+)分/);
|
||||
const hoursQuery = (msg.text || '').match(/([0-9]+)時間/);
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
import 藍 from '../../ai';
|
||||
import IModule from '../../module';
|
||||
import autobind from 'autobind-decorator';
|
||||
import Module from '../../module';
|
||||
import Friend from '../../friend';
|
||||
import serifs from '../../serifs';
|
||||
|
||||
export default class ValentineModule implements IModule {
|
||||
export default class ValentineModule extends Module {
|
||||
public readonly name = 'valentine';
|
||||
|
||||
private ai: 藍;
|
||||
|
||||
public install = (ai: 藍) => {
|
||||
this.ai = ai;
|
||||
|
||||
@autobind
|
||||
public install() {
|
||||
this.crawleValentine();
|
||||
setInterval(this.crawleValentine, 1000 * 60 * 3);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* チョコ配り
|
||||
*/
|
||||
private crawleValentine = () => {
|
||||
@autobind
|
||||
private crawleValentine() {
|
||||
const now = new Date();
|
||||
|
||||
const isValentine = now.getMonth() == 1 && now.getDate() == 14;
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
import 藍 from '../../ai';
|
||||
import IModule from '../../module';
|
||||
import autobind from 'autobind-decorator';
|
||||
import Module from '../../module';
|
||||
|
||||
export default class WelcomeModule implements IModule {
|
||||
export default class WelcomeModule extends Module {
|
||||
public readonly name = 'welcome';
|
||||
|
||||
private ai: 藍;
|
||||
|
||||
public install = (ai: 藍) => {
|
||||
this.ai = ai;
|
||||
|
||||
@autobind
|
||||
public install() {
|
||||
const tl = this.ai.connection.useSharedConnection('localTimeline');
|
||||
|
||||
tl.on('note', this.onLocalNote);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
public onLocalNote = (note: any) => {
|
||||
@autobind
|
||||
private onLocalNote(note: any) {
|
||||
if (note.isFirstNote) {
|
||||
setTimeout(() => {
|
||||
this.ai.api('notes/create', {
|
||||
|
|
Loading…
Reference in a new issue