先日Straightanswer.orgのログにエラーの発生が記録されていました。今回はたまたまログをチェックしていて気がつくことができましたが、想定外のエラーの発生は受動的に知る仕組みになっているべきだと思います。

そこで、自分しかいないDiscordのサーバを作り、そこにエラー発生時にその旨を投稿する処理をStraightanswer.orgに加えることにしました。

通知を受け取る用のDiscordのサーバを作る

Discordのアカウントを持っていれば、サーバを作るのはすぐにできます。左端のサイドバーから「サーバーを追加」をクリックすると、サーバを作成できます。

WebhookのURLを手に入れる

まずは先程用意したサーバに通知を受け取る用のチャンネルを作ります。

チャンネルを作ったら「チャンネルの編集」の「連携サービス」から「ウェブフック」に進み、WebhookのURLを入手します。この操作はスマートフォンのアプリではできないようです。私はパソコンのブラウザから操作しました。

Discordの自分のユーザIDを手に入れる

Webhook経由の投稿で誰かにメンションするにはユーザIDを指定しなくてはなりません。少しややこしいのですが、このユーザIDはDiscordのアカウントを作成したときに自分で決めた名前のことではありません。

チャンネルの参加者一覧に表示されている自分をクリックし、ペンのアイコンの「プロフィール編集」をクリックし「ユーザープロフィールを編集」に進みます。

Discordのユーザプロフィールへの進み方

ここから「詳細設定」の中にある「開発者モード」を有効にします。

開発者モードを有効にしていると、チャンネル参加者一覧の自分を右クリックしたときに「IDをコピー」が選択できるようになっています。これで自分のユーザIDを入手できます。ユーザIDは長い数値です。

PythonからWebhook経由で投稿する

このようなクラスを作りました。

import json
from typing import Final
import urllib.request


class Discord:
    def __init__(self, webhook_url: str, mention_to_id: int) -> None:
        self._webhhok_url: Final = webhook_url
        self._mention: Final = f"<@{mention_to_id}>"
        self._headers: Final = {
            "Content-Type": "application/json",
            # User-Agent必須です。ないと403 ForbiddenがDiscordから返されます。
            "User-Agent": "{自分で決めたUser-Agentを入れてください}",
        }

    def _make_data(self, text: str) -> bytes:
        data: Final = {
            "username": "{Webhook経由での発言した人の名前として表示されます}",
            "content": f"{self._mention} {text}"
        }
        return json.dumps(data).encode()

    def post(self, text: str) -> None:
        request: Final = urllib.request.Request(
            self._webhhok_url,
            headers=self._headers,
            data=self._make_data(text),
            method="POST"
        )
        urllib.request.urlopen(request, timeout=1)

webhook_urlとmention_to_idには先程手に入れた値を与えます。

ソースコード中にも書きましたが、User-Agentの指定が必要です。urllib.request.urlopenの場合、User-Agentヘッダーの指定がないと、自動的にPython-urllib/3.10のような値になります。この値のままだと、Discordが403 Forbiddenを返し、投稿できません。

エラーが発生していたStraightanswer.orgの操作

エラーの発生は私の見通しの甘さが原因でした。当該操作をしてくれていた人には悪いことをしてしまったという気持ちです。

Straightanswer.orgは問いにUnique Indexが作られています。すでに存在している問いを作ろうとすると、このUnique Indexにより作成ずにエラーになっていました。

偶然同じ問いが作られるのは、問いの数が少ないのでまだまだ先だろうと考えていました。

この問題はすでに解消済みです。

通知を受け取る方法をDiscordにした理由

自分だけの環境(Discordでいうサーバ)を用意するのが簡単だからです。元々アカウントを持っていたというのもあります。

またDiscordのWebhookはとても簡単に使えます。