hamacoの日記

どうでもいい日常をたれながす日記だと思う

DiscordPHP を使って Discord のメッセージを取得した #phperkaigi

はじめに

先日開催した PHPerKaigi 2021 で、Ask the Speaker など参加者同士のコミュニケーションのために Discord を利用した。
以前開催した iOSDC Japan 2020 ではボイスチャットが結構使われていたので、PHPerKaigi でも当初の想定ではボイスチャットがメインでテキストチャットはあまり使われない想定だった。
しかし PHPer にはテキストチャットの方が需要が高いみたいで結構テキストでのやり取りがメインになっていたので、最終日の表彰の時とかにそのデータを使わないのも勿体ないかなと思い Discord のメッセージを取得する Bot を書いた。

Discord の Bot を作るのは結構簡単だったんだけど、会期中にサクッと作るにはちょっとめんどくさいことも多かったので、次回以降のスタッフ業の時の為にどうやったかをメモとして残しておく。

ちなみに参考にしたサイトは下記。

Bot を Discord のサーバーに追加

まずは Discord で対象のサーバーに Bot を追加する必要があるので Bot を追加する。

事前準備

Discord の API を叩く時にサーバーID とかチャンネルID とかを取得する必要があるので、事前に開発者モードを有効にしておくと簡単にその辺りが取得できて便利。
開発者モードを有効にするには、ユーザー設定にある「テーマ」から「開発者モード」を有効にするだけ。(なぜテーマにあるのかは謎)

f:id:hamaco:20210330181816p:plain

開発者モードを有効にすると、サーバーアイコンやサーバー名、チャンネルやメッセージなどを右クリックした時に「IDをコピー」というのが増えるので、簡単にそれぞれの ID を取ることができて Bot を作るのが捗る(というか他の方法を知らない)。

Bot の作成

Discord の Bot 自体を作るのはめちゃくちゃ簡単で、Discord Developer Portal から New Application をクリックして名前を入力して、Settings > Bot から Add bot を押すだけで OK。
ちなみにこの時 test みたいにめちゃくちゃよく使われるような名前にすると、アプリケーションは作れるけど Bot を作ろうとすると怒られるので、ある程度ちゃんとした名前で作る必要あり。

Bot をサーバーに追加する

Bot を作ったら、Settings > General Information に行き、CLIENT ID をコピーして、下記 URL の {CLIENT_ID} 部分を置き換えてブラウザでアクセス。

https://discordapp.com/oauth2/authorize?&client_id={CLIENT_ID}&scope=bot&permissions=0

アクセスすると Bot をサーバーに追加する画面が表示されるので、対象のサーバーを選択して「認証」をクリック。
サーバー管理者じゃなかったりしてプルダウンに対象のサーバーがでてこない時は、サーバー管理者に確認して Bot を追加する権限を付与してもらう必要があります。

f:id:hamaco:20210330181649p:plain

サーバーに Bot を追加できるかどうかは、サーバー設定のロールから自分のロールで「サーバー管理」の権限があるかどうかを確認して有効になってれば OK。

f:id:hamaco:20210330181711p:plain

メッセージを取得するコードを書く

Bot の追加ができたら、あとはもうコードを書いてメッセージを取得するだけ。

DiscordPHP のインストール

今回は DiscordPHP を使うので、まずは DiscordPHP を Composer でインストールした。

github.com

$ composer require team-reflex/discord-php

新規メッセージを取得するコードを書く

まずは誰かが新規でメッセージを投稿したら、それを取得して保存する処理を書く。 といってもめちゃくちゃ簡単でこのくらいのコードを書くだけで動きっぱなしになり、新規投稿があると標準出力に投稿者名と内容が流れてくるようになった。

<?php

use Discord\Discord;
use Discord\Parts\Channel\Message;
use Discord\WebSockets\Event;

require_once __DIR__ . '/vendor/autoload.php';

$token = '{TOKEN}'; // Developer Portal から取得した Bot のトークン

$discord = new Discord(['token' => $token]);
$discord->on('ready', function (Discord $discord) {
    $discord->on(Event::MESSAGE_CREATE, function (Message $message, Discord $discord) {
        echo "{$message->author->username}: {$message->content}" . PHP_EOL;
        // 実際はここでメッセージを DB とかなにかに保存する
    });
});

$discord->run();

最初、最後の $discord->run(); を書いていなくてなんで動かないか謎だったけど、Qiita をよく見たら書いてあった。

過去のメッセージを取得するコードを書く

PHPerKaigi 2021 の Day1 の途中から取得を始めたため、過去の投稿を取得する必要があったので、Discord のチャンネル毎に過去の投稿を全部取得するコードも書いた。
実際はチャンネルをループで回して全部取得したけど、サンプルは1つのチャンネルの全投稿を取得しています。

<?php

use Discord\Discord;
use Discord\Helpers\Collection;
use Discord\Parts\Channel\Channel;
use Discord\WebSockets\Event;

require_once __DIR__ . '/vendor/autoload.php';

$token = '{TOKEN}'; // Developer Portal から取得した Bot のトークン

$channel_id = '{CHANNEL_ID}'; // チャンネル名を右クリックから「IDをコピー」で取得したチャンネルID

function getMessageHistories(Channel $channel, array $option = [])
{
    $channel->getMessageHistory($option)->done(function (Collection $messages) use ($channel) {
        foreach ($messages as $message) {
            echo "{$message->author->username}: {$message->content}" . PHP_EOL;
            // 実際はここでメッセージを DB とかなにかに保存する
        }

        if (count($messages) === 100) {
            getMessageHistories($channel, ['before' => $message]);
        }
    });
}

$discord = new Discord(['token' => $token]);
$discord->on('ready', function (Discord $discord) use ($channel_id) {
    $channel = $discord->getChannel($channel_id);
    getMessageHistories($channel);
});

$discord->run();

おわりに

前回の iOSDC Japan 2020 から引き継いだコードもあるが、今回「ニコニコ生放送」「Twitter」「Discord」と投稿内容を取得したなかでは Discord が圧倒的に楽だった。
使ったライブラリのおかげもあると思うけど、殆ど自分でコードを書かなくてもリアルタイムに新規投稿を取得でき、めんどくさいことを考えなくて済んだのはよい。
まあ Twitter もちゃんと Streaming API を使えば楽だったんだろうけど、結構昔に書いたコードの焼き直しで済ませてしまったので…。