ChatWork × Dify × GAS で「キーワード応答Bot」を作る手順

ChatWork(チャットワーク)で特定のbot用のアカウントにメンションが飛んできたら、Dify(LLMを活用したワークフロー)に問い合わせを行い、その応答を再度チャットワークに返す──というフローを構築できると、営業支援やFAQ回答などのシーンで非常に便利です。
本記事では、Google Apps Script(GAS) を使って ChatWorkのWebhookを受け取り、Dify APIを呼び出し、結果を再びChatWorkに投稿する簡単なサンプル実装を紹介します。

自由研究でやってみました。

動作イメージ

目次

全体像

  1. ChatWorkの設定
    • Botユーザー(またはBot的に使うユーザー)を用意し、Webhookを有効化します。
    • 指定のルームにWebhookを紐づけ、GASのWebアプリURLを設定します。
  2. GAS側のスクリプト
    • doPost(e) でWebhookを受け取り、メンション文言を判定。
    • Dify APIを呼び出し、応答テキストを取得。
    • 取得した応答をChatWorkへメッセージ投稿する。
  3. Dify API
    • LLM等を連携するワークフローをあらかじめDifyで作成し、APIエンドポイントとトークンを取得します。

事前準備

1. ChatWorkのAPIのトークンをメモ

チャットワークの対象ルームで、右上の「アカウント名」→「サービス連携」→「APIトークン」を開きます。
そしてトークンをテキストなどにメモ
※あとでGASからAPIをリクエストする際に使います。
APIトークンは第三者に開示しないよう、取り扱いにご注意ください。

次いで、使うグループチャットを作成し、チャッルームのIDをメモ
チャットワーク画面の右上のグループチャットの設定ボタンをクリックすると、ポップアップの下に記載があります。

次いで、botで使うためのアカウントのIDをメモしておきます。
チャットワーク右上の歯車マーク→「環境設定」→「Chatworkについて」で確認できます。

2. Difyの準備

Dify(https://dify.ai/) にサインアップ。

「ワークフロー」を作成し、問い合わせに対して応答を生成・返すフローを構築。

ここではワークフローの内容は割愛しますが、ざっくり以下のような作りになっています。
入力フィールドで単語を入力そちらをもってナレッジから知識を取得しLLM(gpt-4o-miniモデルで)回答してもらったものをanswerとして受け取る形です。
※このナレッジ部分には対応企業の事例をまとめたエクセルを入れてみました。

アプリを公開し、公開するメニューらAPIリファレンスにアクセスをクリック


APIキー(アクセストークン)とエンドポイントURLをメモします。
APIキーや右上のAPIキーをクリックでポップアップで出力されます。

※ちなみにワークフローに実行のエンドポイントはhttps://api.dify.ai/v1/workflows/run でした。

3. Google Apps Script (GAS)

GASプロジェクトを作成

下記のソースコードを貼り付けます。

/**
 * ChatWorkのWebhookを受け取るエンドポイント
 */
function doPost(e) {
  // 受信データをJSONとしてパース
  const data        = JSON.parse(e.postData.contents);
  const messageBody = data.webhook_event.body; // 本文を取得

  // 受け取ったメッセージにチャットワークIDが入っているかどうかでbot宛てのメンションが含まれているかどうかチェックする
  if (messageBody.indexOf('[To:<botのID>]') !== -1) {
    // メンションを除去(必要に応じて実装)
    const cleanedMessageBody = messageBody.replace("[To:<botのID>]<botの名前>", "").trim();

    // Dify APIを呼び出し → 応答を受け取る
    const difyResponse = callDifyAPI(cleanedMessageBody);

    // そのままChatWorkに投稿
    postMessageToChatwork(difyResponse);

  } else {
    //メンションが含まれていない場合の処理
    const message = `[NG] メンションを含まないメッセージでした`;
    Logger.log(message);
  }

  // レスポンスを返す
  const output = ContentService
    .createTextOutput('{"status":"ok"}')
    .setMimeType(ContentService.MimeType.JSON);

  return output;
}


/**
 * Dify APIを呼び出す関数
 * @param {string} messageBody ユーザから受け取ったメッセージ本文
 * @return {string} Dify APIのレスポンス文字列
 */
function callDifyAPI(messageBody) {
  const url = "<Difyのエンドポイント>";
  
  // 送信するデータ(JSON)sys_qyery_stringsはDifyのワークフローを作成する際に定義するものなので、適宜修正してください。
  const payload = {
    "inputs": {
      "sys_qyery_strings": messageBody
    },
    "response_mode": "blocking",
    "user": "hogehoge"
  };
  
  // リクエストオプション
  const options = {
    "method": "post",
    "headers": {
      "Authorization": "Bearer <DifyのAPIトークン>",  // 実際のAPIキーに置き換えてください
      "Content-Type": "application/json"
    },
    "payload": JSON.stringify(payload),
    "muteHttpExceptions": true
  };

  try {
    // APIリクエスト
    const response     = UrlFetchApp.fetch(url, options);
    const responseText = response.getContentText();

    // JSON をパースしてデータを取得
    const jsonData   = JSON.parse(responseText);
    const answerText = jsonData.data?.outputs?.answer || "[No answer found]";

    // レスポンスをログに出力しておく
    Logger.log("Dify API answer: " + answerText);

    // Chatworkに投稿するため、このanswerTextを呼び出し元に返す
    return answerText;

  } catch (error) {
    Logger.log("Error in callDifyAPI: " + error);
    // エラーメッセージを返す
    return "Error in callDifyAPI: " + error;
  }
}


/**
 * Chatworkにメッセージを投稿するサンプル関数
 * @param {string} message 投稿したいメッセージ本文
 */
function postMessageToChatwork(message) {
  const apiToken = '<ChatworkのAPIトークン>';  // 実際のAPIトークンに置き換えてください
  const roomId   = '<ChatworkのルームID>'; // 実際のルームIDに置き換えてください
  const url      = `https://api.chatwork.com/v2/rooms/${roomId}/messages`;

  // Chatwork APIに送信するデータ
  const payload = {
    body: message
  };

  // フォーム形式に変換
  const formData = Object.keys(payload)
    .map(key => `${key}=${encodeURIComponent(payload[key])}`)
    .join('&');

  // リクエストオプション
  const options = {
    method: 'post',
    headers: {
      'X-ChatWorkToken': apiToken,
    },
    contentType: 'application/x-www-form-urlencoded',
    payload: formData,
    muteHttpExceptions: true
  };

  // API呼び出し
  try {
    const response = UrlFetchApp.fetch(url, options);
    Logger.log(`Chatwork Response: ${response.getContentText()}`);
  } catch (err) {
    Logger.log(`Error in postMessageToChatwork: ${err}`);
  }
}

「doPost(e)」関数がWebhookの受信口になり、メンションされたメッセージをDifyに問い合わせ、その結果をChatWorkへ投稿します。

Difyのワークフロー仕様に応じて、payload の形式を調整する必要があります。

以下の変数は自身の数値への修正してください。
<botのID>:先にメモしているbot用アカウントのID
<botの名前>:botのIDと一致するボットのアカウント名
<Difyのエンドポイント>:Difyのワークフロー作成時にメモしていたエンドポイント
<DifyのAPIトークン>:Difyのワークフロー作成時にメモしていたAPIトークン
<ChatworkのAPIトークン>:Chatワークのアカウント作成したさいにメモしたAPIトークン
<ChatworkのルームID>:BOT動作用のルーム作成した際にメモしたルームID

スクリプトをWebアプリとしてデプロイし、公開URLを取得。
新しいデプロイをクリック→種類の選択でウェブアプリを選択

デプロイの設定を以下に修正し、デプロイを完了します。

アクセスの承認が必要なので、以下の流れでクリックしていく

4. ChatWorkのWebhookを設定

チャットワークの対象ルームで、右上の「アカウント名」→「サービス連携」→「Webhook」を開きます。
「新規作成をクリックし、以下の入力して作成
Webhook名:任意でわかる名前
Webhook URL:GASのデプロイ時にメモしておいたURL
イベント
 ルームイベントを選択
 メッセージ作成をチェック
 ルームIDを準備でメモしていたグループチャットのIDを入力
作成をクリックして作成完了

以上で完成です。

よくあるエラーと対処

  • メンションIDが違う
    [To:hogehogehogehoge] の部分が正しいIDか確認。実際にはBotのアカウントIDと一致している必要があります。
  • APIトークンやURLの誤り
    ChatWorkのAPIトークン、DifyのAPIキーやエンドポイントURLが正しいか再度チェック。
  • 公開範囲の設定ミス
    GASのWebアプリが「全員(匿名ユーザ含む)」などに公開されていない場合、Webhookがエラーになります。

まとめ

  • ChatworkのWebhookを GASで受け取り、メンションキーワードを Dify や外部APIに渡すことで、Botが自動返信する仕組みを構築できます。
  • 営業支援ツールとしての事例検索だけでなく、マニュアル回答、FAQシステムなどさまざまなシーンで応用可能です。
  • ぜひ上記のサンプルと手順を参考に、社内コミュニケーションの自動化に役立ててみてください。

Dify × Chatwork × GAS の連携で、手軽に高度なBotを作れるようになります。興味のある方は本記事のサンプルをベースに、自身のユースケースに合わせたカスタマイズに挑戦してみてください。

目次