WeCom(企業向け WeChat)
目次
Hermes を、Tencent の企業向けメッセージングプラットフォームである WeCom(企业微信)につなぎます。アダプターは WeCom の AI Bot 向け WebSocket ゲートウェイを使って双方向にやり取りするので、公開のエンドポイントも Webhook も要りません。
あわせて読む: 受信用の Webhook の設定は WeCom Callback を参照してください。
前提
- WeCom の組織アカウント
- WeCom 管理コンソールで作成した AI Bot
- Bot の認証情報のページにある Bot ID と Secret
- Python パッケージ:
aiohttpとhttpx
設定
手順 1: AI Bot を作る
おすすめ: スキャンして作成する(コマンド 1 つ)
hermes gateway setupWeCom を選び、WeCom のスマートフォンアプリで QR コードを読み取ります。Hermes が必要な権限を備えた Bot アプリケーションを自動で作り、認証情報を保存します。
設定のウィザードは次のように進みます。
- ターミナルに QR コードを表示します
- WeCom のスマートフォンアプリで読み取るのを待ちます
- Bot ID と Secret を自動で取得します
- アクセス制御の設定を案内します
別のやり方: 手動で設定する
スキャンでの作成が使えない場合、ウィザードは手入力に切り替わります。
- WeCom 管理コンソールにログインします
- Applications → Create Application → AI Bot と進みます
- Bot の名前と説明を設定します
- 認証情報のページから Bot ID と Secret をコピーします
hermes gateway setupを実行し、WeCom を選んで、聞かれたら認証情報を入力します
手順 2: Hermes を設定する
方法 A: 対話式の設定(おすすめ)
hermes gateway setupWeCom を選び、聞かれた内容に答えていきます。ウィザードは次の項目を案内します。
- Bot の認証情報(QR コードの読み取り、または手入力)
- アクセス制御の設定(許可リスト、pairing 方式、または誰でも使える形)
- 通知の届け先となるホームチャンネル
方法 B: 手動で設定する
~/.hermes/.env に次の内容を書き足します。
WECOM_BOT_ID=your-bot-id
WECOM_SECRET=your-secret
# Optional: restrict access
WECOM_ALLOWED_USERS=user_id_1,user_id_2
# Optional: home channel for cron/notifications
WECOM_HOME_CHANNEL=chat_id手順 3: ゲートウェイを起動する
hermes gatewayできること
- WebSocket による通信 — 接続を張り続けるので、公開のエンドポイントは要りません
- 個別チャットとグループでのやり取り — アクセスの方針を設定できます
- グループごとの送信者の許可リスト — グループごとに、誰が Bot とやり取りできるかを細かく決められます
- メディアへの対応 — 画像・ファイル・音声・動画のアップロードとダウンロード
- AES で暗号化されたメディア — 受け取った添付を自動で復号します
- 引用の文脈 — 返信のつながりを保ちます
- マークダウンの表示 — 書式付きの応答
- 返信の対応づけ — 応答が、届いたメッセージの文脈に結びつきます
- 自動での再接続 — 接続が切れたときは間隔を広げながらつなぎ直します
設定できる項目
config.yaml の platforms.wecom.extra の下に設定します。
| キー | 既定値 | 説明 |
|---|---|---|
bot_id |
— | WeCom の AI Bot ID(必須) |
secret |
— | WeCom の AI Bot Secret(必須) |
websocket_url |
wss://openws.work.weixin.qq.com |
WebSocket ゲートウェイの URL |
dm_policy |
open |
個別チャットのアクセス: open、allowlist、disabled、pairing |
group_policy |
open |
グループのアクセス: open、allowlist、disabled |
allow_from |
[] |
個別チャットを許可する利用者 ID(dm_policy=allowlist のとき) |
group_allow_from |
[] |
許可するグループ ID(group_policy=allowlist のとき) |
groups |
{} |
グループごとの設定(後述) |
stream_keepalive_enabled |
false |
長いやり取りのとき、WeCom の約 6 分で切れる返信ストリームの枠を延ばすため、一定間隔でキープアライブのフレームを送ります |
stream_keepalive_interval_seconds |
120 |
有効にしたときの、キープアライブのフレームを送る間隔 |
stream_safe_duration_seconds |
330 |
ストリームがこの秒数を過ぎると、締めくくりの送信では確実に届くこちらからの送信を優先します |
アクセスの方針
個別チャットの方針
誰が Bot に個別のメッセージを送れるかを決めます。
| 値 | 振る舞い |
|---|---|
open |
誰でも Bot に個別のメッセージを送れます(既定) |
allowlist |
allow_from に載っている利用者 ID だけが個別のメッセージを送れます |
disabled |
個別のメッセージをすべて無視します |
pairing |
pairing 方式(最初の設定用) |
WECOM_DM_POLICY=allowlistグループの方針
どのグループで Bot が応答するかを決めます。
| 値 | 振る舞い |
|---|---|
open |
すべてのグループで応答します(既定) |
allowlist |
group_allow_from に載っているグループ ID でだけ応答します |
disabled |
グループのメッセージをすべて無視します |
WECOM_GROUP_POLICY=allowlistグループごとの送信者の許可リスト
細かく決めたい場合は、特定のグループの中で Bot とやり取りできる利用者を絞り込めます。設定は config.yaml で行います。
platforms:
wecom:
enabled: true
extra:
bot_id: "your-bot-id"
secret: "your-secret"
group_policy: "allowlist"
group_allow_from:
- "group_id_1"
- "group_id_2"
groups:
group_id_1:
allow_from:
- "user_alice"
- "user_bob"
group_id_2:
allow_from:
- "user_charlie"
"*":
allow_from:
- "user_admin"仕組み:
- まず
group_policyとgroup_allow_fromで、そのグループを扱うかどうかが決まります。 - グループが最初の判定を通ったら、
groups.<group_id>.allow_fromの一覧(あれば)で、そのグループの中の誰が Bot とやり取りできるかをさらに絞ります。 - ワイルドカードの
"*"のグループ項目は、明示的に書かれていないグループの既定として使われます。 - 許可リストの項目では
*のワイルドカードで全員を許可でき、大文字と小文字は区別されません。 - 項目には
wecom:user:やwecom:group:の接頭辞を付けても構いません。接頭辞は自動で取り除かれます。
グループに allow_from を設定していない場合、そのグループにいる全員が許可されます(グループ自体が最初の判定を通っていることが前提です)。
メディアへの対応
受信
アダプターは、利用者から届いた添付メディアを受け取り、エージェントが扱えるよう手元に保存します。
| 種類 | 扱われ方 |
|---|---|
| 画像 | ダウンロードして手元に保存します。URL 形式と base64 形式の両方に対応します。 |
| ファイル | ダウンロードして保存します。ファイル名は元のメッセージのものを保ちます。 |
| 音声 | 音声メッセージの文字起こしがあれば取り出します。 |
| 混在したメッセージ | WeCom の複数の種類が混ざったメッセージ(テキスト + 画像)を解析し、含まれる要素をすべて取り出します。 |
引用されたメッセージ: 引用(返信元)のメッセージに含まれるメディアも取り出すので、エージェントは何に対する返信なのかを把握できます。
AES で暗号化されたメディアの復号
WeCom は、受信するメディアの添付の一部を AES-256-CBC で暗号化します。アダプターはこれを自動で処理します。
- 届いたメディアの項目に
aeskeyのフィールドが含まれている場合、アダプターは暗号化されたデータをダウンロードし、PKCS#7 のパディングを伴う AES-256-CBC で復号します。 - AES の鍵は
aeskeyフィールドを base64 で復号した値です(ちょうど 32 バイトである必要があります)。 - IV は鍵の先頭 16 バイトから作られます。
- この処理には Python パッケージの
cryptography(pip install cryptography)が必要です。
設定は要りません。暗号化されたメディアを受け取ると、復号は裏側で自動的に行われます。
送信
| メソッド | 送るもの | 大きさの上限 |
|---|---|---|
send |
マークダウンのテキストメッセージ | 4000 文字 |
send_image / send_image_file |
WeCom 本来の画像メッセージ | 10 MB |
send_document |
添付ファイル | 20 MB |
send_voice |
音声メッセージ(本来の音声として送れるのは AMR 形式のみ) | 2 MB |
send_video |
動画メッセージ | 10 MB |
分割してのアップロード: ファイルは 512 KB ごとに区切り、3 段階の手順(init → chunks → finish)でアップロードします。この処理はアダプターが自動で行います。
自動での切り替え: メディアがその種類本来の上限を超えていても、全体の上限である 20 MB に収まっている場合は、一般的な添付ファイルとして自動的に送ります。
- 10 MB を超える画像 → ファイルとして送信
- 10 MB を超える動画 → ファイルとして送信
- 2 MB を超える音声 → ファイルとして送信
- AMR 以外の音声 → ファイルとして送信(WeCom 本来の音声は AMR にしか対応していません)
全体の上限である 20 MB を超えるファイルは送られず、その旨を知らせるメッセージがチャットに投稿されます。
返信方式での応答
WeCom のコールバック経由でメッセージを受け取ると、アダプターは届いた要求の ID を覚えておきます。その要求の文脈がまだ生きているうちに応答を送る場合、アダプターは WeCom の返信方式(aibot_respond_msg)を使い、応答を元のメッセージに直接結びつけます。WeCom のクライアント上で、より自然な会話に見えます。
返信のストリームが生きている間、応答は返信方式の msgtype: "stream" フレームで少しずつ流れていきます。届いた要求の文脈が期限切れになっているか使えない場合(あるいはストリームのフレームの送信に失敗した場合)は、aibot_send_msg によるこちらからの送信に切り替えます。
返信方式はメディアにも使えます。アップロードしたメディアも、元のメッセージへの返信として送れます。
接続と再接続
アダプターは、WeCom のゲートウェイ wss://openws.work.weixin.qq.com への WebSocket 接続を張り続けます。
接続の流れ
- 接続: WebSocket 接続を開き、bot_id と secret を載せた
aibot_subscribeの認証フレームを送ります。 - ハートビート: 接続を保つため、アプリケーション層の ping フレームを 30 秒ごとに送ります。
- 待ち受け: 届いたフレームを読み続け、メッセージのコールバックへ渡します。
再接続の動き
接続が切れると、アダプターは待ち時間を延ばしながら再接続します。
| 試行 | 待ち時間 |
|---|---|
| 1 回目 | 2 秒 |
| 2 回目 | 5 秒 |
| 3 回目 | 10 秒 |
| 4 回目 | 30 秒 |
| 5 回目以降 | 60 秒 |
再接続に成功するたび、待ち時間の数え直しが行われます。切断時には処理待ちの要求をすべて失敗させるので、呼び出し側が延々と待たされることはありません。
重複の除去
受け取ったメッセージは、メッセージ ID をもとに 5 分の枠、最大 1000 件のキャッシュで重複を除きます。再接続やネットワークの乱れで、同じメッセージが二重に処理されるのを防ぎます。
環境変数の一覧
| 変数 | 必須 | 既定値 | 説明 |
|---|---|---|---|
WECOM_BOT_ID |
✅ | — | WeCom の AI Bot ID |
WECOM_SECRET |
✅ | — | WeCom の AI Bot Secret |
WECOM_ALLOWED_USERS |
— | _(空)_ | ゲートウェイ全体の許可リストに使う利用者 ID のカンマ区切りの一覧 |
WECOM_HOME_CHANNEL |
— | — | 定期タスクや通知の出力先となるチャット ID |
WECOM_WEBSOCKET_URL |
— | wss://openws.work.weixin.qq.com |
WebSocket ゲートウェイの URL |
WECOM_DM_POLICY |
— | open |
個別チャットのアクセスの方針 |
WECOM_GROUP_POLICY |
— | open |
グループのアクセスの方針 |
困ったときは
| 症状 | 対処 |
|---|---|
WECOM_BOT_ID and WECOM_SECRET are required |
両方の環境変数を設定するか、設定のウィザードで指定します |
WeCom startup failed: aiohttp not installed |
aiohttp を入れます: pip install aiohttp |
WeCom startup failed: httpx not installed |
httpx を入れます: pip install httpx |
invalid secret (errcode=40013) |
Bot の認証情報と secret が一致しているか確かめます |
Timed out waiting for subscribe acknowledgement |
openws.work.weixin.qq.com へのネットワーク接続を確かめます |
| グループで Bot が応答しない | group_policy の設定を見直し、そのグループ ID が group_allow_from に入っているか確かめます |
| グループ内の特定の利用者が無視される | groups の設定にあるグループごとの allow_from の一覧を確かめます |
| メディアの復号に失敗する | cryptography を入れます: pip install cryptography |
cryptography is required for WeCom media decryption |
受け取ったメディアが AES で暗号化されています。pip install cryptography で入れてください。 |
| 音声メッセージがファイルとして送られる | WeCom 本来の音声は AMR 形式にしか対応していません。他の形式は自動でファイルに切り替わります。 |
File too large のエラーが出る |
WeCom はすべてのファイルのアップロードに 20 MB の上限を設けています。圧縮するか分割してください。 |
| 画像がファイルとして送られる | 10 MB を超える画像は本来の画像の上限を超えるため、添付ファイルへ自動で切り替わります。 |
Timeout sending message to WeCom |
WebSocket が切れている可能性があります。再接続のログを確認してください。 |
WeCom websocket closed during authentication |
ネットワークの問題か、認証情報の誤りです。bot_id と secret を確かめてください。 |