屋根裏

コードを書いたりします

大学の掲示板の更新をDiscordで通知する

大学の学部掲示板をいちいち確認するのが面倒でLINE Notifyを使って更新を通知させていたのですが、見た目が微妙だったのでDiscord Webhookを使って作り直すことにしました。

LINE Notify

今まで使っていた更新通知は、1年生の夏休みにLINE Notifyを使って作ったものでした。Seleniumで新着ページのスクリーンショットを撮って送信しています。Heroku上でPythonスクリプトを動かしていました。

使い勝手は悪くなかったのですが、見た目が悪いので前々から作り直したいと思っていました。

f:id:yurkth:20200311041831j:plain

Discord Webhook

https://support.discordapp.com/hc/ja/articles/228383668-%E3%82%BF%E3%82%A4%E3%83%88%E3%83%AB-Webhooks%E3%81%B8%E3%81%AE%E5%BA%8F%E7%AB%A0

DiscordにWebhookでメッセージを送ることができます。Botとは違うのでこちらのアクションに反応するのではなく、自動でメッセージを送るだけです。代わりに面倒な認証などは必要ありません。

SlackのIncoming Webhooksと迷ったのですが、普段Discordの方がよく使うのでこちらにしました。

Embedを使うことでリッチなメッセージを送信することもできます。

メッセージの送信は取得したURLにPOSTするだけなのでrequestsでもいいのですが、今回はPythondhooksというライブラリを使いました。

実装

更新の検知

掲示板の新着掲示一覧のHTMLを見てみると、それぞれの掲示へのリンクは普通にaタグで貼ってあったので正規表現を使いURLを抽出します。

all_articles = re.findall('<A HREF="(.+?)"', articles_html)

前回はBeautiful Soupを使っていたのですが、大した処理でもないので今回は正規表現だけで済ませてしまいます。

更新された差分は、前回の最新記事のURLをファイルに保存しておき、そのインデックスより前の要素から取得できます。

updated_articles = all_articles[: all_articles.index(latest_article)]

Embedの作成

掲示のHTMLを見てみると、掲示の本文は<!-- begin text --><!-- end text -->というコメントの間にあったのでこれも正規表現で取得します。

body = re.search(
    "<!-- begin text -->\r\n(.+?)<!-- end text -->",
    re.sub("<[^<>!]*>", "__", article_html),
    flags=(re.MULTILINE | re.DOTALL),
).group(1)

本文中に色が付けられていたりするとタグがそのまま取得されてしまうので、タグを__という文字列に置き換えることで該当のテキストに下線を引くことができます。

著者やタイトルは上のスクリーンショットにも少し入っていますが、From: 著者名<BR>Subject: タイトル<BR>のようになっていたので正規表現でまとめて取得します。

field = dict(re.findall("(.+?): (.+?)<BR>", article_html))

あとはそれぞれの要素が存在していたらEmbedに追加していきます。

if title := field.get("Subject"):
    embed.set_title(title=f":newspaper: {title}", url=url)

ただし、参照元と添付ファイルに関してはaタグでリンクが指定されているので[]()を使ってリンクにしてからEmbedに追加します。

送信

あらかじめ作っておいたWebhookオブジェクトのsendメソッドにEmbedオブジェクトを渡すだけです。

hook.send(embed=generate_embed(requests.compat.urljoin(url, article)))

例外処理

エラーが発生したときはエラーの文章をそのままDiscordに送ってしまいましょう。

except Exception:
    hook.send(traceback.format_exc())
    return

環境変数

外部に公開するとまずいWebhookのURLなどは、そのままGitHubに上げると危ないので環境変数に保存しておくと安心です。

普通に環境変数に追加するのは嫌なのでdirenvを使用しています。

定期実行

前回はHerokuで動かしていましたが、最近ラズパイを買ったので今回はラズパイで動かすことにしました。

環境構築はWSLの記事とだいたい一緒です。

定期実行にはcronを使います。crontabではなく、/etc/cron.d/に設定ファイルを置いて実行しています。環境変数もこのファイル内に書けます。

結果

一時間に一回、掲示板の更新を確認して新着掲示があると以下のように通知してくれます。

f:id:yurkth:20200311041857p:plain

まとめ

いい感じの見た目になったので満足です。

学科掲示板以外にもなにか通知したいものがあったら適宜追加していきたいです。

yurkth/discord-notify - GitHub

使用したライブラリ