diff --git a/src/modules/core/index.ts b/src/modules/core/index.ts index 4e3c823..d5f9d2a 100644 --- a/src/modules/core/index.ts +++ b/src/modules/core/index.ts @@ -4,6 +4,7 @@ import MessageLike from '../../message-like'; import serifs from '../../serifs'; import Friend from '../../friend'; import getDate from '../../utils/get-date'; +import includes from '../../utils/includes'; function zeroPadding(num: number, length: number): string { return ('0000000000' + num).slice(-length); @@ -107,8 +108,6 @@ export default class CoreModule implements IModule { } private greet = (msg: MessageLike): boolean => { - if (!msg.text) return false; - const incLove = () => { //#region 1日に1回だけ親愛度を上げる const today = getDate(); @@ -124,19 +123,25 @@ export default class CoreModule implements IModule { //#endregion }; - if (msg.text.includes('こんにちは')) { + if (includes(msg.text, ['こんにちは'])) { msg.reply(serifs.core.hello(msg.friend.name)); incLove(); return true; } - if (msg.text.includes('おはよ')) { + if (includes(msg.text, ['こんばんは'])) { + msg.reply(serifs.core.helloNight(msg.friend.name)); + incLove(); + return true; + } + + if (includes(msg.text, ['おはよ', 'お早う'])) { msg.reply(serifs.core.goodMorning(msg.friend.name)); incLove(); return true; } - if (msg.text.includes('おやすみ')) { + if (includes(msg.text, ['おやすみ', 'お休み'])) { msg.reply(serifs.core.goodNight(msg.friend.name)); incLove(); return true; @@ -146,8 +151,7 @@ export default class CoreModule implements IModule { } private nadenade = (msg: MessageLike): boolean => { - if (!msg.text) return false; - if (!msg.text.includes('なでなで')) return false; + if (!includes(msg.text, ['なでなで'])) return false; // メッセージのみ if (!msg.isMessage) return true; @@ -175,8 +179,7 @@ export default class CoreModule implements IModule { } private kawaii = (msg: MessageLike): boolean => { - if (!msg.text) return false; - if (!msg.text.includes('かわいい') && !msg.text.includes('可愛い')) return false; + if (!includes(msg.text, ['かわいい', '可愛い'])) return false; msg.reply(msg.friend.love >= 5 ? serifs.core.kawaii2 : serifs.core.kawaii1); diff --git a/src/modules/emoji/index.ts b/src/modules/emoji/index.ts index fca4f5e..ffb207c 100644 --- a/src/modules/emoji/index.ts +++ b/src/modules/emoji/index.ts @@ -2,6 +2,7 @@ import 藍 from '../../ai'; import IModule from '../../module'; import MessageLike from '../../message-like'; import serifs from '../../serifs'; +import includes from '../../utils/includes'; const hands = [ '👏', @@ -124,7 +125,7 @@ export default class EmojiModule implements IModule { public install = (ai: 藍) => { } public onMention = (msg: MessageLike) => { - if (msg.text && (msg.text.includes('絵文字') || msg.text.includes('emoji'))) { + if (includes(msg.text, ['絵文字', '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; diff --git a/src/modules/fortune/index.ts b/src/modules/fortune/index.ts index 4db3a23..b455f75 100644 --- a/src/modules/fortune/index.ts +++ b/src/modules/fortune/index.ts @@ -3,6 +3,7 @@ import IModule from '../../module'; import MessageLike from '../../message-like'; import serifs from '../../serifs'; import * as seedrandom from 'seedrandom'; +import includes from '../../utils/includes'; const omikujis = [ '大大吉', @@ -30,9 +31,7 @@ export default class FortuneModule implements IModule { public install = (ai: 藍) => { } public onMention = (msg: MessageLike) => { - if (msg.text == null) return false; - - if (msg.text.includes('占') || msg.text.includes('うらな') || msg.text.includes('運勢') || msg.text.includes('おみくじ')) { + if (includes(msg.text, ['占', 'うらな', '運勢', 'おみくじ'])) { const date = new Date(); const seed = `${date.getFullYear()}/${date.getMonth()}/${date.getDay()}@${msg.userId}`; const rng = seedrandom(seed); diff --git a/src/modules/guessing-game/index.ts b/src/modules/guessing-game/index.ts index cc429ff..62d8ce1 100644 --- a/src/modules/guessing-game/index.ts +++ b/src/modules/guessing-game/index.ts @@ -4,6 +4,7 @@ import IModule from '../../module'; import MessageLike from '../../message-like'; import serifs from '../../serifs'; import getCollection from '../../utils/get-collection'; +import includes from '../../utils/includes'; export default class GuessingGameModule implements IModule { public readonly name = 'guessingGame'; @@ -28,7 +29,7 @@ export default class GuessingGameModule implements IModule { } public onMention = (msg: MessageLike) => { - if (msg.text && (msg.text.includes('数当て') || msg.text.includes('数あて'))) { + if (includes(msg.text, ['数当て', '数あて'])) { const exist = this.guesses.findOne({ userId: msg.userId, isEnded: false diff --git a/src/modules/reversi/index.ts b/src/modules/reversi/index.ts index 2a40a17..f96511a 100644 --- a/src/modules/reversi/index.ts +++ b/src/modules/reversi/index.ts @@ -9,6 +9,7 @@ import * as WebSocket from 'ws'; import Friend from '../../friend'; import getDate from '../../utils/get-date'; import { User } from '../../misskey/user'; +import includes from '../../utils/includes'; export default class ReversiModule implements IModule { public readonly name = 'reversi'; @@ -45,7 +46,7 @@ export default class ReversiModule implements IModule { } public onMention = (msg: MessageLike) => { - if (msg.text && (msg.text.includes('リバーシ') || msg.text.includes('りばーし') || msg.text.includes('オセロ') || msg.text.includes('おせろ') || msg.text.toLowerCase().includes('reversi'))) { + if (includes(msg.text, ['リバーシ', 'オセロ', 'reversi', 'othello'])) { if (config.reversiEnabled) { msg.reply(serifs.reversi.ok); diff --git a/src/serifs.ts b/src/serifs.ts index 36eaf4b..8d43412 100644 --- a/src/serifs.ts +++ b/src/serifs.ts @@ -8,6 +8,8 @@ export default { hello: name => name ? `こんにちは、${name}♪` : `こんにちは♪`, + helloNight: name => name ? `こんばんは、${name}♪` : `こんばんは♪`, + goodMorning: name => name ? `おはようございます、${name}!` : 'おはようございます!', goodNight: name => name ? `おやすみなさい、${name}!` : 'おやすみなさい!', diff --git a/src/utils/includes.ts b/src/utils/includes.ts new file mode 100644 index 0000000..e92d81f --- /dev/null +++ b/src/utils/includes.ts @@ -0,0 +1,10 @@ +import { hiraganaToKatagana, zenkakuToHankaku, hankakuToZenkaku } from './japanese'; + +export default function(text: string, words: string[]): boolean { + if (text == null) return false; + + text = hankakuToZenkaku(hiraganaToKatagana(text)); + words = words.map(word => hiraganaToKatagana(word)); + + return words.some(word => text.includes(word)); +} diff --git a/src/utils/japanese.ts b/src/utils/japanese.ts new file mode 100644 index 0000000..43ab538 --- /dev/null +++ b/src/utils/japanese.ts @@ -0,0 +1,79 @@ +// Utilities for Japanese + +const kanaMap: string[][] = [ + ['ア', 'ア'], ['イ', 'イ'], ['ウ', 'ウ'], ['エ', 'エ'], ['オ', 'オ'], + ['カ', 'カ'], ['キ', 'キ'], ['ク', 'ク'], ['ケ', 'ケ'], ['コ', 'コ'], + ['サ', 'サ'], ['シ', 'シ'], ['ス', 'ス'], ['セ', 'セ'], ['ソ', 'ソ'], + ['タ', 'タ'], ['チ', 'チ'], ['ツ', 'ツ'], ['テ', 'テ'], ['ト', 'ト'], + ['ナ', 'ナ'], ['ニ', 'ニ'], ['ヌ', 'ヌ'], ['ネ', 'ネ'], ['ノ', 'ノ'], + ['ハ', 'ハ'], ['ヒ', 'ヒ'], ['フ', 'フ'], ['ヘ', 'ヘ'], ['ホ', 'ホ'], + ['マ', 'マ'], ['ミ', 'ミ'], ['ム', 'ム'], ['メ', 'メ'], ['モ', 'モ'], + ['ヤ', 'ヤ'], ['ユ', 'ユ'], ['ヨ', 'ヨ'], + ['ラ', 'ラ'], ['リ', 'リ'], ['ル', 'ル'], ['レ', 'レ'], ['ロ', 'ロ'], + ['ワ', 'ワ'], ['ヲ', 'ヲ'], ['ン', 'ン'], + ['ガ', 'ガ'], ['ギ', 'ギ'], ['グ', 'グ'], ['ゲ', 'ゲ'], ['ゴ', 'ゴ'], + ['ザ', 'ザ'], ['ジ', 'ジ'], ['ズ', 'ズ'], ['ゼ', 'ゼ'], ['ゾ', 'ゾ'], + ['ダ', 'ダ'], ['ヂ', 'ヂ'], ['ヅ', 'ヅ'], ['デ', 'デ'], ['ド', 'ド'], + ['バ', 'バ'], ['ビ', 'ビ'], ['ブ', 'ブ'], ['ベ', 'ベ'], ['ボ', 'ボ'], + ['パ', 'パ'], ['ピ', 'ピ'], ['プ', 'プ'], ['ペ', 'ペ'], ['ポ', 'ポ'], + ['ヴ', 'ヴ'], ['ヷ', 'ヷ'], ['ヺ', 'ヺ'], + ['ァ', 'ァ'], ['ィ', 'ィ'], ['ゥ', 'ゥ'], ['ェ', 'ェ'], ['ォ', 'ォ'], + ['ッ', 'ッ'], ['ャ', 'ャ'], ['ュ', 'ュ'], ['ョ', 'ョ'], + ['ー', 'ー'] +]; + +/** + * カタカナをひらがなに変換します + * @param str カタカナ + * @returns ひらがな + */ +export function katakanaToHiragana(str: string): string { + return str.replace(/[\u30a1-\u30f6]/g, match => { + const char = match.charCodeAt(0) - 0x60; + return String.fromCharCode(char); + }); +} + +/** + * ひらがなをカタカナに変換します + * @param str ひらがな + * @returns カタカナ + */ +export function hiraganaToKatagana(str: string): string { + return str.replace(/[\u3041-\u3096]/g, match => { + const char = match.charCodeAt(0) + 0x60; + return String.fromCharCode(char); + }); +} + +/** + * 全角カタカナを半角カタカナに変換します + * @param str 全角カタカナ + * @returns 半角カタカナ + */ +export function zenkakuToHankaku(str: string): string { + const reg = new RegExp('(' + kanaMap.map(x => x[0]).join('|') + ')', 'g'); + + return str + .replace(reg, match => + kanaMap.find(x => x[0] == match)[1] + ) + .replace(/゛/g, '゙') + .replace(/゜/g, '゚'); +}; + +/** + * 半角カタカナを全角カタカナに変換します + * @param str 半角カタカナ + * @returns 全角カタカナ + */ +export function hankakuToZenkaku(str: string): string { + const reg = new RegExp('(' + kanaMap.map(x => x[1]).join('|') + ')', 'g'); + + return str + .replace(reg, match => + kanaMap.find(x => x[1] == match)[0] + ) + .replace(/゙/g, '゛') + .replace(/゚/g, '゜'); +};