PR(記事内にアフィリエイト広告が含まれています)
スポンサーリンク
ツール

Discordお題箱システム

ツール

概要

匿名でメッセージを受け取る「お題箱」ですが、誰が投稿してきたのか分からないので、一人の人が連投してるのかが判別できなくて困りました。

既存のサービスをいくつか調べてみましたが、お題箱・マシュマロ・mond・Peing のいずれも、投稿者から見ても運営者から見ても匿名という設計で、運営者だけが同一性を判別できるという仕組みを持つものは見当たりません。

そこで、対外的には匿名のまま、運営者だけが投稿者の同一性を判別できるメッセージボックス「Banana Request」を作りました。

投稿するには Discord アカウントでのログインが必要です。ただしログインした情報が公開されることはありません。投稿内容だけが受信箱に届き、誰が送ったかは第三者には一切分かりません。運営者の側だけが、その投稿が Discord のどのアカウントから来たものかを把握できます。この非対称性が、既存のサービスとの本質的な違いです。

PHP と MySQL が使えるレンタルサーバーに設置して使います。常駐プロセスも Cron も使わないので、定期実行の仕組みが用意できないサーバーでも動きます。

主な機能

・Discord アカウントによるログイン(OAuth2)を必須化
・指定した Discord サーバーの参加者だけに投稿を限定(オン/オフ切替可)
・投稿フォーム(文字数上限は設定可能。初期値は 400 文字)
・新着投稿の運営者へのメール通知(その投稿者の通算お題数つき)
・投稿の制限:未対応のお題が残っているあいだは新しいお題を受け付けない
・運営者が個別に与える「追加投稿の許可」(特定のお題への追記を 1 回だけ許可)
・NG ワードフィルタ(該当する投稿を自動で非表示にする)
・手動 BAN リスト(投稿者ごと、または Discord ユーザー ID を直接指定)
・ルールへの同意(同意しないと投稿フォームを開けない。本文は管理画面で編集)
・受信箱での投稿一覧・対応済み管理・非表示と復元・投稿者単位の絞り込み
・操作ログ・ログイン試行ログ

動作環境

・PHP 8.3(8.0 以上で動作する想定ですが、8.3以外は未検証)
・PHP 拡張:curl / mbstring / PDO_MySQL
・mail() 関数が使えること
・MySQL 5.7 以上(utf8mb4 対応)
・HTTPS で運用できること

※Cronは不要
※エックスサーバー スタンダードプランでの動作確認済み

同梱の手順書はエックスサーバーを前提とした記載です。他のレンタルサーバーをご利用の場合は、フォルダのパスやパーミッションの設定などを、ご利用環境に合わせて読み替えてください。

配布について

ソース一式を zip で配布します。サーバーへのアップロード、データベースの作成、設定ファイルの編集が必要になるため、設置にはある程度の技術的知識が必要です。所要時間はおおむね 1 時間程度を見込んでください。

同梱の README.md に、Discord 側の準備からデータベースの作成、パーミッションの設定、動作確認、トラブルシューティングまでの手順を書いてます。

BananaRequest1.0.zip

システムの仕様

誰に何が見えるのか

投稿者から見ると、送った内容が自分のアカウント名と一緒に公開されることはありません。第三者から見ても、誰が何を送ったかは分かりません。表に出るのは、運営者がその内容を使って何かをしたときの、その成果物だけです。

運営者から見ると、受信箱には投稿本文に加えて、投稿者の Discord ユーザー名・表示名・ユーザー ID、投稿日時、IP アドレスが並びます。そして「この人からの通算のお題数」が各投稿に表示されます。初めての投稿であれば「初めての投稿」と出ます。

同一人物の判定に使っているのは Discord のユーザー ID です。これはアカウントに一意で、名前を変更しても変わりません。ユーザー名や表示名のほうは投稿時点のものをそのまま保存しているので、あとから改名されても過去の投稿は当時の名前のまま残ります。

Discord から取得する情報

OAuth2 で要求する権限は「identify」と「guilds.members.read」の二つだけで、どちらも読み取り専用です。前者でログインした本人のユーザー ID と名前を、後者でその人が対象サーバーに参加しているかどうかだけを確認します。メールアドレスは取得していません。

なお、Bot の作成は必要ありません。このシステムが Discord に対して行うのは質問だけで、サーバー内で何かをするわけではないからです。Developer Portal で用意するのは、クライアント ID・クライアントシークレット・リダイレクト URI の三つだけです。

サーバーメンバー限定

設定で有効にすると、指定した Discord サーバーに参加している人だけが投稿できるようになります。無効にすれば、Discord アカウントを持っている人なら誰でも投稿できます。

有効にしている場合、参加していない人がログインしても投稿フォームは表示されず、その旨のメッセージだけが出ます。

投稿の制限

荒らしが連続投稿してくるのを防ぐために、設定ファイルで分数で制限をかけてます。

「対応が終わっていないのに次々と積み上がる」状況を防ぐため、未対応のお題が一定数ある人は新しいお題を送れない、という形にしてます。件数は設定で変更できます(初期値は1件)。

運営者が受信箱で「対応済みにする」を押せば、その人はすぐにまた送れるようになります。

追記の許可

一度届いたお題について、こちらから確認したいことが出てくる場合があります。ただ、このシステムは一方通行で、フォームから返信する機能はありません。

そこで、運営者が特定のお題に対して「追記を一回だけ許可する」という操作をできるようにしました。許可を出すと、その人が次にフォームを開いたときに、元のお題が引用表示された状態で入力欄が現れます。許可を出すときに一言を添えることもでき、それが相手の画面に表示されます。

届いた追記は、受信箱で元のお題の下にぶら下がって表示されます。やり取りの経緯が一か所にまとまるので、あとから見返すときにも追いやすくなっています。

追記は未対応の件数には数えません。許可は一回使うと無効になり、必要であればまた出すことができます。

NG ワードフィルタ

管理画面で語を登録しておくと、その語を含む投稿は受け付けたうえで自動的に「非表示」として保存されます。投稿者の画面には通常どおり送信完了と表示され、どの語で引っかかったのかは伝わりません。伝えてしまうと、単語を変えて送り直されるだけだからです。新着の通知メールも送りません。

該当した投稿は受信箱の「非表示」から確認でき、誤検知であれば元に戻せます。

判定は部分一致で、英字の大文字と小文字は区別しません。ただし全角と半角、ひらがなとカタカナは別の文字として扱います。表記ゆれをまとめて弾きたい場合は、それぞれ登録してください。機械的に正規化することもできますが、無関係な投稿まで巻き添えにしやすいので、あえてそうしていません。

なお、一文字だけの登録はできないようにしてあります。普通の投稿まで大量に非表示にしてしまうためです。

「サスペンダー」とか登録するのに使えます。

非表示と対応済み

受信箱の各投稿には二つのステータスがあります。

ひとつは「未対応」と「対応済み」です。

もうひとつは「非表示」です。受信箱の一覧から外した状態ですが、削除ではないので元に戻せます。データベースからも消えません。

荒らしへの対応で消してしまうと、あとで同じ相手かどうかを確かめる手立てがなくなるので、残す作りにしています。非表示にした投稿も「この人から何件」の集計には含まれます。

BAN

特定の Discord アカウントからの投稿を拒否できます。

判定はログイン時と投稿時の両方で行うので、ログイン中に BAN された相手もその場で弾かれます。

ルールと同意

投稿にあたってのルールを管理画面で書けます。同意は一人一回だけ記録され、二回目以降は同意画面が出ません。

同意済みの人も、フォームの送信ボタンのすぐ上にある「ルールを見る」からいつでも読み返せます。

ルールを書き換えたとき、全員にもう一度同意を求めるかどうかは選べます。保存ボタンが二つあり、誤字を直しただけのときは再同意を求めずに保存できます。中身を変えたときだけ版を上げて、全員に読み直してもらう、という使い分けです。

通知メール

投稿があるたびに、設定したアドレスへ通知が届きます。本文には投稿内容に加えて、投稿者の名前と Discord ユーザー ID、そして「この人の通算お題数」が入ります。メールを見た時点で、初めての人なのか常連なのか、あるいは連投なのかが分かるようにしてあります。

追記が届いた場合は件名が変わり、本文に元のお題が添えられます。

通知に投稿内容を載せたくない場合は、設定で本文を省くこともできます。その場合は新着があったことだけが通知されます。

ログ

Discord との通信エラー、所属判定の結果、データベース接続の失敗、通知メールの送信失敗などが、システム内のログファイルに記録されます。

PHP 標準のエラー出力はサーバーによってどこに出るのか分かりにくいので、出力先を固定してあります。うまく動かないときは、まずここを見れば原因の見当がつきます。このほか、管理者の操作と、投稿が拒否されたときの記録(BAN・投稿制限・サーバー未参加)がデータベースに残ります。

他環境での使用

PHP と MySQL が使えるレンタルサーバーであれば、エックスサーバー以外でも動作すると思います。

さくらのレンタルサーバ、ConoHa WING、ロリポップ、カラフルボックスあたりは、いずれも PHP と MySQL が使えるので候補になるはずです。ただしフォルダのパスやパーミッションの考え方はサービスごとに違うので、その部分は読み替えが必要です。

前作の予約投稿システムと違って、こちらは Cron を使いません。通知メールは投稿があったその場で送信しているためです。定期実行の仕組みが用意できないサーバーや、実行間隔が粗いサーバーでも問題なく動くので、その分だけ選択肢は広いはずです。

一方で、外せない前提が二つあります。

ひとつは HTTPS で運用することです。Discord の認証から戻ってくるコールバック URL を Developer Portal に登録する必要があり、そこに登録した文字列と設定ファイルに書く文字列が一字一句一致していないと認証が通りません。設置で最もつまずきやすいのがこの部分です。

もうひとつは mail() 関数が使えることです。新着の通知に使っています。メールが送れない環境でも投稿の受け付け自体は動きますが、管理画面を自分で見に行かないと新着に気づけなくなります。

AWS で動かす場合

レンタルサーバーとは構成の考え方が変わります。PHP の実行環境として Lightsail や EC2 を使い、データベースは RDS に置く形が一般的です。このシステムはファイルのアップロードを扱わないので、S3 との連携を考える必要はありません。Cron も使わないため、EventBridge や Lambda を組み合わせる必要もありません。前作に比べると構成としては単純になります。

ただしメール送信については検討が必要です。EC2 などから直接メールを送ると届きにくいことがあるため、SES のようなサービスを経由する形になります。その場合は mail() を使っている部分を書き換えることになります。

Railway などの PaaS で動かす場合

PHP を動かすコンテナと MySQL のデータベースサービスを用意し、両者を接続する形が基本になります。このシステムはファイルをディスクに保存しないので、コンテナの再デプロイでデータが消える心配は、ログファイル以外にはありません。ログが消えても運用に支障はないので、外部ストレージと組み合わせる必要も特にないはずです。

こちらもメール送信は別途考える必要があります。PaaS 系のサービスは環境構築の手間が少ない一方、無料枠の範囲や制限がサービスごとに異なるので、事前に利用規約や料金体系をご確認ください。

なお、AWS と PaaS のいずれについても、私自身は今回検証していません。構成の考え方として書いているだけですので、その点はご承知おきください。

おわりに

このシステムは、投稿者の Discord ユーザー ID・ユーザー名・表示名と、投稿時の IP アドレス・User-Agent を保存します。運営者が同一人物からの連投を判別できるようにするための設計です。メールアドレスは取得していません。第三者に公開して使う場合は、こうした情報を保存していることをルールに記載しておくことをおすすめします。ルールの本文は管理画面から編集できます。

ひとつ、検証しきれていない部分があります。対象サーバーに参加していない人がログインしようとしたときの挙動です。参加している場合に正しく通ることは確認済みで、参加していない場合は拒否される想定で実装していますが、検証用のアカウントを用意できなかったため、その部分だけは実際に試せていません。

このシステムは個人で使う分には十分な作りにしていますが、無料・無保証での公開となります。導入や利用によって生じたトラブル・データの損失・その他不利益について、責任は負いかねますので、あらかじめご了承ください。動作環境や仕様は今後変更される可能性もあります。

不具合の報告や改善のご提案などがあれば、コメント等でお知らせいただけると嬉しいです。

コメント

タイトルとURLをコピーしました