押し寄せる「こんばんは」を迎え撃つ
この記事は TCU-CTRL場外乱闘 Advent Calendar 2017 8日目の記事です。 また、本日 12/8 は著者りぶの誕生日です。プレゼントお待ちしています。
ごあいさつ
こんばんは。挨拶は人間とコミュニケーションをとる上で重要です。 Wikipedia の「挨拶」の記事にはこう記されています。
挨拶(あいさつ、儐とも書く)は、新たに顔を合わせた際や別れ際に行われる、礼儀として行われる定型的な言葉や動作のことを指す。また、式典などで儀礼的に述べる言葉をいうこともある。また、日常生活には欠かせない人と人とが気持ちよく生活できる言葉でもある。 挨拶 - Wikipedia
しかし、 Discord のチャットで「こんばんは」とだけ発してすぐにオフラインになったり、「こんばんは」を安売りしすぎて誰も反応してくれないなどといったことが以前から問題になっていました。
そこで、サーバー内にいる bot が自動で返事をするようにすれば、「こんばんは」と言う彼の心は満たされるだろうと考えました。
bot作ろうと思ったけどめんどうになってるとこ
サーバー内にいる他のユーザーからの需要もあるので、今回は bot を作ってみました。
とりあえず作る
今回は Node.js で Discord の bot を作ります。バージョンは以下の通り。
$ node -v
v8.5.0
$ npm -v
5.5.1
それでは、プロジェクトを作って discord.js をインストールしましょう。
$ mkdir bot
$ cd bot
$ npm init -y
$ npm i discord.js
$ touch index.js
これで bot
ディレクトリ内に index.js
、 node_modules
、 package-lock.json
、 package.json
ができているはずです。エントリーポイントは index.js
なので、この中にコードを書いていきます。
'use strict';
const {Client} = require('discord.js');
// クライアントのインスタンスを作る
const client = new Client();
// メッセージが来た時に呼ばれる関数を登録
client.on('message', msg => {
// 投稿者の ID が XXXXX であり、かつ本文が正規表現パターン /^こんばんは$/ にマッチ
if (msg.author.id === 'XXXXX' && /^こんばんは$/.test(msg.content))
// 同じテキストチャンネルに「おやすみ」と送信
msg.channel.send('おやすみ');
});
// Discord にログイン
client.login('YYYYY');
XXXXX
は、 Discord のユーザー設定で開発者モードをオンにした上で、「こんばんは」をよく投稿するユーザーを右クリックして ID をコピーしたものを貼り付けます。
YYYYY
は、 Discord の My Apps で作成したアプリケーションのトークンを貼り付けます。
あとは bot をサーバーに参加させて、この bot を起動するだけです。
$ node .
これで、「こんばんは」とメッセージが来たら「おやすみ」と返信してあげる bot が完成しました。非常に簡単にできました。
この bot の傾向と対策
文字列が「こんばんは」に完全一致していないといけないので、一文字でも変えられてしまうと反応できなくなってしまいます。
何に勝ったのかはよく分かりませんが、このようなパターンにも対応する必要がありそうです。
反応できるパターンを増やす
判定に正規表現を用いているため、「こんばんは」が「こんばんわ」に変わった程度のものならパターンを変更することですぐに対応できます。更に、今後複雑化が予想される「こんばんは」の変化形に対応するため、「こんばんは」かどうか判定する部分を関数化しておくのが良いでしょう。
const f = msg => {
// 投稿者の ID が XXXXX でないなら false を返す
if (msg.author.id !== 'XXXXX')
return false;
// 「こんばんは」っぽい文字列なら true を返す
if (/^こんばん[はわ]$/.test(msg.content))
return true;
// 「こんばんは」を意味するメッセージではないので false を返す
return false;
};
// メッセージが来た時に呼ばれる関数を登録
client.on('message', msg => {
// 「こんばんは」判定
if (f(msg))
// 同じテキストチャンネルに「おやすみ」と送信
msg.channel.send('おやすみ');
});
これで、「こんばんわ」にも対応することができます。実際には、もっと複雑に変更してくると考えられるので、このくらいやっておくのが良いでしょう。
if (/^(?:こん|今)(?:(?:ば|は゛)ん|晩)[はわ]$/.test(msg.content))
return true;
空白文字などを削除しておく
「こ ん ば ん は」などと打たれると対応できません。正規表現パターンの間に空白文字をマッチさせても良いのですが、文字の間と前後すべてに空白文字パターンを書かなければならず、かなり冗長になってしまうので、判定の前処理として空白文字を削除しておく方法をとります。
const f = msg => {
// (略)
// 判定時邪魔になりそうな文字をすべて削除
const str = msg.content.replace(/[-ー!?!?.,\s]/g, '');
// 「こんばんは」っぽい文字列なら true を返す
if (/^(?:こん|今)(?:(?:ば|は゛)ん|晩)[はわ]$/.test(str))
return true;
// (略)
};
非常に満足している様子が確認できました。
ギャル文字を翻訳しておく
「こんばんは」の多様化は止まりません。以下のような施策を打ち出してきました。
手動での「おやすみ」が発生してしまっています。これは一大事です。早急に対策しましょう。
落ち着いて文字列を置換すれば、あっという間に実装完了です。
const f = msg => {
// (略)
// 判定時邪魔になりそうな文字をすべて削除
let str = msg.content.replace(/[-ー!?!?.,\s]/g, '');
// ギャル文字を翻訳
str = str.replace(/⊇/g, 'こ');
str = str.replace(/ω/g, 'ん');
str = str.replace(/レよ/g, 'は');
str = str.replace(/″/g, '゛');
str = str.replace(/ゎ/g, 'わ');
// 「こんばんは」っぽい文字列なら true を返す
if (/^(?:こん|今)(?:(?:ば|は゛)ん|晩)[はわ]$/.test(str))
return true;
// (略)
};
外国語などに対応する
「こんばんは」をよく投稿するユーザーが最近、外国語などで「こんばんは」を意味する投稿をするようになりました。
上から、
- アラビア語
- ロシア語
- 英語
- BOM なし UTF-8 文字列「こんばんは」の MD5 ハッシュ値
- 日本語での謎の勝利宣言
となっています。 4 番目に関しては、以下のコマンドで確認することができます。
$ echo $LANG
ja_JP.UTF-8
$ echo -n こんばんは | md5sum
73c9cdc2c87a450424374452e0a28f2b -
正直、これらに関しては上述「ギャル文字を翻訳しておく」セクションの要領で置換するしかありません。
自然言語である外国語に関しては、 Google 翻訳 API 等を使用するなどといった解決策がありますが、処理に時間が掛かる・ API アクセスが有料といったデメリットがあるので、運用するには少し厳しいように思います。
幸い、パターンは「こんばんは」しかないので、手動で外国語→日本語変換結果のデータベースを作って持っておくのが最適解っぽいです。今度作っておきます。
〆
たかが自動応答する bot とは言え、追求すればするほど楽しいものになっていきます。
世の中には、以下のように進化しようとする気が全く無い、柔軟性の欠片も無いような bot も横行しています(最近は動いていないようですが) 1 。
[訂正]
名誉毀損を訴える条件
・リプでないツイート
・日本語
・署名入り
・5,7,5←重要
です— DefamationBOT (@defamationPa) October 4, 2017
人間と bot の間にインタラクションが発生する以上、進化をし続けることは重要であると考えています。
今後も「こんばんは」に反応する bot の進化にご期待ください。
次回 9 日目の記事は リトマス (@rito193) 君です。よろしくお願いします。
真理
Footnotes
-
bot の仕様が書かれたツイートが本記事に埋め込まれることを嫌って、元のツイートを削除しているようです。非常に滑稽ですね。 Twitter の埋め込みツイートの仕様上、
<blockquote>
タグに元のツイートの内容が記載されているため、削除されたツイートは本記事で引用文として表示されたままになっています。 ↩