mirror of
https://github.com/syuilo/ai.git
synced 2024-11-25 06:01:07 +00:00
数取りゲーム
This commit is contained in:
parent
e88bffa93c
commit
69f271cae5
|
@ -99,16 +99,21 @@ export default class 藍 {
|
|||
const mainStream = this.connection.useSharedConnection('main');
|
||||
|
||||
// メンションされたとき
|
||||
mainStream.on('mention', data => {
|
||||
mainStream.on('mention', async data => {
|
||||
if (data.userId == this.account.id) return; // 自分は弾く
|
||||
if (data.text && data.text.startsWith('@' + this.account.username)) {
|
||||
// Misskeyのバグで投稿が非公開扱いになる
|
||||
if (data.text == null) data = await this.api('notes/show', { noteId: data.id });
|
||||
this.onReceiveMessage(new Message(this, data, false));
|
||||
}
|
||||
});
|
||||
|
||||
// 返信されたとき
|
||||
mainStream.on('reply', data => {
|
||||
mainStream.on('reply', async data => {
|
||||
if (data.userId == this.account.id) return; // 自分は弾く
|
||||
if (data.text && data.text.startsWith('@' + this.account.username)) return;
|
||||
// Misskeyのバグで投稿が非公開扱いになる
|
||||
if (data.text == null) data = await this.api('notes/show', { noteId: data.id });
|
||||
this.onReceiveMessage(new Message(this, data, false));
|
||||
});
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import PingModule from './modules/ping';
|
|||
import EmojiModule from './modules/emoji';
|
||||
import FortuneModule from './modules/fortune';
|
||||
import GuessingGameModule from './modules/guessing-game';
|
||||
import KazutoriModule from './modules/kazutori';
|
||||
import KeywordModule from './modules/keyword';
|
||||
import WelcomeModule from './modules/welcome';
|
||||
import TimerModule from './modules/timer';
|
||||
|
@ -46,6 +47,7 @@ promiseRetry(retry => {
|
|||
new EmojiModule(),
|
||||
new FortuneModule(),
|
||||
new GuessingGameModule(),
|
||||
new KazutoriModule(),
|
||||
new ReversiModule(),
|
||||
new TimerModule(),
|
||||
new DiceModule(),
|
||||
|
|
|
@ -50,7 +50,7 @@ export default class Message {
|
|||
}
|
||||
|
||||
@autobind
|
||||
public async reply(text: string, cw?: string) {
|
||||
public async reply(text: string, cw?: string, renote?: string) {
|
||||
if (text == null) return;
|
||||
|
||||
this.ai.log(`>>> Sending reply to ${chalk.underline(this.id)}`);
|
||||
|
@ -65,7 +65,8 @@ export default class Message {
|
|||
return await this.ai.post({
|
||||
replyId: this.messageOrNote.id,
|
||||
text: text,
|
||||
cw: cw
|
||||
cw: cw,
|
||||
renoteId: renote
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ export type User = {
|
|||
id: string;
|
||||
name: string;
|
||||
username: string;
|
||||
host: string;
|
||||
isFollowing: boolean;
|
||||
isBot: boolean;
|
||||
};
|
||||
|
|
176
src/modules/kazutori/index.ts
Normal file
176
src/modules/kazutori/index.ts
Normal file
|
@ -0,0 +1,176 @@
|
|||
import autobind from 'autobind-decorator';
|
||||
import * as loki from 'lokijs';
|
||||
import Module from '../../module';
|
||||
import Message from '../../message';
|
||||
import serifs from '../../serifs';
|
||||
import getCollection from '../../utils/get-collection';
|
||||
import { User } from '../../misskey/user';
|
||||
|
||||
type Game = {
|
||||
votes: {
|
||||
user: User;
|
||||
number: number;
|
||||
}[];
|
||||
isEnded: boolean;
|
||||
startedAt: number;
|
||||
postId: string;
|
||||
};
|
||||
|
||||
export default class extends Module {
|
||||
public readonly name = 'kazutori';
|
||||
|
||||
private games: loki.Collection<Game>;
|
||||
|
||||
@autobind
|
||||
public install() {
|
||||
this.games = getCollection(this.ai.db, 'kazutori');
|
||||
|
||||
this.crawleGameEnd();
|
||||
setInterval(this.crawleGameEnd, 1000);
|
||||
|
||||
return {
|
||||
mentionHook: this.mentionHook,
|
||||
contextHook: this.contextHook
|
||||
};
|
||||
}
|
||||
|
||||
@autobind
|
||||
private async mentionHook(msg: Message) {
|
||||
if (!msg.includes(['数取り'])) return false;
|
||||
|
||||
const games = this.games.find({});
|
||||
|
||||
const recentGame = games.length == 0 ? null : games[games.length - 1];
|
||||
|
||||
if (recentGame) {
|
||||
// 現在アクティブなゲームがある場合
|
||||
if (!recentGame.isEnded) {
|
||||
msg.reply(serifs.kazutori.alreadyStarted, null, recentGame.postId);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 直近のゲームから1時間経ってない場合
|
||||
if (Date.now() - recentGame.startedAt < 1000 * 60 * 60) {
|
||||
msg.reply(serifs.kazutori.matakondo);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const post = await this.ai.post({
|
||||
text: serifs.kazutori.intro
|
||||
});
|
||||
|
||||
this.games.insertOne({
|
||||
votes: [],
|
||||
isEnded: false,
|
||||
startedAt: Date.now(),
|
||||
postId: post.id
|
||||
});
|
||||
|
||||
this.subscribeReply(null, false, post.id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@autobind
|
||||
private async contextHook(msg: Message) {
|
||||
if (msg.text == null) return;
|
||||
|
||||
const game = this.games.findOne({
|
||||
isEnded: false
|
||||
});
|
||||
|
||||
// 既に数字を取っていたら
|
||||
if (game.votes.some(x => x.user.id == msg.userId)) return;
|
||||
|
||||
const match = msg.text.match(/[0-9]+/);
|
||||
if (match == null) return;
|
||||
|
||||
const num = parseInt(match[0], 10);
|
||||
|
||||
// 整数じゃない
|
||||
if (!Number.isInteger(num)) return;
|
||||
|
||||
// 範囲外
|
||||
if (num < 0 || num > 100) return;
|
||||
|
||||
this.log(`Voted ${num} by ${msg.user.id}`);
|
||||
|
||||
game.votes.push({
|
||||
user: msg.user,
|
||||
number: num
|
||||
});
|
||||
|
||||
this.games.update(game);
|
||||
}
|
||||
|
||||
/**
|
||||
* 終了すべきゲームがないかチェック
|
||||
*/
|
||||
@autobind
|
||||
private crawleGameEnd() {
|
||||
const game = this.games.findOne({
|
||||
isEnded: false
|
||||
});
|
||||
|
||||
if (game == null) return;
|
||||
|
||||
// ゲーム開始から3分以上経過していたら
|
||||
if (Date.now() - game.startedAt >= 1000 * 60 * 3) {
|
||||
this.finish(game);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ゲームを終わらせる
|
||||
*/
|
||||
@autobind
|
||||
private finish(game: Game) {
|
||||
game.isEnded = true;
|
||||
this.games.update(game);
|
||||
|
||||
// お流れ
|
||||
if (game.votes.length <= 1) {
|
||||
this.ai.post({
|
||||
text: serifs.kazutori.onagare,
|
||||
renoteId: game.postId
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
function acct(user: User): string {
|
||||
return user.host ? `@${user.username}@${user.host}` : `@${user.username}`;
|
||||
}
|
||||
|
||||
let results: string[] = [];
|
||||
|
||||
let winner: User = null;
|
||||
|
||||
for (let i = 100; i >= 0; i--) {
|
||||
const users = game.votes.filter(x => x.number == i).map(x => x.user);
|
||||
if (users.length == 1) {
|
||||
if (winner == null) {
|
||||
winner = users[0];
|
||||
results.push(`${i == 100 ? '💯' : '🎉'} ${i}: ${acct(users[0])}`);
|
||||
} else {
|
||||
results.push(`➖ ${i}: ${acct(users[0])}`);
|
||||
}
|
||||
} else if (users.length > 1) {
|
||||
results.push(`❌ ${i}: ${users.map(u => acct(u)).join(' ')}`);
|
||||
}
|
||||
}
|
||||
|
||||
const text = results.join('\n') + '\n\n' + (winner
|
||||
? serifs.kazutori.finishWithWinner(acct(winner))
|
||||
: serifs.kazutori.finishWithNoWinner);
|
||||
|
||||
this.ai.post({
|
||||
text: text,
|
||||
cw: serifs.kazutori.finish,
|
||||
renote: game.postId
|
||||
});
|
||||
|
||||
this.unsubscribeReply(null);
|
||||
}
|
||||
}
|
|
@ -235,6 +235,25 @@ export default {
|
|||
congrats: tries => `正解です🎉 (${tries}回目で当てました)`,
|
||||
},
|
||||
|
||||
/**
|
||||
* 数取りゲーム
|
||||
*/
|
||||
kazutori: {
|
||||
alreadyStarted: '今ちょうどやってますよ~',
|
||||
|
||||
matakondo: 'また今度やりましょう!',
|
||||
|
||||
intro: 'みなさん、数取りゲームしましょう!\n0~100の中で最も大きい数字を取った人が勝ちです。他の人と被ったらだめですよ~\n制限時間は3分です。数字はこの投稿にリプライで送ってくださいね!',
|
||||
|
||||
finish: 'ゲームの結果発表です!',
|
||||
|
||||
finishWithWinner: user => `今回は${user}さんの勝ちです!またやりましょう♪`,
|
||||
|
||||
finishWithNoWinner: '今回は勝者はいませんでした... またやりましょう♪',
|
||||
|
||||
onagare: '参加者が集まらなかったのでお流れになりました...'
|
||||
},
|
||||
|
||||
/**
|
||||
* 絵文字生成
|
||||
*/
|
||||
|
|
Loading…
Reference in a new issue