mirror of
https://github.com/syuilo/ai.git
synced 2024-12-22 08:21:08 +00:00
数当てゲームを実装するなど
This commit is contained in:
parent
1bf139b204
commit
24af1d80ae
15
package-lock.json
generated
15
package-lock.json
generated
|
@ -8,6 +8,11 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz",
|
||||
"integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA=="
|
||||
},
|
||||
"@types/lokijs": {
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/lokijs/-/lokijs-1.5.2.tgz",
|
||||
"integrity": "sha512-ZF14v1P1Bjbw8VJRu+p4WS9V926CAOjWF4yq23QmSBWRPe0/GXlUKzSxjP1fi/xi8nrq6zr9ECo8Z/8KsRqroQ=="
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "10.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.0.5.tgz",
|
||||
|
@ -314,6 +319,11 @@
|
|||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
|
||||
"integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg=="
|
||||
},
|
||||
"lokijs": {
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/lokijs/-/lokijs-1.5.5.tgz",
|
||||
"integrity": "sha1-HCH4KvdXkDf63nueSBNIXCNwi7Y="
|
||||
},
|
||||
"make-error": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.4.tgz",
|
||||
|
@ -514,6 +524,11 @@
|
|||
"has-flag": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"timeout-as-promise": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/timeout-as-promise/-/timeout-as-promise-1.0.0.tgz",
|
||||
"integrity": "sha1-c2foEfyZKs/Nzaq/LlDfr4shV28="
|
||||
},
|
||||
"tough-cookie": {
|
||||
"version": "2.4.3",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
|
||||
|
|
|
@ -5,16 +5,19 @@
|
|||
"build": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/lokijs": "1.5.2",
|
||||
"@types/node": "10.0.5",
|
||||
"@types/promise-retry": "1.1.2",
|
||||
"@types/seedrandom": "2.4.27",
|
||||
"@types/ws": "5.1.2",
|
||||
"lokijs": "1.5.5",
|
||||
"misskey-reversi": "0.0.5",
|
||||
"promise-retry": "1.1.1",
|
||||
"reconnecting-websocket": "4.0.0-rc5",
|
||||
"request": "2.87.0",
|
||||
"request-promise-native": "1.0.5",
|
||||
"seedrandom": "2.4.3",
|
||||
"timeout-as-promise": "1.0.0",
|
||||
"ts-node": "6.0.3",
|
||||
"typescript": "2.8.3",
|
||||
"ws": "6.0.0"
|
||||
|
|
48
src/ai.ts
48
src/ai.ts
|
@ -6,13 +6,14 @@ import serifs from './serifs';
|
|||
import config from './config';
|
||||
import IModule from './module';
|
||||
import MessageLike from './message-like';
|
||||
import { contexts } from './memory';
|
||||
const ReconnectingWebSocket = require('../node_modules/reconnecting-websocket/dist/reconnecting-websocket-cjs.js');
|
||||
|
||||
/**
|
||||
* 藍
|
||||
*/
|
||||
export default class 藍 {
|
||||
private account: any;
|
||||
public account: any;
|
||||
|
||||
/**
|
||||
* ホームストリーム
|
||||
|
@ -99,17 +100,31 @@ export default class 藍 {
|
|||
}
|
||||
}, 1000);
|
||||
|
||||
this.modules.filter(m => m.hasOwnProperty('onMention')).some(m => {
|
||||
return m.onMention(msg);
|
||||
const context = !msg.isMessage && msg.replyId == null ? null : contexts.findOne(msg.isMessage ? {
|
||||
isMessage: true,
|
||||
userId: msg.userId
|
||||
} : {
|
||||
isMessage: false,
|
||||
noteId: msg.replyId
|
||||
});
|
||||
|
||||
if (context != null) {
|
||||
const module = this.modules.find(m => m.name == context.module);
|
||||
module.onReplyThisModule(msg);
|
||||
} else {
|
||||
this.modules.filter(m => m.hasOwnProperty('onMention')).some(m => {
|
||||
return m.onMention(msg);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public post = (param: any) => {
|
||||
this.api('notes/create', param);
|
||||
public post = async (param: any) => {
|
||||
const res = await this.api('notes/create', param);
|
||||
return res.createdNote;
|
||||
}
|
||||
|
||||
public sendMessage = (userId: any, param: any) => {
|
||||
this.api('messaging/messages/create', Object.assign({
|
||||
return this.api('messaging/messages/create', Object.assign({
|
||||
userId: userId,
|
||||
}, param));
|
||||
}
|
||||
|
@ -121,4 +136,25 @@ export default class 藍 {
|
|||
}, param)
|
||||
});
|
||||
};
|
||||
|
||||
public subscribeReply = (module: IModule, key: string, isMessage: boolean, id: string) => {
|
||||
contexts.insertOne(isMessage ? {
|
||||
isMessage: true,
|
||||
userId: id,
|
||||
module: module.name,
|
||||
key: key,
|
||||
} : {
|
||||
isMessage: false,
|
||||
noteId: id,
|
||||
module: module.name,
|
||||
key: key,
|
||||
});
|
||||
}
|
||||
|
||||
public unsubscribeReply = (module: IModule, key: string) => {
|
||||
contexts.findAndRemove({
|
||||
key: key,
|
||||
module: module.name
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import ServerModule from './modules/server';
|
|||
import PingModule from './modules/ping';
|
||||
import EmojiModule from './modules/emoji';
|
||||
import FortuneModule from './modules/fortune';
|
||||
import GuessingGameModule from './modules/guessing-game';
|
||||
import * as request from 'request-promise-native';
|
||||
const promiseRetry = require('promise-retry');
|
||||
|
||||
|
@ -20,6 +21,7 @@ promiseRetry(retry => {
|
|||
ai.install(new PingModule());
|
||||
ai.install(new EmojiModule());
|
||||
ai.install(new FortuneModule());
|
||||
ai.install(new GuessingGameModule());
|
||||
ai.install(new ServerModule());
|
||||
ai.install(new ReversiModule());
|
||||
});
|
||||
|
|
17
src/memory.ts
Normal file
17
src/memory.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
// 藍の記憶
|
||||
|
||||
import * as loki from 'lokijs';
|
||||
|
||||
const db = new loki('ai');
|
||||
|
||||
export default db;
|
||||
|
||||
export const contexts = db.addCollection<{
|
||||
isMessage: boolean;
|
||||
noteId?: string;
|
||||
userId?: string;
|
||||
module: string;
|
||||
key: string;
|
||||
}>('contexts', {
|
||||
indices: ['key']
|
||||
});
|
|
@ -1,4 +1,5 @@
|
|||
import 藍 from './ai';
|
||||
const delay = require('timeout-as-promise');
|
||||
|
||||
export default class MessageLike {
|
||||
private ai: 藍;
|
||||
|
@ -21,27 +22,31 @@ export default class MessageLike {
|
|||
return this.messageOrNote.text;
|
||||
}
|
||||
|
||||
public get replyId() {
|
||||
return this.messageOrNote.replyId;
|
||||
}
|
||||
|
||||
constructor(ai: 藍, messageOrNote: any, isMessage: boolean) {
|
||||
this.ai = ai;
|
||||
this.messageOrNote = messageOrNote;
|
||||
this.isMessage = isMessage;
|
||||
}
|
||||
|
||||
public reply = (text: string, cw?: string) => {
|
||||
public reply = async (text: string, cw?: string) => {
|
||||
console.log(`sending reply of ${this.id} ...`);
|
||||
|
||||
setTimeout(() => {
|
||||
if (this.isMessage) {
|
||||
this.ai.sendMessage(this.messageOrNote.userId, {
|
||||
text: text
|
||||
});
|
||||
} else {
|
||||
this.ai.post({
|
||||
replyId: this.messageOrNote.id,
|
||||
text: text,
|
||||
cw: cw
|
||||
});
|
||||
}
|
||||
}, 2000);
|
||||
await delay(2000);
|
||||
|
||||
if (this.isMessage) {
|
||||
return await this.ai.sendMessage(this.messageOrNote.userId, {
|
||||
text: text
|
||||
});
|
||||
} else {
|
||||
return await this.ai.post({
|
||||
replyId: this.messageOrNote.id,
|
||||
text: text,
|
||||
cw: cw
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ import 藍 from './ai';
|
|||
import MessageLike from './message-like';
|
||||
|
||||
export default interface IModule {
|
||||
name: string;
|
||||
install?: (ai: 藍) => void;
|
||||
onMention?: (msg: MessageLike) => boolean;
|
||||
onReplyThisModule?: (msg: MessageLike) => void;
|
||||
}
|
||||
|
|
|
@ -119,10 +119,12 @@ const faces = [
|
|||
]
|
||||
|
||||
export default class EmojiModule implements IModule {
|
||||
public name = 'emoji';
|
||||
|
||||
public install = (ai: 藍) => { }
|
||||
|
||||
public onMention = (msg: MessageLike) => {
|
||||
if (msg.text && msg.text.includes('絵文字')) {
|
||||
if (msg.text && (msg.text.includes('絵文字') || msg.text.includes('emoji'))) {
|
||||
const hand = hands[Math.floor(Math.random() * hands.length)];
|
||||
const face = faces[Math.floor(Math.random() * faces.length)];
|
||||
const emoji = Array.isArray(hand) ? hand[0] + face + hand[1] : hand + face + hand;
|
||||
|
|
|
@ -24,7 +24,9 @@ const items = [
|
|||
'寿司'
|
||||
];
|
||||
|
||||
export default class EmojiModule implements IModule {
|
||||
export default class FortuneModule implements IModule {
|
||||
public name = 'fortune';
|
||||
|
||||
public install = (ai: 藍) => { }
|
||||
|
||||
public onMention = (msg: MessageLike) => {
|
||||
|
|
118
src/modules/guessing-game/index.ts
Normal file
118
src/modules/guessing-game/index.ts
Normal file
|
@ -0,0 +1,118 @@
|
|||
import 藍 from '../../ai';
|
||||
import IModule from '../../module';
|
||||
import MessageLike from '../../message-like';
|
||||
import serifs from '../../serifs';
|
||||
import db from '../../memory';
|
||||
|
||||
export const guesses = db.addCollection<{
|
||||
userId: string;
|
||||
secret: number;
|
||||
tries: number[];
|
||||
isEnded: boolean;
|
||||
startedAt: number;
|
||||
endedAt: number;
|
||||
}>('guessingGame', {
|
||||
indices: ['userId']
|
||||
});
|
||||
|
||||
export default class GuessingGameModule implements IModule {
|
||||
public name = 'guessingGame';
|
||||
private ai: 藍;
|
||||
|
||||
public install = (ai: 藍) => {
|
||||
this.ai = ai;
|
||||
}
|
||||
|
||||
public onMention = (msg: MessageLike) => {
|
||||
if (msg.text && msg.text.includes('数当て')) {
|
||||
const exist = guesses.findOne({
|
||||
userId: msg.userId,
|
||||
isEnded: false
|
||||
});
|
||||
|
||||
if (exist != null) {
|
||||
msg.reply(serifs.GUESSINGGAME_ARLEADY_STARTED);
|
||||
} else {
|
||||
const secret = Math.floor(Math.random() * 100);
|
||||
|
||||
guesses.insertOne({
|
||||
userId: msg.userId,
|
||||
secret: secret,
|
||||
tries: [],
|
||||
isEnded: false,
|
||||
startedAt: Date.now(),
|
||||
endedAt: null
|
||||
});
|
||||
|
||||
msg.reply(serifs.GUESSINGGAME_STARTED).then(reply => {
|
||||
this.ai.subscribeReply(this, msg.userId, msg.isMessage, msg.isMessage ? msg.userId : reply.id);
|
||||
});
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public onReplyThisModule = (msg: MessageLike) => {
|
||||
if (msg.text == null) return;
|
||||
|
||||
const exist = guesses.findOne({
|
||||
userId: msg.userId,
|
||||
isEnded: false
|
||||
});
|
||||
|
||||
if (msg.text.includes('やめ')) {
|
||||
msg.reply(serifs.GUESSINGGAME_CANCEL);
|
||||
exist.isEnded = true;
|
||||
exist.endedAt = Date.now();
|
||||
guesses.update(exist);
|
||||
this.ai.unsubscribeReply(this, msg.userId);
|
||||
return;
|
||||
}
|
||||
|
||||
const guess = msg.text.toLowerCase().replace(this.ai.account.username.toLowerCase(), '').match(/[0-9]+/);
|
||||
|
||||
if (guess == null) {
|
||||
msg.reply(serifs.GUESSINGGAME_NAN).then(reply => {
|
||||
this.ai.subscribeReply(this, msg.userId, msg.isMessage, reply.id);
|
||||
});
|
||||
} else {
|
||||
const g = parseInt(guess, 10);
|
||||
|
||||
const firsttime = exist.tries.indexOf(g) === -1;
|
||||
|
||||
let text: string;
|
||||
let end = false;
|
||||
|
||||
if (exist.secret < g) {
|
||||
text = firsttime
|
||||
? serifs.GUESSINGGAME_LESS.replace('$', g.toString())
|
||||
: serifs.GUESSINGGAME_LESS_AGAIN.replace('$', g.toString());
|
||||
} else if (exist.secret > g) {
|
||||
text = firsttime
|
||||
? serifs.GUESSINGGAME_GRATER.replace('$', g.toString())
|
||||
: serifs.GUESSINGGAME_GRATER_AGAIN.replace('$', g.toString());
|
||||
} else {
|
||||
end = true;
|
||||
text = serifs.GUESSINGGAME_CONGRATS.replace('{tries}', exist.tries.length.toString());
|
||||
}
|
||||
|
||||
if (end) {
|
||||
exist.isEnded = true;
|
||||
exist.endedAt = Date.now();
|
||||
guesses.update(exist);
|
||||
this.ai.unsubscribeReply(this, msg.userId);
|
||||
} else {
|
||||
exist.tries.push(g);
|
||||
guesses.update(exist);
|
||||
}
|
||||
|
||||
msg.reply(text).then(reply => {
|
||||
if (!end) {
|
||||
this.ai.subscribeReply(this, msg.userId, msg.isMessage, reply.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,8 @@ import IModule from '../../module';
|
|||
import MessageLike from '../../message-like';
|
||||
|
||||
export default class PingModule implements IModule {
|
||||
public name = 'ping';
|
||||
|
||||
public install = (ai: 藍) => { }
|
||||
|
||||
public onMention = (msg: MessageLike) => {
|
||||
|
|
|
@ -8,6 +8,8 @@ import MessageLike from '../../message-like';
|
|||
import * as WebSocket from 'ws';
|
||||
|
||||
export default class ReversiModule implements IModule {
|
||||
public name = 'reversi';
|
||||
|
||||
private ai: 藍;
|
||||
|
||||
/**
|
||||
|
@ -38,7 +40,7 @@ export default class ReversiModule implements IModule {
|
|||
}
|
||||
|
||||
public onMention = (msg: MessageLike) => {
|
||||
if (msg.text && msg.text.includes('リバーシ')) {
|
||||
if (msg.text && (msg.text.includes('リバーシ') || msg.text.toLowerCase().includes('reversi'))) {
|
||||
if (config.reversiEnabled) {
|
||||
msg.reply(serifs.REVERSI_OK);
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@ import MessageLike from '../../message-like';
|
|||
const ReconnectingWebSocket = require('../../../node_modules/reconnecting-websocket/dist/reconnecting-websocket-cjs.js');
|
||||
|
||||
export default class ServerModule implements IModule {
|
||||
public name = 'server';
|
||||
|
||||
private ai: 藍;
|
||||
private connection?: any;
|
||||
private preventScheduleReboot = false;
|
||||
|
|
|
@ -35,5 +35,50 @@ export default {
|
|||
*/
|
||||
EMOJI_SUGGEST: 'こんなのはどうですか?→$',
|
||||
|
||||
FORTUNE_CW: '私が今日のあなたの運勢を占いました...'
|
||||
FORTUNE_CW: '私が今日のあなたの運勢を占いました...',
|
||||
|
||||
/**
|
||||
* 数当てゲームをやろうと言われたけど既にやっているとき
|
||||
*/
|
||||
GUESSINGGAME_ARLEADY_STARTED: 'え、ゲームは既に始まってますよ!',
|
||||
|
||||
/**
|
||||
* 数当てゲーム開始
|
||||
*/
|
||||
GUESSINGGAME_STARTED: '0~100の秘密の数を当ててみてください♪',
|
||||
|
||||
/**
|
||||
* 数当てゲームで数字じゃない返信があったとき
|
||||
*/
|
||||
GUESSINGGAME_NAN: '数字でお願いします!「やめる」と言ってゲームをやめることもできますよ!',
|
||||
|
||||
/**
|
||||
* 数当てゲーム中止を要求されたとき
|
||||
*/
|
||||
GUESSINGGAME_CANCEL: 'わかりました~。ありがとうございました♪',
|
||||
|
||||
/**
|
||||
* 数当てゲームで小さい数を言われたとき
|
||||
*/
|
||||
GUESSINGGAME_GRATER: '$より大きいですね',
|
||||
|
||||
/**
|
||||
* 数当てゲームで小さい数を言われたとき(2度目)
|
||||
*/
|
||||
GUESSINGGAME_GRATER_AGAIN: 'もう一度言いますが$より大きいですよ!',
|
||||
|
||||
/**
|
||||
* 数当てゲームで大きい数を言われたとき
|
||||
*/
|
||||
GUESSINGGAME_LESS: '$より小さいですね',
|
||||
|
||||
/**
|
||||
* 数当てゲームで大きい数を言われたとき(2度目)
|
||||
*/
|
||||
GUESSINGGAME_LESS_AGAIN: 'もう一度言いますが$より小さいですよ!',
|
||||
|
||||
/**
|
||||
* 数当てゲームで正解したとき
|
||||
*/
|
||||
GUESSINGGAME_CONGRATS: '正解です🎉 ({tries}回目で当てました)',
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue