diff --git a/README.md b/README.md index 87dcf11..042e75e 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,8 @@ Misskey用の日本語Botです。 "chartEnabled": "チャート機能を無効化する場合は false を入れてください", "reversiEnabled": "藍とリバーシで対局できる機能を有効にする場合は true を入れる (無効にする場合は false)", "serverMonitoring": "サーバー監視の機能を有効にする場合は true を入れる (無効にする場合は false)", + "checkEmojisEnabled": "カスタム絵文字チェック機能を有効にする場合は true を入れる (無効にする場合は false)", + "checkEmojisAtOnce": "カスタム絵文字チェック機能で投稿をまとめる場合は true を入れる (まとめない場合は false)", "mecab": "MeCab のインストールパス (ソースからインストールした場合、大体は /usr/local/bin/mecab)", "mecabDic": "MeCab の辞書ファイルパス (オプション)", "memoryDir": "memory.jsonの保存先(オプション、デフォルトは'.'(レポジトリのルートです))" @@ -40,6 +42,8 @@ Misskey用の日本語Botです。 "chartEnabled": "チャート機能を無効化する場合は false を入れてください", "reversiEnabled": "藍とリバーシで対局できる機能を有効にする場合は true を入れる (無効にする場合は false)", "serverMonitoring": "サーバー監視の機能を有効にする場合は true を入れる (無効にする場合は false)", + "checkEmojisEnabled": "カスタム絵文字チェック機能を有効にする場合は true を入れる (無効にする場合は false)", + "checkEmojisAtOnce": "カスタム絵文字チェック機能で投稿をまとめる場合は true を入れる (まとめない場合は false)", "mecab": "/usr/bin/mecab", "mecabDic": "/usr/lib/x86_64-linux-gnu/mecab/dic/mecab-ipadic-neologd/", "memoryDir": "data" diff --git a/src/config.ts b/src/config.ts index 1918b4f..59154a3 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,5 +1,6 @@ type Config = { host: string; + serverName?: string; i: string; master?: string; wsUrl: string; @@ -9,6 +10,8 @@ type Config = { notingEnabled: boolean; chartEnabled: boolean; serverMonitoring: boolean; + checkEmojisEnabled?: boolean; + checkEmojisAtOnce?: boolean; mecab?: string; mecabDic?: string; memoryDir?: string; diff --git a/src/index.ts b/src/index.ts index dca5703..c6c2fa8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -34,6 +34,7 @@ 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(' /__\\ (_ _)( _ )/ __)'); @@ -88,6 +89,7 @@ promiseRetry(retry => { new NotingModule(), new PollModule(), new ReminderModule(), + new CheckCustomEmojisModule(), ]); }).catch(e => { log(chalk.red('Failed to fetch the account')); diff --git a/src/modules/check-custom-emojis/index.ts b/src/modules/check-custom-emojis/index.ts new file mode 100644 index 0000000..d164c59 --- /dev/null +++ b/src/modules/check-custom-emojis/index.ts @@ -0,0 +1,162 @@ +import autobind from 'autobind-decorator'; +import * as loki from 'lokijs'; +import Module from '@/module'; +import serifs from '@/serifs'; +import config from '@/config'; +import Message from '@/message'; + +export default class extends Module { + public readonly name = 'checkCustomEmojis'; + + private lastEmoji: loki.Collection<{ + id: string; + updatedAt: number; + }>; + + @autobind + public install() { + if (!config.checkEmojisEnabled) return {}; + this.lastEmoji = this.ai.getCollection('lastEmoji', { + indices: ['id'] + }); + + this.timeCheck(); + setInterval(this.timeCheck, 1000 * 60 * 3); + + return { + mentionHook: this.mentionHook + }; + } + + @autobind + private timeCheck() { + const now = new Date(); + if (now.getHours() !== 23) return; + const date = `${now.getFullYear()}-${now.getMonth()}-${now.getDate()}`; + const data = this.getData(); + if (data.lastPosted == date) return; + data.lastPosted = date; + this.setData(data); + + this.log('Time to Check CustomEmojis!'); + this.post(); + } + + @autobind + private async post() { + this.log('Start to Check CustomEmojis.'); + const lastEmoji = this.lastEmoji.find({}); + + const lastId = lastEmoji.length != 0 ? lastEmoji[0].id : null; + const emojisData = await this.checkCumstomEmojis(lastId); + if (emojisData.length == 0) { + this.log('No CustomEmojis Added.'); + return; + } + + // 絵文字データが取得された場合、元々のデータを削除しておく + const emojiSize = emojisData.length; + this.lastEmoji.remove(lastEmoji); + + const server_name = config.serverName ? config.serverName : 'このサーバー'; + this.log('Posting...'); + + // 一気に投稿しないver + if (!config.checkEmojisAtOnce){ + // 概要について投稿 + this.log(serifs.checkCustomEmojis.post(server_name, emojiSize)); + await this.ai.post({ + text: serifs.checkCustomEmojis.post(server_name, emojiSize) + }); + + // 各絵文字について投稿 + for (const emoji of emojisData){ + await this.ai.post({ + text: serifs.checkCustomEmojis.emojiPost(emoji.name) + }); + this.log(serifs.checkCustomEmojis.emojiPost(emoji.name)); + } + } else { + // 一気に投稿ver + let text = ''; + for (const emoji of emojisData){ + text += serifs.checkCustomEmojis.emojiOnce(emoji.name); + } + const message = serifs.checkCustomEmojis.postOnce(server_name, emojiSize, text); + this.log(message); + await this.ai.post({ + text: message + }); + } + + // データの保存 + this.log('Last CustomEmojis data saving...'); + this.log(JSON.stringify(emojisData[emojiSize-1],null,'\t')); + this.lastEmoji.insertOne({ + id: emojisData[emojiSize-1].id, + updatedAt: Date.now() + }); + this.log('Check CustomEmojis finished!'); + } + + @autobind + private async checkCumstomEmojis(lastId : any) { + this.log('CustomEmojis fetching...'); + let emojisData; + if(lastId != null){ + this.log('lastId is **not** null'); + emojisData = await this.ai.api('admin/emoji/list', { + sinceId: lastId, + limit: 30 + }); + } else { + this.log('lastId is null'); + emojisData = await this.ai.api('admin/emoji/list', { + limit: 100 + }); + + // 最後まで取得 + let beforeEmoji = null; + let afterEmoji = emojisData.length > 1 ? emojisData[0] : null; + while(emojisData.length == 100 && beforeEmoji != afterEmoji){ + const lastId = emojisData[emojisData.length-1].id; + // sinceIdを指定して再度取り直す + emojisData = await this.ai.api('admin/emoji/list', { + limit: 100, + sinceId: lastId + }); + beforeEmoji = afterEmoji; + afterEmoji = emojisData.length > 1 ? emojisData[0] : null; + await this.sleep(50); + } + + // sinceIdが未指定の場合、末尾から5件程度にしておく + let newJson: any[] = []; + for (let i = emojisData.length - 5; i < emojisData.length; i++) { + newJson.push(emojisData[i]); + } + emojisData = newJson; + } + return emojisData; + } + + @autobind + private async mentionHook(msg: Message) { + if (!msg.includes(['カスタムえもじチェック','カスタムえもじを調べて','カスタムえもじを確認'])) { + return false; + } else { + this.log('Check CustomEmojis requested'); + } + + await this.post(); + + return { + reaction: 'like' + }; + } + + @autobind + private async sleep(ms: number) { + return new Promise((res) => setTimeout(res, ms)); + } +} diff --git a/src/serifs.ts b/src/serifs.ts index 15ae16e..49fc8d4 100644 --- a/src/serifs.ts +++ b/src/serifs.ts @@ -381,6 +381,13 @@ export default { foryou: '描きました!' }, + checkCustomEmojis: { + post: (server_name, num) => `${server_name}に${num}件の絵文字が追加されました!`, + emojiPost: emoji => `:${emoji}:\n(\`${emoji}\`) #AddCustomEmojis`, + postOnce: (server_name, num, text) => `${server_name}に${num}件の絵文字が追加されました!\n${text} #AddCustomEmojis`, + emojiOnce: emoji => `:${emoji}:(\`${emoji}\`)` + }, + sleepReport: { report: hours => `んぅ、${hours}時間くらい寝ちゃってたみたいです`, reportUtatane: 'ん... うたた寝しちゃってました', diff --git a/torisetu.md b/torisetu.md index 3b1c13b..0d29985 100644 --- a/torisetu.md +++ b/torisetu.md @@ -75,6 +75,9 @@ Misskeyにアカウントを作成して初めて投稿を行うと、藍がネ ### ping PONGを返します。生存確認にどうぞ +### カスタム絵文字チェック +1日に1回、カスタム絵文字の追加を監視してくれます。「カスタムえもじチェック」または「カスタムえもじを確認して」ですぐに確認してくれます。 + ### その他反応するフレーズ (トークのみ) * かわいい * なでなで