外部非公開の自宅サーバーとDiscord BOTでインタラクションする

Progress 14 / 18
目次

自宅サーバーから通知を受け取る手段として、今更ながらDiscord BOTを作ってみました。

普段からk8sの運用をしていると、「このジョブ終わったかな」「どこかで異常起きてないか」といった情報を手軽にキャッチする口が欲しくなるんですよね。メールだと他の通知に埋もれてしまうし、個人のHomelabでSlackを導入するのも少し大げさ。その点、Discordならスマホにもデスクトップにもスッと通知が届くので、個人的な用途にはぴったりだ。

今回は、おうちk8s環境からDiscordへどうやって連携したのか、備忘録も兼ねて紹介します。

WebhookではなくGateway方式を選ぶ

Discord Developer Portalの公式ドキュメントには、Getting Startedとしていくつかのサンプルが用意されています。

Building your first Discord Bot - Documentation - Discord

Step-by-step tutorial for building your first Discord app.

docs.discord.com

一般的なBOT開発ではWebhookを利用することも多いですが、今回はおうちk8s内に閉じて運用したい。Discord側からWebhookを叩いてもらう(=サーバーを外部公開する)アプローチは避けたかったので、コネクションをこちらから張るGateway方式を利用しました。

そして、k8s内の各アプリからの通知については、BOT内にAPIサーバーを立ててリクエストを受け取り、それをDiscordに横流しする構成としています。

まずはDiscord側の準備からです。

アプリケーションの作成とトークン取得

Discord Developer Portalにアクセスしてアプリを作成します。

Discord for Developers

Build games, experiences, and integrations for millions of users on Discord.

discord.com
  1. 新しいアプリケーションを作成し、適当な名前をつける。
  2. 「Bot」タブから「Reset Token」を選択し、トークンを生成する。

生成されたトークンはページを離れると見えなくなるので、確実にメモしておきます。これがアプリケーションからDiscordに接続するための鍵になります。

サーバーへのBOT追加

次に「OAuth2」タブのURLジェネレーターを使ってBOTをサーバーに招待します。 スコープとして botapplications.commands にチェックを入れます。

権限については最低限 Send Messages があれば十分ですが、自分のテストサーバーであれば管理を楽にするために Administrator を付けてしまっても良いでしょう。

生成されたリンクを踏むと、BOTをサーバーに追加できます。プルダウンで対象のサーバーを間違えないように注意です。

Goとdiscordgoで軽量なAPIを構築する

今回作成した成果物はこちらです。

GitHub - tamara1031/botama

Contribute to tamara1031/botama development by creating an account on GitHub.

github.com

実装にはGoと bwmarrin/discordgo というライブラリを採用しました。

GitHub - bwmarrin/discordgo: (Golang) Go bindings for Discord

(Golang) Go bindings for Discord. Contribute to bwmarrin/discordgo development by creating an account on GitHub.

github.com

k8sで動かす以上、コンテナイメージが軽量で起動が速いGoはやはり正義だ。

コードの大部分はOpusに丸投げしています。「機能の追加や有効/無効の切り替えを抽象化して、module.go周辺で管理しやすいように」といった構成の指示だけ出せば、面倒なルーティングやインターフェースの設計は全てAIがやってくれる。本当に助かります。

ローカルでの動作確認

まずはDocker Composeでローカルテストを行います。 .env ファイルを作成し、先ほど取得した DISCORD_TOKEN を設定してコンテナを起動します。

少し待つとアプリケーションコマンドが同期され、Discord上でスラッシュコマンドが使えるようになります。ちなみに、再度サーバーにBOTを招待し直すとコマンドの同期がすぐに行われるという裏技めいた挙動もあるようです。

Discord上でのBOTとのインタラクション(pingに対してpongと返す様子)
Discord上でのBOTとのインタラクション(pingに対してpongと返す様子)

k8s内連携の実装と、予想外のメリット

ローカルで動くことが確認できたので、本来の目的である「おうちサーバーからの通知連携」を実装していきます。

BOT側にAPIの受け口を用意し、受け取ったペイロードをDiscordに横流しするシンプルな仕組みです。ただ、いくらプライベートネットワークとはいえ無防備なのは気持ち悪いので、簡易的なBearerトークン認証を挟んでいます。

さっそく起動してcurlでテストしてみます。

Terminal window
curl -X POST http://localhost:8080/notify \
-H "Authorization: Bearer your_api_key" \
-H "Content-Type: application/json" \
-d '{"content": "テスト"}'

ピホッという通知音とともに、無事Discordにメッセージが飛びました。

API経由で送信されたテスト通知
API経由で送信されたテスト通知

双方向通信がもたらす可能性

今回、単なる「通知の送信先」として作り始めましたが、実際に動かしてみて驚いたことがあります。Gateway方式を採用した恩恵で、サーバーを外部公開していなくても、Discord側からインタラクションを返せるという点です。

これはセキュリティと利便性を両立する上で大きなメリットです。これなら、単に通知を受け取るだけでなく、エラー通知の画面から「サーバーの再起動」や「特定コンテナのログ取得」といった操作をDiscordから直接キックすることもできそうです。

Homelab界隈で似たようなことをやりたいと考えている方は、AIの手を借りればこのレベルのものはサクッと作れるので、ぜひ試してみてください。