0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Zoom Team Chat と DALL-E を組み合わせたチャットボットインテグレーション

Last updated at Posted at 2025-01-29

プレゼンテーションの準備や、同僚へのチャットグループ内でのリアクションなどで、オリジナルのイラストを作りたい、と思ったことありますよね。例えば、取り扱っているプロダクトの電力消費量が減りました!ということを表すイラストが欲しい、とか、金曜の夜にトラブルが起きてまだ帰れてないからエナジードリンクが欲しい、とか。
そんな時に、こんなパートナーbotがいたら助かっちゃうかも。

presentation.gif

TL;DR

Zoom Team Chatで描いて欲しいイラストのプロンプトを入力すると、OpenAIのDALL-Eを使って指定したプロンプトに基づく画像を生成します。その後、生成された画像をZoom Team Chatのチャンネルに投稿するチャットボットを実装します。少し長くなりますが、複雑な実装はありませんので、ぜひ試してみてください。

システム構成

概要

このチャットボットインテグレーションは以下のような構成になっています。

  1. Zoom App MarketplaceでのGeneral Appの作成 — Botアプリの構築の前提作業
  2. Slash Commandの設定 — チャットボットがコマンドを受け取る設定
  3. DALL-E APIとの連携 — OpenAI APIを使用して画像を生成する処理
  4. チャットボットからのチャンネルへの送信 — Zoom Chatbot APIを使用したメッセージ送信の処理

以下では、それぞれの部分のコードスニペットを提供しながら解説していきます。


Step 1: Zoom App MarketplaceでのGeneral Appの作成

Appの設定手順

Zoomチャットボットを作成するのは想像ほど難しくないです。ここでは、Zoom App Marketplace上でチャットボットをスタートするための手順を簡単にまとめます。

1. Zoom App Marketplaceにログイン

Zoom App Marketplaceにログインします。

Screenshot 2025-01-28 at 22.03.44.png

2. アプリタイプの選択

「Develop」 -> 「Build App」を選択し、「General App」をタイプとして選びます。

Screenshot 2025-01-28 at 22.05.31.png

「Admin-managed」か「User-managed」のどちらかを選ぶ必要があります。

  • Admin-managed: チャンネルの参加者に公開されます。
  • User-managed: アプリを追加したユーザーにのみ送信されます。

Screenshot 2025-01-28 at 22.06.09.png

3. Slash Commandを設定

「Features」 -> 「Surface」から「Team Chat」にチェックを入れ、「Team Chat Subscription」を有効化します。このSlash CommandはGlobalで一意である必要があることに注意してください。

chrome-capture-2025-1-28.gif

4. サーバーのセットアップ

以下はNode.jsで簡易的なサーバーを構築する例です。これにより、ZoomのBot Endpoint URLやOAuth Redirect URLを設定可能になります。

const express = require('express');
const app = express();
const port = 8080;

// ルートエンドポイント
app.get('/', (req, res) => {
  res.send('It worked!');
});

// OAuth認証リダイレクトエンドポイント
app.get('/authorize', (req, res) => {
  console.log('Authorize request received.');
  res.redirect('https://zoom.us/launch/chat?jid=robot_' + process.env.ZOOM_BOT_JID);
});

// Botのエンドポイント
app.post('/generate-image', (req, res) => {
  console.log(req.body);
  res.send('Chatbot message received');
});

// サーバー起動
app.listen(port, () => console.log(`Server running on http://localhost:${port}`));
  1. ngrokを使用した公開URLの設定
    ローカル開発環境でサーバーを外部からアクセス可能にするには、例えばngrokを使用します。
    ngrok http 8080
    
    このコマンドを実行すると生成されるURLを以下の2つに設定します。
    • OAuth Redirect URL: https://<ngrok-url>/authorize
    • Bot Endpoint URL: https://<ngrok-url>/generate-image

Bot Endpoint URL を入力して、「Save」をクリックすると、Bot JIDが生成されます。上述のコード内の環境変数として設定されている、ZOOM_BOT_JIDにこれを設定します。
以上の設定が完了すれば、Zoom App Marketplaceでアプリを登録し、動作テストを開始できます。

まずはここまでで、いったんアクセスのテストをしてみると良いかもしれません。Add App Nowをクリックすると、Team Chatにアプリが追加され、メッセージが届きます。
Screenshot 2025-01-29 at 14.53.48.png


Step 2: DALL-E APIを使用した画像生成

次に、OpenAI APIを使ってDALL-Eに画像を作成するプロンプトを投げ、レスポンスとして返されるURLを取得するコードを書いてみましょう。事前にOpenAIのページで、API keyを準備することを忘れずに。準備したトークンは、OPENAI_API_KEYの環境変数として設定します。

コードスニペット: DALL-E API連携

const { Configuration, OpenAIApi } = require('openai');

// OpenAI APIクライアントの設定
const configuration = new Configuration({
  apiKey: process.env.OPENAI_API_KEY,
});
const openai = new OpenAIApi(configuration);

// DALL-E APIを呼び出す関数
async function generateImageUrl(prompt) {
  try {
    const response = await openai.images.generate({
      model: "dall-e-3",
      prompt: `draw an illustration of ${prompt}`,
      n: 1,
      size: "1024x1024",
    });
    console.log("Response:", response.data);
    const imageUrl = response.data[0].url;
    console.log(`Image URL: ${imageUrl}`);
    return imageUrl;
  } catch (error) {
    console.error("Error:", error.message);
    throw error;
  }
}

// テスト環境用の設定
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

上記コードでは、process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";を設定することで、テスト環境でHTTPS証明書のチェックを無効にしています。ただし、プロダクション環境ではこの設定を使用しないように注意してください。

このコードに const prompt = "適当なイラスト用のテキスト";を追加して実行すれば、おそらくimage urlが返却され、画像を見ることができると思います。


Step 3: Bot Endpointでのリクエスト検証と環境変数について

以下は、Zoom Team Chatからのリクエストを受け取るエンドポイントのコード例です。このエンドポイントは、ZoomのWebhook認証を検証する仕組みを備えており、Endpoint URL Validationに対応しています。

コードスニペット

const crypto = require('crypto');

app.post("/generate-image", async (req, res) => {
    console.log("Request:", req.body);
    if (verifyWebhook(req)) {
        let response = { message: 'Authorized request from Zoom Team Chat.', status: 200 };
        console.log(response);
        if (req.body.event === 'endpoint.url_validation') {
            response = {
                message: {
                    plainToken: req.body.payload.plainToken,
                    encryptedToken: validateWebhook(req)
                },
                status: 200
            };
        } else if (req.body.event === 'bot_notification') {
            console.log("Bot Notification:", req.body.payload);
            const prompt = `draw a flat illustration of ${req.body.payload.cmd}`;
            console.log("Cmd:", prompt);
            try {
                const imageUrl = await generateImageUrl(prompt);
                sendMessageToZoom(req.body.payload.toJid, req.body.payload.accountId, imageUrl, prompt);
            } catch (error) {
                console.error("Failed to generate image:", error.message);
                res.status(500).send("Failed to generate image");
            }
        } else {
            console.log("Other event.");
        }
        console.log(response.message);
        res.status(response.status).json(response);
    } else {
        res.status(401).send("Unauthorized request");
    }
});

function verifyWebhook(req) {
    const message = `v0:${req.headers['x-zm-request-timestamp']}:${JSON.stringify(req.body)}`;
    const hashForVerify = crypto.createHmac('sha256', process.env.ZOOM_WEBHOOK_SECRET_TOKEN).update(message).digest('hex');
    const signature = `v0=${hashForVerify}`;
    return req.headers['x-zm-signature'] === signature;
}

function validateWebhook(req) {
    return crypto.createHmac('sha256', process.env.ZOOM_WEBHOOK_SECRET_TOKEN).update(req.body.payload.plainToken).digest('hex');
}

Endpoint URL Validationについて

このコードは、ZoomのWebhook機能を使用する際にリクエストを検証する仕組みを提供します。verifyWebhook関数は、Zoomから送られてきたリクエストヘッダーの署名を検証し、正規のリクエストであることを確認します。

  • endpoint.url_validationイベント: ZoomがWebhookエンドポイントを検証する際に送信するイベントです。この場合、ZoomにplainTokenencryptedTokenの両方を返す必要があります。
  • bot_notificationイベント: Botが受け取る通知イベントです。このイベントをトリガーに、DALL-Eに画像生成をリクエストします。

環境変数について

一部にはすでに言及していますが、まとめると以下の環境変数が必要です。.envファイルや環境設定で指定してください。

  • OPENAI_API_KEY: OpenAI APIを利用するためのキー。
  • ZOOM_BOT_JID: Zoom BotのJID(Bot ID)。
  • ZOOM_WEBHOOK_SECRET_TOKEN: Zoom Webhookの秘密鍵。この鍵は署名検証に使用されます。
  • ZOOM_CLIENT_ID: Team Chat Botのアプリに設定されているClient ID。
  • ZOOM_CLIENT_SECRET: 同じくSecret。この2つは先に挙げられているencryptedTokenを生成する際に利用します。

Step 4: ZoomのチャットAPIを使用した送信

生成された画像をZoom Team Chatに送信する部分の実装について解説します。

リクエスト例

この範例はZoomのAPIを使用してチャットボットからメッセージを送信するものです。

curl -X POST \
  https://api.zoom.us/v2/im/chat/messages \
  -H "Authorization: Bearer YOUR_ZOOM_BOT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "robot_jid": "YOUR_BOT_JID",
    "to_jid": "YOUR_CHANNEL_OR_USER_JID",
    "account_id": "YOUR_ACCOUNT_ID",
    "content": {
      "head": {
        "text": "Hello!"
      }
    }
  }'

詳しい仕様はこちらを参照していただければと思いますが、基本的にはこれでメッセージを送れるはずです。

以下では、これを基本としつつStep 2で受け取った画像のURLを使って、最初に投げたプロンプトに沿った画像をチャンネルに投稿する、という部分の実装を行います。

画像とプロンプトをチャンネルに投稿

async function sendChat(rid, aid, imageUrl, prompt) {
    try {
        const authResponse = await axios.post('https://api.zoom.us/oauth/token?grant_type=client_credentials', {}, {
            headers: {
                'Authorization': `Basic ${Buffer.from(`${process.env.ZOOM_CLIENT_ID}:${process.env.ZOOM_CLIENT_SECRET}`).toString('base64')}`
            }
        });
        
        const messageResponse = await axios.post('https://api.zoom.us/v2/im/chat/messages', {
            "robot_jid": process.env.ZOOM_BOT_JID,
            "to_jid": rid,
            "account_id": aid,
            "content": {
                "head": {
                    "text": "DALL-E Image"
                },
                "body": [{
                    "type": "attachments",
                    "img_url": imageUrl,
                    "resource_url": imageUrl,
                    "information": {
                        "title": {
                            "text": "DALL-E Image"
                        },
                        "description": {
                            "text": prompt
                        }
                    }
                }]
            }
        }, {
            headers: {
                'Authorization': `Bearer ${authResponse.data.access_token}`,
                'Content-Type': 'application/json'
            }
        });
        
        console.log('Message sent successfully:', messageResponse.data);
        return messageResponse.data;
        
    } catch (error) {
        console.error('Error in sendChat:', error.response?.data || error.message);
        throw error;
    }
}

以上でチャンネルに投稿されるようになったはずです。一度試してみましょう。


まとめ

Zoom Team ChatとDALL-Eを組み合わせることで、自動化や創造性の高いプラットフォームを実現できます。ここで紹介したガイドを基に、あなたのアプリに適したチャットボットを構築してみてください。

今回の実装サンプルは以下に挙げています。ご参考までに。


参考文献

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?