プレゼンテーションの準備や、同僚へのチャットグループ内でのリアクションなどで、オリジナルのイラストを作りたい、と思ったことありますよね。例えば、取り扱っているプロダクトの電力消費量が減りました!ということを表すイラストが欲しい、とか、金曜の夜にトラブルが起きてまだ帰れてないからエナジードリンクが欲しい、とか。
そんな時に、こんなパートナーbotがいたら助かっちゃうかも。
TL;DR
Zoom Team Chatで描いて欲しいイラストのプロンプトを入力すると、OpenAIのDALL-Eを使って指定したプロンプトに基づく画像を生成します。その後、生成された画像をZoom Team Chatのチャンネルに投稿するチャットボットを実装します。少し長くなりますが、複雑な実装はありませんので、ぜひ試してみてください。
システム構成
概要
このチャットボットインテグレーションは以下のような構成になっています。
- Zoom App MarketplaceでのGeneral Appの作成 — Botアプリの構築の前提作業
- Slash Commandの設定 — チャットボットがコマンドを受け取る設定
- DALL-E APIとの連携 — OpenAI APIを使用して画像を生成する処理
- チャットボットからのチャンネルへの送信 — Zoom Chatbot APIを使用したメッセージ送信の処理
以下では、それぞれの部分のコードスニペットを提供しながら解説していきます。
Step 1: Zoom App MarketplaceでのGeneral Appの作成
Appの設定手順
Zoomチャットボットを作成するのは想像ほど難しくないです。ここでは、Zoom App Marketplace上でチャットボットをスタートするための手順を簡単にまとめます。
1. Zoom App Marketplaceにログイン
Zoom App Marketplaceにログインします。
2. アプリタイプの選択
「Develop」 -> 「Build App」を選択し、「General App」をタイプとして選びます。
「Admin-managed」か「User-managed」のどちらかを選ぶ必要があります。
- Admin-managed: チャンネルの参加者に公開されます。
- User-managed: アプリを追加したユーザーにのみ送信されます。
3. Slash Commandを設定
「Features」 -> 「Surface」から「Team Chat」にチェックを入れ、「Team Chat Subscription」を有効化します。このSlash CommandはGlobalで一意である必要があることに注意してください。
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}`));
-
ngrokを使用した公開URLの設定
ローカル開発環境でサーバーを外部からアクセス可能にするには、例えばngrokを使用します。このコマンドを実行すると生成されるURLを以下の2つに設定します。ngrok http 8080
-
OAuth Redirect URL:
https://<ngrok-url>/authorize
-
Bot Endpoint URL:
https://<ngrok-url>/generate-image
-
OAuth Redirect URL:
Bot Endpoint URL
を入力して、「Save」をクリックすると、Bot JID
が生成されます。上述のコード内の環境変数として設定されている、ZOOM_BOT_JID
にこれを設定します。
以上の設定が完了すれば、Zoom App Marketplaceでアプリを登録し、動作テストを開始できます。
まずはここまでで、いったんアクセスのテストをしてみると良いかもしれません。Add App Now
をクリックすると、Team Chatにアプリが追加され、メッセージが届きます。
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にplainToken
とencryptedToken
の両方を返す必要があります。 -
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を組み合わせることで、自動化や創造性の高いプラットフォームを実現できます。ここで紹介したガイドを基に、あなたのアプリに適したチャットボットを構築してみてください。
今回の実装サンプルは以下に挙げています。ご参考までに。
参考文献