WeCom (Enterprise WeChat)
Hermes를 Tencent의 enterprise messaging platform인 WeCom(企业微信)에 연결합니다. adapter는 WeCom의 AI Bot WebSocket gateway를 사용해 realtime bidirectional communication을 제공합니다. public endpoint나 webhook은 필요 없습니다.
prerequisites
- WeCom organization account
- WeCom Admin Console에서 만든 AI Bot
- bot credential page의 Bot ID와 Secret
- Python package:
aiohttp,httpx
setup
Step 1: AI Bot 만들기
권장: scan-to-create(명령 하나)
hermes gateway setup
WeCom을 선택하고 WeCom mobile app으로 QR code를 scan합니다. Hermes가 올바른 permission을 가진 bot application을 자동 생성하고 credential을 저장합니다.
setup wizard가 하는 일:
- terminal에 QR code 표시
- WeCom mobile app으로 scan할 때까지 대기
- Bot ID와 Secret 자동 retrieval
- access control configuration 안내
대안: manual setup
scan-to-create를 사용할 수 없으면 wizard가 manual input으로 fallback합니다.
- WeCom Admin Console에 로그인합니다.
- Applications → Create Application → AI Bot으로 이동합니다.
- bot name과 description을 설정합니다.
- credential page에서 Bot ID와 Secret을 copy합니다.
hermes gateway setup을 실행하고 WeCom을 선택한 뒤 prompt가 나오면 credential을 입력합니다.
Bot Secret은 비공개로 유지하세요. 이 값을 가진 사람은 bot을 impersonate할 수 있습니다.
Step 2: Hermes 설정
Option A: interactive setup(권장)
hermes gateway setup
WeCom을 선택하고 prompt를 따릅니다. wizard가 다음을 안내합니다.
- bot credential(QR scan 또는 manual entry)
- access control setting(allowlist, pairing mode, open access)
- cron/notification용 home channel
Option B: manual configuration
~/.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
Step 3: gateway 시작
hermes gateway
features
- WebSocket transport - persistent connection, public endpoint 필요 없음
- DM and group messaging - configurable access policy
- Per-group sender allowlists - group별로 interaction 가능 sender를 세밀하게 제어
- Media support - image, file, voice, video upload/download
- AES-encrypted media - inbound attachment 자동 decrypt
- Quote context - reply threading 보존
- Markdown rendering - rich text response
- Reply-mode streaming - inbound message context에 response를 correlate
- Auto-reconnect - connection drop 시 exponential backoff
configuration options
config.yaml의 platforms.wecom.extra 아래에 설정합니다.
| Key | Default | Description |
|---|---|---|
bot_id | — | WeCom AI Bot ID(required) |
secret | — | WeCom AI Bot Secret(required) |
websocket_url | wss://openws.work.weixin.qq.com | WebSocket gateway URL |
dm_policy | open | DM access: open, allowlist, disabled, pairing |
group_policy | open | Group access: open, allowlist, disabled |
allow_from | [] | DM 허용 user ID(dm_policy=allowlist일 때) |
group_allow_from | [] | 허용 group ID(group_policy=allowlist일 때) |
groups | {} | group별 configuration(아래 참고) |
access policies
DM policy
bot에게 direct message를 보낼 수 있는 사람을 제어합니다.
| Value | Behavior |
|---|---|
open | 누구나 bot에게 DM 가능(기본값) |
allowlist | allow_from에 있는 user ID만 DM 가능 |
disabled | 모든 DM 무시 |
pairing | initial setup용 pairing mode |
WECOM_DM_POLICY=allowlist
group policy
bot이 어느 group에서 respond할지 제어합니다.
| Value | Behavior |
|---|---|
open | 모든 group에서 respond(기본값) |
allowlist | group_allow_from에 listed된 group ID에서만 respond |
disabled | 모든 group message 무시 |
WECOM_GROUP_POLICY=allowlist
per-group sender allowlists
세밀한 제어를 위해 특정 group 안에서 어떤 user가 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이 group 자체가 허용되는지 결정합니다.- group이 top-level check를 통과하면,
groups.<group_id>.allow_fromlist가 있을 때 그 group 안에서 상호작용할 수 있는 sender를 추가로 제한합니다. - wildcard
"*"group entry는 명시적으로 나열되지 않은 group의 default 역할을 합니다. - allowlist entry는 모든 user를 허용하는
*wildcard를 지원하며, case-insensitive입니다. - entry는 선택적으로
wecom:user:또는wecom:group:prefix format을 사용할 수 있습니다. prefix는 자동으로 제거됩니다.
group에 allow_from이 설정되어 있지 않으면, group 자체가 top-level policy check를 통과했다는 전제에서 해당 group의 모든 user가 허용됩니다.
media support
inbound(receiving)
adapter는 user의 media attachment를 받아 agent processing을 위해 local에 cache합니다.
| Type | 처리 방식 |
|---|---|
| Images | download 후 local cache. URL-based 및 base64-encoded image 모두 지원 |
| Files | download 후 cache. original message의 filename 보존 |
| Voice | 사용 가능한 경우 voice message text transcription 추출 |
| Mixed messages | WeCom mixed-type message(text + images)를 parse하고 모든 component 추출 |
Quoted messages: quoted(replied-to) message의 media도 추출되므로 agent는 사용자가 무엇에 reply하는지 context를 갖습니다.
AES-encrypted media decryption
WeCom은 일부 inbound media attachment를 AES-256-CBC로 encrypt합니다. adapter가 이를 자동 처리합니다.
- inbound media item에
aeskeyfield가 있으면 adapter가 encrypted bytes를 download하고 AES-256-CBC + PKCS#7 padding으로 decrypt합니다. - AES key는
aeskeyfield를 base64-decode한 값입니다. 정확히 32 bytes여야 합니다. - IV는 key의 첫 16 bytes에서 파생됩니다.
cryptographyPython package가 필요합니다. (pip install cryptography)
별도 configuration은 필요 없습니다. encrypted media가 오면 transparent하게 decrypt됩니다.
outbound(sending)
| Method | What it sends | Size limit |
|---|---|---|
send | Markdown text messages | 4000 chars |
send_image / send_image_file | native image messages | 10 MB |
send_document | file attachments | 20 MB |
send_voice | voice messages(native voice는 AMR format only) | 2 MB |
send_video | video messages | 10 MB |
Chunked upload: file은 512KB chunk로 3-step protocol(init → chunks → finish)을 통해 upload됩니다. adapter가 자동으로 처리합니다.
Automatic downgrade: media가 native type size limit을 넘지만 absolute 20MB file limit 아래라면 generic file attachment로 자동 전송됩니다.
- Images > 10 MB → file로 전송
- Videos > 10 MB → file로 전송
- Voice > 2 MB → file로 전송
- Non-AMR audio → file로 전송(WeCom native voice는 AMR만 지원)
absolute 20MB limit을 넘는 file은 거부되며 chat에 안내 message가 전송됩니다.
reply-mode stream responses
bot이 WeCom callback으로 message를 받으면 adapter는 inbound request ID를 기억합니다. request context가 아직 active인 동안 response가 전송되면 adapter는 WeCom reply-mode(aibot_respond_msg)와 streaming을 사용해 response를 inbound message에 직접 correlate합니다. WeCom client에서 더 자연스러운 대화 경험을 제공합니다.
inbound request context가 만료되었거나 사용할 수 없으면 adapter는 aibot_send_msg를 통한 proactive message sending으로 fallback합니다.
reply-mode는 media에도 동작합니다. uploaded media를 originating message에 대한 reply로 보낼 수 있습니다.
connection and reconnection
adapter는 WeCom gateway wss://openws.work.weixin.qq.com에 persistent WebSocket connection을 유지합니다.
connection lifecycle
- Connect: WebSocket connection을 열고 bot_id와 secret이 담긴
aibot_subscribeauthentication frame을 보냅니다. - Heartbeat: connection 유지를 위해 30초마다 application-level ping frame을 보냅니다.
- Listen: inbound frame을 계속 읽고 message callback을 dispatch합니다.
reconnection behavior
connection loss가 발생하면 adapter는 exponential backoff로 reconnect합니다.
| Attempt | Delay |
|---|---|
| 1st retry | 2 seconds |
| 2nd retry | 5 seconds |
| 3rd retry | 10 seconds |
| 4th retry | 30 seconds |
| 5th+ retry | 60 seconds |
reconnection이 성공할 때마다 backoff counter는 0으로 reset됩니다. disconnect 시 pending request future는 모두 fail 처리되어 caller가 무기한 hang되지 않습니다.
deduplication
inbound message는 message ID로 deduplicate됩니다. window는 5분이고 cache 최대 크기는 1000 entries입니다. reconnection이나 network hiccup 중 message가 두 번 처리되는 것을 막습니다.
all environment variables
| Variable | Required | Default | Description |
|---|---|---|---|
WECOM_BOT_ID | ✅ | — | WeCom AI Bot ID |
WECOM_SECRET | ✅ | — | WeCom AI Bot Secret |
WECOM_ALLOWED_USERS | — | (empty) | gateway-level allowlist용 comma-separated user IDs |
WECOM_HOME_CHANNEL | — | — | cron/notification output용 Chat ID |
WECOM_WEBSOCKET_URL | — | wss://openws.work.weixin.qq.com | WebSocket gateway URL |
WECOM_DM_POLICY | — | open | DM access policy |
WECOM_GROUP_POLICY | — | open | group access policy |
troubleshooting
| Problem | Fix |
|---|---|
WECOM_BOT_ID and WECOM_SECRET are required | env var 둘 다 설정하거나 setup wizard에서 configure |
WeCom startup failed: aiohttp not installed | aiohttp 설치: pip install aiohttp |
WeCom startup failed: httpx not installed | httpx 설치: pip install httpx |
invalid secret (errcode=40013) | secret이 bot credential과 일치하는지 확인 |
Timed out waiting for subscribe acknowledgement | openws.work.weixin.qq.com network connectivity 확인 |
| Bot doesn't respond in groups | group_policy setting과 group ID가 group_allow_from에 있는지 확인 |
| Bot ignores certain users in a group | groups config section의 per-group allow_from list 확인 |
| Media decryption fails | cryptography 설치: pip install cryptography |
cryptography is required for WeCom media decryption | inbound media가 AES-encrypted입니다. 설치: pip install cryptography |
| Voice messages sent as files | WeCom native voice는 AMR format만 지원합니다. 다른 format은 file로 auto-downgrade됩니다. |
File too large error | WeCom은 모든 file upload에 20MB absolute limit이 있습니다. file을 compress하거나 split하세요. |
| Images sent as files | 10MB 초과 image는 native image limit을 넘어 file attachment로 auto-downgrade됩니다. |
Timeout sending message to WeCom | WebSocket이 disconnect되었을 수 있습니다. reconnection log를 확인하세요. |
WeCom websocket closed during authentication | network issue 또는 잘못된 credential입니다. bot_id와 secret을 확인하세요. |