🤖

実験的プロダクト: Ubiquitimes, Timesを複数サーバ,(理論上は複数アプリ)間で共有するDiscord Bot

2024/03/06に公開

はじめに

ナマステ.
neruneruna7だ.

実験的に作成していたプロダクトである,UbiquiTimesについて記述する.
実用性を重視したものではなく,考えていた仕組みが実現できるかどうかを実験するためのものである.
一定の区切りがついたので,その記録を残すために本記事を記述する.

作成したリポジトリ
https://github.com/neruneruna7/UbiquiTimes

概要

Timesを複数サーバで共有するDiscord Botである.
正確に言うならば,Discordのサーバーにおいて,特定のチャンネルをユーザー自身のTimesと指定し,任意のサーバー,チャンネルで書き込んだ内容を他のサーバーのTimes指定したチャンネルにも書き込むものである.

名前の由来としては,Ubiquitous(遍在する)とTimes(ソフトウェアエンジニア系のコミュニティにおいてよくある,内部Twitterのようなものだと思っている.由来は知らない)を組み合わせたものである.

また通常のDiscord Botとは異なる点がある.多くのDiscord Botでは,1つの稼働しているBotを,複数のDiscordサーバーに導入するという形式だと思われるが,Ubiquitimesは,1つのBotにつき,1つのDiscordサーバーという形になっている.

多くのbotの形式 UbiquiTimesの形式
1つのbotを複数のサーバに導入しているイメージ図 1つのbotにつき,1つのサーバ上で稼働しているイメージ図

目標

  • Timesに書いた内容を複数のサーバへ拡散することができる
  • 1つのサーバにつき1つのbotという形式である
  • 他のアプリにも対応できるようにする
    • そのために,特定のアプリへの依存性が低い手段を用いて,拡散を行う

背景

同じことを書き込むのに,複数のサーバを行き来するのは面倒だと感じたため.

私は現在,Idea×TechIDEAという2つの学生団体に所属しており,また両者とも主なコミュニケーションツールとしてDiscordを利用している.

同じ内容を書き込みたいことも多いのだが,いちいち移動して書き込むのは面倒であるという動機から作成を始めた.
また,1つのサーバにつき1つのbotという形式にも理由がある.人によってはDiscordではなくSlackや他のコミュニケーションアプリを主に利用する団体に加入している人もいるだろう.その際でも,それぞれのアプリに対応したbotを作成することで,複数のアプリ間でも同じ内容を共有,拡散することができるようになると考えた.
(1つのサーバーに書き込んだ内容を他のサーバーにも飛ばす,ということで本プロダクトでは拡散という語を使用している)

仕様

bot間のやりとり

UbiquiTimesは,以下のようなbot間のやりとりを行う.

指定したサーバに拡散するための設定時

拡散時

解説

すべてのやり取りはWebhookを用いて行われる.設定時のシーケンスダイアグラムでは,bot間で直接やりとりがなされているように見えるが(もちろんそれができるのが理想),実際にはWebhookを用いて相手のDiscordサーバーに送信し,それを送信先のbotが監視し,新しいメッセージが来たというイベント時に受信処理を行うことでやりとりが行われる.

manage_webhookというのは,この設定のやり取りを行うためのWebhookである.Discordサーバにおいて,Bot専用のチャンネルをつくり,そのチャンネルへのWebhookがmanage_webhookとなる.仕様上,このWebhookは公開する必要がある.

ここで問題が生ずる.manage_webhookは公開されているため,誰でもそのWebhookを利用して拡散設定を行うことができてしまう.そのため,拡散設定リクエストを行う際には,署名を行い,それを検証することで,本当にそのサーバーからのリクエストであるかを確認する.

これを実現するために,各サーバの公開鍵とmanage_webhookを管理するサーバが必要となる.これは仕方なく適当に取得だけのAPIを用意したwebサーバをデプロイしている.(理想とする仕組みではない)
このwebサーバへデータの追加,更新,削除ができるのは私だけである(デプロイ時に乗っけており,APIは存在しない).もちろん,Git,GitHub上にもその情報が載らないようにしている.
webサーバーなのは,楽するために少し前からShuttleを用いて動かしているwebサーバーの片隅で動作させているからである.
念のため,リポジトリもprivateにしている.
なにかと面倒な上,私自身が最大の脆弱性となる.いい方法があればとっととやめたい.

初期では,DiscordのAPIを用いてうまいことサーバーの情報を取得できないか,と考えていたものの,サーバーの情報を得るには,(おそらくbotが)そのサーバーに参加しているかサーバーがdiscoverableフィーチャーを持っていなければいけないとのことで,断念した.

設定を終え,内容を拡散するときは,Timesへの直接のWebhookを使用して送信する.

使用方法

現在存在するコマンド一覧

現段階では,コマンド名などにおいて雑な命名になって部分がある[1]

すべてスラッシュコマンドで使用できる.

  • help(),
    • 各コマンドの説明を表示できる.
  • ut_initialize(),
    • botの初期設定を行う.専用のチャンネルを作ってそこで実行することを推奨.
    • manage_webhookの作成,公開鍵ペアの作成などを行う.
  • ut_get_own_server_data(),
    • ut_initialise()で設定した情報を表示できる.
  • ut_times_set(),
    • 実行したチャンネルを,そのユーザーのTimesと指定する.拡散する際の名前も指定する.
  • ut_times_show(),
    • ut_times_set()で設定した情報を表示できる.
  • ut_times_unset(),
    • 実行したユーザーのTimes指定を解除する.どのチャンネルで実行してもよい.
  • ut_times_spread_setting(),
    • 指定したidのサーバーに対し,拡散するための設定を試みる.
    • 識別用のサーバー名を自由につけられる.あくまでユーザー自身がサーバーを識別するためのもの.
  • ut_list(),
    • 現在,実行したユーザーが拡散する設定済みのサーバーの一覧を表示する.
  • ut_times_spread_unset(),
    • 指定したサーバーへの,拡散する設定を解除する.
  • ut_times_release(),
    • 書き込んだ内容を拡散する.
    • スラッシュコマンドではなく,~UTプレフィックスコマンドとして使うことを推奨.
    • スラッシュコマンドでは,送信元のサーバーで書き込んだ内容が残らないため.
  • hello(),
    • world! と返す.動作確認用のコマンド.

導入から拡散するまでの流れ

大きく分けて,3つの存在がある.

  • 送信元
  • 送信先
  • 各サーバーの公開鍵,manage_webhookを管理するサーバー
    • 以下,管理サーバーと呼ぶ

拡散元,拡散先両者のサーバーでbotが稼働している必要がある.

  1. Botの導入後,サーバー管理者はUT_initialize()で設定を行う.
  2. 公開鍵とmanage_webhookを管理サーバーに登録する.
    1. 現段階では私が手動で登録するほかないため,実用には向かないだろう.
  3. それぞれのユーザーは,両者のサーバーでut_times_setコマンドでTimesとなるチャンネルを指定する.
  4. 送信元から,送信先へ向けてut_times_spread_settingコマンドで拡散するための設定を行う.
  5. 送信元から,送信先へ向けてut_times_releaseスラッシュコマンドまたは~UTプレフィックスコマンドで拡散する.

manage_webhookをもつbot専用チャンネルに設定メッセージが書き込まれている様子

manage_webhookをもつチャンネルに設定メッセージが書き込まれている様子

やかましいのでこのチャンネルはミュートにしておくのが推奨.

書き込んだ内容を他サーバへ拡散するコマンドを実行している様子

書き込んだ内容を他サーバへ拡散するコマンドを実行している様子

ut_times_releaseスラッシュコマンド,または画像のように~UTプレフィックスコマンドを使用することで拡散できる.名前が違うだけで両者とも同じコマンドである.
スラッシュコマンドでは,送信元のサーバーで書き込んだ内容が残らないためプレフィックスコマンドを推奨する.

書き込んだ内容を他サーバへ拡散するコマンドを実行している様子

拡散された内容が他サーバのtimesに書き込まれている様子

技術構成

  • 使用言語
    • Rust
  • Discord Bot 作成ライブラリ
    • serenity
    • poise (poiseはserenityのスーパーセット)
  • Webサーバー 作成ライブラリ
    • Actix-Web
  • データベース ライブラリ
    • (主体として)Sled
  • デプロイ用プラットフォーム
    • Shuttle
  • 公開鍵生成
    • rsa
  • 署名,検証ライブラリの形式
    • JWS

Rustを使用しているのは,単に最も得意だから,使いたかったからである.
データベースとしてキーバリュー型のDBライブラリであるSledを使用しているのは,このプロダクトにおいてRDBはあまり相性がよくないのではないかと考えたのと,単に使ってみたかったからである.ただし,Shuttleではshuttle-shared-databasesというPostgreSQLベースの共有データベースが提供されているほか,shuttle-persistというKVS型のデータベースが提供されている.Sledは無理やり使っているだけであり,本来ならばShuttleの提供するものを使うのがよいだろう.

利点・欠点

利点

  • 複数のアプリの間でも,アプリにあったbotを作ることで内容の共有が可能(なハズである)
  • 個々のコンピューティングコストが小さい. どこか1点に負荷が集中しにくい
  • 管理するのはサーバーごとなので,サーバー管理者はそのサーバーに適したようにカスタマイズ,機能追加できる

欠点

  • 導入するのが少々大変
  • サーバー管理者の手間が増える
  • 偽装されたリクエストや,攻撃的,迷惑目的のメッセージがmanage_webhookを通じて送られてくる可能性がある
  • 私が最大の脆弱性

結論

欠点は多いが,目標としていたこと自体は実現できる可能性は十分あると考える.
実際に目標を達成できるかどうかは,Slack等他のアプリに対応したBotを作成して試してみなければわからない.しかし,Slack等でもWebhookは利用できるはずであり,十分に実現できると考える.

今後

テストやロギングなど,不足している部分も多いので,それらを学びつつ実装していきたい.
以下今後おこないたいことのリスト

  • テストの追加
  • ロギングの追加
  • 設計,ディレクトリ構造などの改善
  • Webhookの仕様を学ぶ
    • Webhookの認証?を特に
  • etc...

また,比較的実用的なバージョンのDiscord用Times拡散Botを作成することも考えている.

おわりに

データベースへのアクセスや,署名,外部のサーバーへのアクセスなどを行い,その過程で学べたことも多かった.
本プロダクトのディレクトリ構造や設計は,Rust Fullstack WorkshopにてデータベースへのアクセスでDAOパターンを知り,それを参考にして作られた部分が多い.あまりうまく扱えていない部分もあるだろうが,この設計パターンは非常に便利だと感じた.
設計については知らないことが多いが,様々な設計パターンなどを学んで手札を増やしていきたい.

脚注
  1. 本プロダクトの命名などには,ARMORED CORE Ⅵ から影響を受けている部分が時折ある. 拡散とリリースなど. また偶然にもDiscord botライブラリpoiseの名前は,同FROM SOFTWARE社のゲーム DARK SOULS でのステータスの1つである 強靭度 の英語名が由来となっているようだ. ↩︎

Discussion