Dify × Discord (Slash Command)連携

この記事は、Qiita Dify Advent Calendar 2024 の16日目の記事です。
qiita.com

はじめに

本記事では、DiscordのSlash CommandAWS Lambda で受け取り、そのイベントを Dify に転送する仕組みを紹介します。

Discordアプリを作成した経験がある方はご存知かもしれませんが、Slash Commandが実行されると、あらかじめ設定したInteractions Endpoint URLにイベントが送信されます。しかし、このURLは1つしか設定できないため、1つのDiscordアプリから複数のDifyアプリを呼び出すには少しだけ工夫が必要です。本記事では、その実現方法を紹介します。

また、Discordアプリを常駐させるとコスト面で不利になるため、必要なときにのみイベントを受けDifyに連携できるようAWS Lambdaを採用しています。

前提条件

  • AWSアカウントがあること
  • AWS SAM CLIをインストール済みで、初期設定が終わっていること
  • 連携したいDifyアプリ(workflow)を作成済みであること
  • Discordのアプリを作成済みで、下記の権限(bot, applications.commands)を有効にして任意のサーバーに入れていること

    discord-app-oauth2

注意事項

  • 本記事で対象にしているDifyアプリはworkflowのみです。chatbotを利用したい場合はリクエスト先を変更することで動くはずです。
  • Slash CommandとLambdaの連携については最低限のみ記載します。詳細は本記事で紹介するソースコードや他の方の記事を見ていただければと思います。

Slash Commandとは?

Slash Commandは、Discord上で「/」を入力することで呼び出せるコマンド機能です。
例えば、「/」を入力すると使用可能なコマンド一覧が表示され、「/neko」などのコマンドを実行できます。



ex-slash-command

上記の例は"text"を入力するようにしていますが、オプションで指定できApplication Commandsあたりに詳しく書いてあります。

システム構成



system-architecture

  1. Discordでコマンドを実行
  2. Lambdaがイベントを受け取り、どのDify Appに送信するかを識別してリクエス
  3. Dify Appが動作し、Discordにメッセージ送信

ソースコード

SAMでデプロイできるようにしています。コードを自分用に一部書き換える必要があるので、該当箇所だけ記載します。他の箇所はSlash CommandとLambdaの連携についてなので、割愛します。 github.com

  • register-commands:コマンドの登録用Lambda
  • discord:Discordからのイベントを受けるLambda

register-commands

DiscordのSlash Commandは事前にコマンドの登録をするLambdaです。今回は複数人で開発をすることを想定しLambdaで動かしていますが、ローカルで実行でも大丈夫です。

  • コマンドは下記のように記載してください。optionsはApplication Commandsあたりを参照し、自身のDifyアプリに合わせてください。
    commands = [
        {
            'name': 'neko', 
            'description': 'にゃーん',
            'options': [
                    {
                        "name": "text",
                        "description": "string",
                        "type": 3,
                        "required": True,
                    }
                ]
        },
        {
            'name': 'summary',
            'description': '要約',
            'options': [
                {
                    "name": "url",
                    "description": "string",
                    "type": 3,
                    "required": True,
                }
            ]
        }
    ]

discord

Discordからイベントを受け取った際に、コマンド名を取得し正しいDifyアプリへリクエストを送信、DifyからのレスポンスをDiscordへ送信するLambdaです。Lambdaの関数URLを発行します。

  • 自身のコマンド名に合わせ、下記のコードを変更してください。inputsはDifyアプリの開始ノードに合わせてください。command_inputは入力内容をDiscordに返すための変数です。(Difyアプリの実行には影響しません)

例:/nekoではqueryを設定

    inputs = {}
    if command_name == 'neko':
        command_input += f'text={body['data']['options'][0]['value']}'
        inputs = {
            'query': body['data']['options'][0]['value']
        }
    elif command_name == 'summary':
        command_input += f'url={body['data']['options'][0]['value']}'
        inputs = {
            'url': body['data']['options'][0]['value']
        }
  • Dify アプリの終了ノードはtextで返すようにしてください。 変更したい場合は、下記のdataを終了ノードの出力に合わせてください。
    response = send_dify_workflow(inputs, body['member']['user']['username'], DIFY_API_KEY)
    logger.info(f'dify response: {response}')

    url = f'https://discordapp.com/api/channels/{channel_id}/messages'
    headers = {
        'authorization': f'Bot {DISCORD_BOT_TOKEN}',
        'content-type': 'application/json'
    }
    data = {
        'content': response['data']['outputs']['text'],
    }
    r = requests.post(url, headers=headers, json=data)
  • (変更不要です。紹介のみ。)DifyアプリをLambdaで振り分けるロジックは下記の部分です。command_nameにはregister-commandsで登録するnameが入ってきます。
    DIFY_API_KEY = get_ssm_parameter(f'/dify/app/{command_name}')

デプロイ & Discordアプリ設定

準備

登録手順を参考に下記の値をパラメータストアにに登録してください。(SAMでデプロイする前でも後でもOK)

名前
/discord/dify/DISCORD_APP_ID Discord Developer Portal > Discordアプリ > General Information > Application ID
/discord/dify/DISCORD_BOT_TOKEN Discord Developer Portal > Discordアプリ > Bot > TOKEN
/discord/dify/PUBLIC_KEY Discord Developer Portal > Discordアプリ > General Information > Public Key
/dify/ENDPOINT Difyのエンドポイント
/dify/app/[command name] DifyアプリのAPI KEY。必要なコマンドの分だけ登録してください。この記事では/dify/app/neko/dify/app/summaryを登録しています。

登録手順

  1. パラメータの作成
  2. 赤枠を入力・選択して「パラメータを作成」をクリック

デプロイ

$ sam build
$ sam deploy

デプロイが成功すると下記のようにLambdaの関数URLが出力されるのでコピーします。

deploy-output

Discordアプリ設定

Discord Developer Portal > Discordアプリ > General Information > Interactions Endpoint URLに上記でコピーした関数URLをペーストし、「Save Changes」をクリック

[
interaction-endpoint-url

下記のメッセージが表示されればOK。

コマンド登録

register-commandsのLambda(そのままデプロイしていれば、discord-slashcommand-CommandRegisterFunction-xxxxxxxxxxxxのような関数名)を開き、テスト実行してください。statusCode": 200が返ってきていればコマンド登録はできているはずです。

関数URLを発行してcurlで実行しても良いし、そもそもLambdaにせずローカルで実行してもよいです。おまかせします。

コマンド実行

"/[command name]"で下記のようにコマンドが表示されていればOKです。



neko-slash-command

実行しましょう!アプリから返答があればOKです!(/nekoは猫語で返答してくれるworkflow)

まとめ

DiscordのSlash CommandからDifyを呼び出す仕組みについて紹介しました。

コマンド登録の部分は改良したい。