Ryujinは、Clairemontボットの外交連絡モジュールです。システム内の他のモジュールがセキュリティや管理を担当する一方、Ryujinは同盟関係の構築、共同イニシアチブの調整、ギルド間チャンネルでの冷静な対応の維持に重点を置いています。ElixirとNostrumで構築されており、高性能タスクのためにRustベースの機能に拡張できるように設計されています。
◆ 機能
- 同盟重視の自動化: インテントはダイレクトメッセージおよびギルドレベルの関係管理用に事前設定されています。
- 音声対応: FFmpeg、Streamlink、yt-dlpをバンドルしており、音声配信をサポートします。
- Rustによる高速化: Rustler NIF用の
native/ryujinクレートを含み、計算負荷の高いタスクを処理します。 - スクリプトによるセットアップ: ローカルの
install.shおよびrun.shスクリプトがツールチェーンと環境を管理し、再現可能なセットアップを実現します。
◆ 前提条件
- Elixir ≥ 1.18(対応するErlang/OTPリリースと共に)
- PostgreSQL(デフォルトでは
localhost上のpostgres/postgres) - Python 3(virtualenv内のStreamlink + yt-dlp用)
curlおよびtar(FFmpegダウンロード用)- Rustツールチェーン(ネイティブ拡張を変更する場合)
開始前にヘルパースクリプトが実行可能であることを確認してください:
chmod +x install.sh run.sh

◆ 設定
設定はenvs/から読み込まれます。実行時にはenvs/.env(共有デフォルト)と、envs/dev.envのような環境固有のファイルがマージされます。
最低限、envs/.envにDiscordトークンを定義する必要があります:
DISCORD_TOKEN=YOUR_DISCORD_TOKEN
実行スクリプトは自動的にローカルのvendor/binとPython virtualenvをPATHに追加します。
◆ インストール
インストーラーはMix依存関係を取得し、必要なメディアバイナリをローカルのvendor/ディレクトリに配置します:
./install.sh
このスクリプトは以下の処理を行います:
- Mix依存関係の取得とコンパイル
- 静的FFmpegビルドのダウンロード
- Streamlinkとyt-dlp用のPython virtualenvの作成
◆ 開発環境での実行
実行スクリプトを使用して開発用にボットを起動します:
./run.sh
このスクリプトは、すべてのツール(ffmpeg、yt-dlpなど)が存在することを確認し、データベースマイグレーションを実行し、iex -S mixを介してPhoenixアプリケーションを起動します。
◆ PostgreSQL AGE統合
このプロジェクトには、グラフクエリ用のApache AGE拡張を有効にするマイグレーションが含まれています。PostgreSQLサーバーにAGEをインストールした後、run.shスクリプトまたはmix ecto.setupを実行すると、データベースでAGEが有効になります。
その後、Ecto.Repo.query/2を使用してCypherクエリを発行できます:
Repo.query!("""
SELECT *
FROM cypher('ryujin_graph', $$
MATCH (n) RETURN n
$$) AS (node ag_catalog.agtype);
""")
◆ ネイティブRust拡張
native/ryujinクレートはRustlerと統合されています。ElixirとRustのコードはmix compileで一緒にコンパイルされます。新しいNIFはsrc/lib.rsに追加し、lib/ryujin.exモジュールを通じてElixirに公開します。
◆ トラブルシューティング
- 「command not found」:
chmod +x install.sh run.shを実行したことを確認してください。 - 古いバイナリ:
vendor/ディレクトリを削除し、./install.shを再実行してください。 - データベースが存在しない: PostgreSQLが実行中であることを確認し、
./run.shを再実行してください(mix ecto.createを実行します)。 - Observerへの接続: ボットは名前付きノードとして実行されます。新しいターミナルを開き、以下を実行してください:
iex --sname observer --cookie ryujin_cookie
:observer.start()
