🐰

ブルーアーカイブのプラナちゃんとDiscordで喋ろう!

に公開

TL;DR (記事を読むのが面倒な人向け)

ここをクリックしてPLANAをサーバーに招待!

リンクが無効な場合はこちら:
https://discord.com/discovery/applications/1031673203774464160
リポジトリはこちら:
https://github.com/coffin399/llmcord-JP-plana

  • PLANAは、AI会話から音楽再生までこなす多機能Discordボットです。
  • AIとおしゃべり: @PLANA とメンションするだけで、最新のAIと自然な会話ができます。画像の内容も理解し、相談相手やアイデア出しのパートナーにもなります。
  • 音楽を高機能に再生: /play コマンドでYouTubeの音楽を再生。キュー管理、ループ再生、シャッフルなど、音楽ボットとして欲しい機能が全て揃っています。
  • 他にも機能が盛りだくさん: アキネーター、緊急地震速報(BETA)、動画ダウンロード、ブルーアーカイブ風ガチャ、TRPG用ダイスロールなど、便利で楽しい機能が満載です。
  • リポジトリ (開発に興味がある方向け): https://github.com/coffin399/llmcord-JP-plana

DiscordでAIのプラナちゃんとおしゃべり!多機能Bot「PLANA」ちゃん

はじめまして!この記事では、私が開発している多機能Discord Bot「PLANA」について、その魅力と機能を、技術的な側面も交えながら徹底的に解説します。

「PLANA」は、単なるコマンド実行ボットではありません。AIとの自由な会話を核とし、音楽再生、アキネーター、緊急地震速報まで、あなたのDiscordサーバーをより楽しく、より便利にするための多彩な機能を統合した、まさに「パートナー」と呼ぶにふさわしい存在です。

この記事を読めば、PLANAがあなたのサーバーライフをどう変えることができるのか、その全貌がわかるはずです。

1. PLANAの核心:AIプラナちゃんとの対話機能

PLANAの最も中心的な機能は、大規模言語モデル(LLM)を活用したAIとの対話機能です。まるで人間と話しているかのような、自然で文脈に沿った会話をDiscord上で楽しめます。

使い方:簡単2ステップで会話スタート

使い方は非常に直感的です。

  1. メンションで話しかける: チャンネルで @PLANA こんにちは! のように、Botにメンションを送るだけです。
  2. 返信で会話を続ける: PLANAからのメッセージにDiscordの「返信」機能を使って応答することで、会話を続けることができます。この場合、再度メンションする必要はありません。

多彩な会話能力:あなたの頼れるパートナー

PLANAは単なる雑談相手にとどまりません。あなたの良きパートナーとして、様々な場面で活躍します。

  • 相談相手として: 「今日の夕飯、何がいいかな?」といった日常の相談から、「このPythonコードのエラーが解決できないんだけど、どこが問題だと思う?」といった専門的な相談まで、幅広いトピックに対応します。
  • クリエイティブなアイデア出し: 「新しいYouTube動画の企画を5つ考えて」「このキャラクターに合う名前をいくつか提案して」など、行き詰まった時のブレインストーミング相手として最適です。
  • 文章の要約・翻訳: 長いニュース記事のURLを貼り付けて「この記事を3行で要約して」と頼んだり、外国語の文章を翻訳させたりすることも可能です。
  • 画像認識 (Visionモデル利用時): メッセージと一緒に画像を投稿すると、PLANAがその内容を理解して応答します。「この画像の犬種は何?」「この絵の画風について教えて」「このスクリーンショットのエラーメッセージを解説して」といった、画像に基づいた高度な対話が可能です。

チャンネルごとにAIモデルを切り替え可能!

PLANAのAI機能の大きな特徴として、チャンネルごとに使用するLLMを自由に切り替えられる点が挙げられます。

なぜこれが重要なのでしょうか?LLMにはそれぞれ得意分野、応答速度、そして利用コストに違いがあります。

  • 雑談チャンネル: 応答速度が速く、コストの低いモデルを設定して、気軽に会話を楽しむ。
  • プログラミング相談チャンネル: コーディング能力に優れた、高精度なモデルを設定して、的確なアドバイスをもらう。
  • アイデア出しチャンネル: 創造性の高いモデルを設定して、ユニークな発想を引き出す。

このように、チャンネルの目的に合わせて最適なAIを割り当てることができます。切り替えは /switch-models コマンドで誰でも簡単に行えます。

# llm_cog.py
# チャンネルに設定されたモデルを取得する部分
async def _get_llm_client_for_channel(self, channel_id: int) -> Optional[openai.AsyncOpenAI]:
    channel_id_str = str(channel_id)
    # チャンネル専用の設定があればそれを使い、なければデフォルト設定を使う
    model_string = self.channel_models.get(channel_id_str) or self.llm_config.get('model')
    if not model_string:
        return None
    # ... (クライアントの初期化処理) ...

この仕組みにより、サーバー全体で一つのモデルに縛られることなく、柔軟で効率的なAIの活用が実現します。利用可能なモデルは、Botの管理者が設定ファイルで自由に定義できます。

コード解説:文脈を理解する仕組み

PLANAが自然な会話を続けられる秘密は、過去のやり取りを記憶し、文脈としてLLMに渡している点にあります。

# llm_cog.py
async def _collect_conversation_history(self, message: discord.Message) -> List[Dict[str, Any]]:
    history = []
    current_msg = message
    # メッセージの返信チェーンを遡っていく
    while current_msg.reference and current_msg.reference.message_id:
        try:
            # 親メッセージを取得
            parent_msg = current_msg.reference.resolved or await message.channel.fetch_message(...)
            # ユーザーの発言とBotの発言を交互に履歴に追加していく
            if parent_msg.author != self.bot.user:
                history.append({"role": "user", "content": ...})
            else:
                history.append({"role": "assistant", "content": ...})
            current_msg = parent_msg
        except (discord.NotFound, discord.HTTPException):
            break
    history.reverse() # 時系列順に並び替え
    return history

この _collect_conversation_history 関数が、メッセージの返信を辿って会話の文脈を再構築しています。これにより、PLANAは「さっきの話の続きなんだけど…」といった会話も理解できるのです。

2. サーバーを彩るエンターテイメント機能

PLANAには、会話以外にもサーバーを盛り上げるための機能がたくさんあります。

アキネーター (Akinator)

/akinator コマンドを実行すると、あの有名な「アキネーター」で遊ぶことができます。PLANAからの質問にボタンで答えていくだけで、あなたが思い浮かべた人物やキャラクターをズバリ当ててくれます。

技術的なポイント: この機能は、DiscordのUIコンポーネント(discord.ui.View, discord.ui.Button)を全面的に採用しています。これにより、ユーザーはコマンドを何度も入力することなく、マウス操作だけで直感的にゲームを進めることができます。これは、ユーザー体験(UX)を大幅に向上させるための重要な設計思想です。

# akinator.py
class GameButtonView(discord.ui.View):
    """ゲーム用のボタンビュー"""
    # ...
    @discord.ui.button(label="はい / Yes", style=discord.ButtonStyle.primary, emoji="✅")
    async def yes_button(self, interaction: discord.Interaction, button: discord.ui.Button):
        await self.handle_answer(interaction, "y")

    @discord.ui.button(label="いいえ / No", style=discord.ButtonStyle.primary, emoji="❌")
    async def no_button(self, interaction: discord.Interaction, button: discord.ui.Button):
        await self.handle_answer(interaction, "n")
    # ... (他のボタン定義) ...


しかしながらお祈り駆動開発の影響で25問以上行けない問題があります。

高機能な音楽再生

/play <曲名 or URL> で、ボイスチャンネルにPLANAを呼び出して音楽を再生できます。

  • 豊富なキュー管理: /queueで再生待ちリストを確認したり、/shuffleで順番をシャッフルしたり、/removeで特定の曲を削除したりと、柔軟な管理が可能です。
  • 多彩なループモード: /loopコマンドで「1曲リピート」「キュー全体をリピート」「リピートなし」を切り替えられます。
  • 安定した再生: 内部的にyt-dlpライブラリを使用し、YouTubeはもちろん、様々な動画・音楽サイトからの再生に対応しています。

技術的なポイント: 複数のサーバーで同時に音楽再生を安定して行うため、接続処理には細心の注意を払っています。asyncio.Lockを用いて、同じサーバーで同時に接続処理が走ることを防ぎ、競合によるエラーを回避しています。

# music_cog.py
async def _ensure_voice(self, interaction: discord.Interaction, connect_if_not_in: bool = True) -> Optional[discord.VoiceClient]:
    state = self._get_guild_state(interaction.guild.id)
    # ...
    # ギルドごとの接続ロックで競合を防ぐ
    async with state.connection_lock:
        # ... (接続・切断処理) ...

この堅牢な設計により、多くのユーザーが同時に利用しても安定した音楽体験を提供します。

ガチャ&ダイスロール

  • /gacha: ブルーアーカイブ風のガチャシミュレーションです。10連では☆2以上が1枚確定する仕様も忠実に再現しています。
  • /roll <表記>: 2d6+3 のようなTRPGでよく使われる形式でダイスを振れます。
  • /check: ダイスロールの結果が目標値を達成したかどうかの成功/失敗判定も行えます。「/check expression:1d100 condition:<= target:70」のように入力すれば、CoCなどの技能判定が一発で完了します。

3. 実用的で頼れるユーティリティ機能

エンタメ機能だけでなく、サーバー運営をサポートする実用的な機能も搭載しています。

緊急地震速報(開発中)

日本の緊急地震速報(予報)を指定したチャンネルに通知します。/earthquake <チャンネル> で設定すれば、万が一の際にサーバーメンバーへ迅速に情報を共有できます。

技術的なポイント: この機能は、discord.ext.tasksを利用したバックグラウンドタスクで実現されています。ボットが起動している間、5秒ごとにAPIをチェックし続け、新しい情報があれば即座に通知します。これにより、リアルタイムに近い速報性を確保しています。

# earthquake_notification.py
@tasks.loop(seconds=5)
async def check_eew(self):
    # APIを定期的にチェック
    url = "https://api.p2pquake.net/v2/history?codes=551&limit=1"
    async with self.session.get(url, timeout=10) as response:
        data = await response.json()
        # ... (新しい地震情報を検知したら通知を送信) ...

@check_eew.before_loop
async def before_check_eew(self):
    await self.bot.wait_until_ready() # Botの準備が完了するまで待機

動画・音声ダウンロード

YouTubeやTwitterなどの動画や音声をダウンロードしたい時に便利な機能です。

  • /ytdlp_video <URL>: 動画をダウンロードします。画質を選択することも可能です。
  • /ytdlp_audio <URL>: 音声のみをMP3やFLACなどの形式で抽出してダウンロードします。

技術的なポイント: Discordのファイルサイズ制限(通常8MB)を回避するため、Google Drive APIと連携しています。時間のかかるダウンロードやアップロード処理は、asyncio.to_threadを用いて別スレッドで実行することで、ボット本体の動作をブロックしない(応答が固まらない)ように設計されています。また、プライバシーとリソース管理のため、アップロードされたファイルは一定時間後に自動で削除されます。

その他

  • /meow: かわいい猫の画像で癒やされます。TheCatAPIを利用しています。
  • /serverinfo, /userinfo: サーバーの作成日やメンバー数、ユーザーのアカウント作成日や参加日などを詳しく確認できます。
  • /ping: Botの応答速度(レイテンシ)をミリ秒単位でチェックします。

4. 導入とサポート

PLANAをあなたのサーバーに招待する

PLANAを気に入っていただけたら、ぜひあなたのサーバーに招待してください!
下のリンクをクリックするか、/invite コマンドで表示される招待リンクから、簡単に導入できます。

ここをクリックしてPLANAをサーバーに招待!
リンクが無効な場合はこちら:
https://discord.com/discovery/applications/1031673203774464160

困ったときは

コマンドの使い方がわからなくなったら、まずは /help コマンドをお試しください。Botの全機能の概要と、より詳細なヘルプコマンドへの案内が表示されます。特に、AI機能と音楽機能はそれぞれ /llm_help/music_help で詳しいガイドを確認できます。

サポート

不具合の報告や機能に関する要望は、/support コマンドで表示される開発者の連絡先や、GitHubリポジトリのIssuesまでお気軽にご連絡ください。

まとめ

PLANAは、AIとの対話を中心に、サーバーでの活動を多角的にサポートする、あなたのためのDiscordパートナーです。
ぜひあなたのサーバーに導入して、プラナちゃんとのコミュニケーションや便利な機能を楽しんでみてください。

開発はオープンソースで行っており、誰でもコードを閲覧したり、開発に参加したりすることができます。

リポジトリ: https://github.com/coffin399/llmcord-JP-plana

皆さんのコントリビューションやフィードバックをお待ちしています!

さいごに

この記事の80%はGemini2.5Proが書いています。便利な世の中になりましたね。。。
この記事を作成した段階(2025-09-24)では272サーバーで稼働しています。意外と見つけてくれる人がいるんですね。

ちなみにこのプログラムはllmcordをフォークし好き勝手弄った結果肥大化した物になります。
フォーク元はこちら:
https://github.com/jakobdylanc/llmcord

フォーク元のllmcordを自分でホスティングしたい!って方は自分が書いた下記記事をご参照ください。
https://zenn.dev/coffin299/articles/43e171d5af6e7f

Discussion