hermes - 💡(How to fix) Fix [i18n] Thai Translation: Messaging Part e - webhooks, wecom, wecom-callback, weixin, whatsapp [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#15024Fetched 2026-04-25 06:24:59
View on GitHub
Comments
0
Participants
1
Timeline
5
Reactions
0
Author
Participants
Timeline (top)
labeled ×5

Error Message

  • Keys ที่ขาดหายไปจะถูกทิ้งไว้เป็น string literal {key} (ไม่มี error) | File too large error | WeCom มีขีดจำกัดสูงสุด 20 MB สำหรับการอัปโหลดไฟล์ทั้งหมด. ให้บีบอัดหรือแบ่งไฟล์ | | Transient error (1st–2nd) | Retry after 2 seconds | มีเพียง Weixin gateway instance เดียวเท่านั้นที่สามารถใช้ token ที่กำหนดได้ในเวลาใดเวลาหนึ่ง อะแดปเตอร์จะเข้าถึง scoped lock เมื่อเริ่มต้นและปล่อยเมื่อปิด หาก gateway อื่นกำลังใช้ token เดียวกันอยู่ การเริ่มต้นจะล้มเหลวพร้อมข้อความ error ที่ให้ข้อมูล

Fix Action

Fix / Workaround

  1. Connect: ตรวจสอบ credentials และเริ่ม poll loop
  2. Poll: เรียก getupdates ด้วย timeout 35 วินาที; server จะคงคำขอไว้จนกว่าข้อความจะมาถึงหรือหมดเวลา
  3. Dispatch: ข้อความขาเข้าจะถูก dispatch แบบ concurrent ผ่าน asyncio.create_task
  4. Sync buffer: sync cursor แบบถาวร (get_updates_buf) จะถูกบันทึกใน disk เพื่อให้อะแดปเตอร์สามารถดำเนินการต่อจากตำแหน่งที่ถูกต้องหลังจากการรีสตาร์ท

Code Example

hermes gateway setup

---

WEBHOOK_ENABLED=true
WEBHOOK_PORT=8644        # default
WEBHOOK_SECRET=your-global-secret

---

curl http://localhost:8644/health

---

{"status": "ok", "platform": "webhook"}

---

platforms:
  webhook:
    enabled: true
    extra:
      port: 8644
      secret: "global-fallback-secret"
      routes:
        github-pr:
          events: ["pull_request"]
          secret: "github-webhook-secret"
          prompt: |
            Review this pull request:
            Repository: {repository.full_name}
            PR #{number}: {pull_request.title}
            Author: {pull_request.user.login}
            URL: {pull_request.html_url}
            Diff URL: {pull_request.diff_url}
            Action: {action}
          skills: ["github-code-review"]
          deliver: "github_comment"
          deliver_extra:
            repo: "{repository.full_name}"
            pr_number: "{number}"
        deploy-notify:
          events: ["push"]
          secret: "deploy-secret"
          prompt: "New push to {repository.full_name} branch {ref}: {head_commit.message}"
          deliver: "telegram"

---

prompt: "PR #{pull_request.number} by {pull_request.user.login}: {__raw__}"

---

webhooks:
  routes:
    alerts:
      events: ["alert"]
      prompt: "Alert: {__raw__}"
      deliver: "telegram"
      deliver_extra:
        chat_id: "-1001234567890"
        message_thread_id: "42"

---

gh auth login

---

platforms:
  webhook:
    enabled: true
    extra:
      routes:
        gitlab-mr:
          events: ["merge_request"]
          secret: "your-gitlab-secret-token"
          prompt: |
            Review this merge request:
            Project: {project.path_with_namespace}
            MR !{object_attributes.iid}: {object_attributes.title}
            Author: {object_attributes.last_commit.author.name}
            URL: {object_attributes.url}
            Action: {object_attributes.action}
          deliver: "log"

---

platforms:
  webhook:
    enabled: true
    extra:
      port: 8644
      secret: "global-secret"
      routes:
        antenna-matches:
          secret: "antenna-webhook-secret"
          deliver: "telegram"
          deliver_only: true
          prompt: "🎉 New match: {match.user_name} matched with you!"
          deliver_extra:
            chat_id: "{match.telegram_chat_id}"

---

hermes webhook subscribe antenna-matches \
  --deliver telegram \
  --deliver-chat-id "123456789" \
  --deliver-only \
  --prompt "🎉 New match: {match.user_name} matched with you!" \
  --description "Antenna match notifications"

---

hermes webhook subscribe github-issues \
  --events "issues" \
  --prompt "New issue #{issue.number}: {issue.title}\nBy: {issue.user.login}\n\n{issue.body}" \
  --deliver telegram \
  --deliver-chat-id "-100123456789" \
  --description "Triage new GitHub issues"

---

hermes webhook list

---

hermes webhook remove github-issues

---

hermes webhook test github-issues
hermes webhook test github-issues --payload '{"issue": {"number": 42, "title": "Test"}}'

---

platforms:
  webhook:
    extra:
      rate_limit: 60  # requests per minute

---

platforms:
  webhook:
    extra:
      max_body_bytes: 2097152  # 2 MB

---

hermes gateway setup

---

hermes gateway setup

---

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

---

hermes gateway

---

WECOM_DM_POLICY=allowlist

---

WECOM_GROUP_POLICY=allowlist

---

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"

---

WECOM_CALLBACK_CORP_ID=your-corp-id
WECOM_CALLBACK_CORP_SECRET=your-corp-secret
WECOM_CALLBACK_AGENT_ID=1000002
WECOM_CALLBACK_TOKEN=your-callback-token
WECOM_CALLBACK_ENCODING_AES_KEY=your-43-char-aes-key

# Optional
WECOM_CALLBACK_HOST=0.0.0.0
WECOM_CALLBACK_PORT=8645
WECOM_CALLBACK_ALLOWED_USERS=user1,user2

---

hermes gateway start

---

platforms:
  wecom_callback:
    enabled: true
    extra:
      host: "0.0.0.0"
      port: 8645
      apps:
        - name: "dept-a"
          corp_id: "ww_corp_a"
          corp_secret: "secret-a"
          agent_id: "1000002"
          token: "token-a"
          encoding_aes_key: "key-a-43-chars..."
        - name: "dept-b"
          corp_id: "ww_corp_b"
          corp_secret: "secret-b"
          agent_id: "1000003"
          token: "token-b"
          encoding_aes_key: "key-b-43-chars..."

---

# อนุญาตเฉพาะผู้ใช้ที่ระบุ
WECOM_CALLBACK_ALLOWED_USERS=zhangsan,lisi,wangwu

# หรืออนุญาตผู้ใช้ทั้งหมด
WECOM_CALLBACK_ALLOW_ALL_USERS=true

---

pip install aiohttp cryptography
# Optional: for terminal QR code display
pip install hermes-agent[messaging]

---

hermes gateway setup

---

微信连接成功,account_id=your-account-id

---

WEIXIN_ACCOUNT_ID=your-account-id

# Optional: override the token (normally auto-saved from QR login)
# WEIXIN_TOKEN=your-bot-token

# Optional: restrict access
WEIXIN_DM_POLICY=open
WEIXIN_ALLOWED_USERS=user_id_1,user_id_2

# Optional: restore legacy multiline splitting behavior
# WEIXIN_SPLIT_MULTILINE_MESSAGES=true

# Optional: home channel for cron/notifications
WEIXIN_HOME_CHANNEL=chat_id
WEIXIN_HOME_CHANNEL_NAME=Home

---

hermes gateway

---

WEIXIN_DM_POLICY=allowlist
WEIXIN_ALLOWED_USERS=user_id_1,user_id_2

---

WEIXIN_GROUP_POLICY=allowlist
WEIXIN_GROUP_ALLOWED_USERS=group_id_1,group_id_2

---

hermes whatsapp

---

# Required
WHATSAPP_ENABLED=true
WHATSAPP_MODE=bot                          # "bot" หรือ "self-chat"

# การควบคุมการเข้าถึง - เลือกตัวเลือกใดตัวเลือกหนึ่ง:
WHATSAPP_ALLOWED_USERS=15551234567         # หมายเลขโทรศัพท์ที่คั่นด้วยเครื่องหมายจุลภาค (พร้อมรหัสประเทศ, ห้ามมี +)
# WHATSAPP_ALLOWED_USERS=*                 # หรือใช้ * เพื่ออนุญาตทุกคน
# WHATSAPP_ALLOW_ALL_USERS=true            # หรือตั้งค่า flag นี้แทน (ผลลัพธ์เหมือนกับ *)

---

unauthorized_dm_behavior: pair

whatsapp:
  unauthorized_dm_behavior: ignore

---

hermes gateway              # Foreground
hermes gateway install      # ติดตั้งเป็น user service
sudo hermes gateway install --system   # สำหรับ Linux เท่านั้น: system service ที่บูตเครื่อง

---

hermes whatsapp

---

# ~/.hermes/config.yaml
whatsapp:
  reply_prefix: ""                          # ข้อความว่างเปล่าจะปิด header
  # reply_prefix: "🤖 *My Bot*\n──────\n"  # Custom prefix (รองรับ \n สำหรับการขึ้นบรรทัดใหม่)

---

whatsapp:
  unauthorized_dm_behavior: ignore
RAW_BUFFERClick to expand / collapse

📄 user-guide/messaging/webhooks.md


sidebar_position: 13 title: "Webhooks" description: "Receive events from GitHub, GitLab, and other services to trigger Hermes agent runs"

Webhooks

รับ event จากบริการภายนอก (เช่น GitHub, GitLab, JIRA, Stripe, เป็นต้น) และเรียกใช้ Hermes agent runs โดยอัตโนมัติ Webhook adapter ทำงานโดยการรัน HTTP server ที่รับ POST requests, ตรวจสอบ HMAC signatures, แปลง payloads ให้เป็น agent prompts, และส่งต่อ responses กลับไปยังแหล่งที่มาหรือไปยังแพลตฟอร์มอื่นที่กำหนดค่าไว้

agent จะประมวลผล event และสามารถตอบกลับได้โดยการโพสต์ comment บน PRs, ส่งข้อความไปยัง Telegram/Discord, หรือบันทึกผลลัพธ์


Quick Start

  1. เปิดใช้งานผ่าน hermes gateway setup หรือ environment variables
  2. กำหนด routes ใน config.yaml หรือ สร้างแบบไดนามิกด้วย hermes webhook subscribe
  3. ชี้บริการของคุณไปยัง http://your-server:8644/webhooks/<route-name>

Setup

มีสองวิธีในการเปิดใช้งาน webhook adapter

ผ่าน setup wizard

hermes gateway setup

ทำตามคำแนะนำเพื่อเปิดใช้งาน webhooks, กำหนด port, และกำหนด global HMAC secret

ผ่าน environment variables

เพิ่มใน ~/.hermes/.env:

WEBHOOK_ENABLED=true
WEBHOOK_PORT=8644        # default
WEBHOOK_SECRET=your-global-secret

ตรวจสอบ server

เมื่อ gateway ทำงานแล้ว:

curl http://localhost:8644/health

Expected response:

{"status": "ok", "platform": "webhook"}

Configuring Routes {#configuring-routes}

Routes กำหนดวิธีการจัดการ webhook sources ที่แตกต่างกัน แต่ละ route คือ entry ที่มีชื่อภายใต้ platforms.webhook.extra.routes ในไฟล์ config.yaml ของคุณ

Route properties

PropertyRequiredDescription
eventsNoรายการประเภท event ที่ต้องการรับ (เช่น ["pull_request"]) หากว่างเปล่า จะรับทุก event type event type จะถูกอ่านจาก X-GitHub-Event, X-GitLab-Event, หรือ event_type ใน payload
secretYesHMAC secret สำหรับการตรวจสอบ signature จะใช้ global secret เป็นค่า fallback หากไม่ได้กำหนดบน route นั้นๆ กำหนดเป็น "INSECURE_NO_AUTH" สำหรับการทดสอบเท่านั้น (ข้ามการตรวจสอบ)
promptNoTemplate string ที่สามารถเข้าถึง payload ด้วย dot-notation (เช่น {pull_request.title}) หากละเว้น จะ dump full JSON payload เข้าไปใน prompt
skillsNoรายการชื่อ skill ที่จะโหลดสำหรับการรัน agent
deliverNoที่ที่จะส่ง response: github_comment, telegram, discord, slack, signal, sms, whatsapp, matrix, mattermost, homeassistant, email, dingtalk, feishu, wecom, weixin, bluebubbles, qqbot, หรือ log (default)
deliver_extraNoการตั้งค่าการส่งมอบเพิ่มเติม - keys ขึ้นอยู่กับ deliver type (เช่น repo, pr_number, chat_id) ค่าต่างๆ รองรับ template แบบ {dot.notation} เช่นเดียวกับ prompt
deliver_onlyNoหากเป็น true จะข้าม agent ไปโดยสิ้นเชิง - template prompt ที่ถูก render จะกลายเป็นข้อความ literal ที่ถูกส่งออกไป ประหยัดค่า LLM tokens และส่งมอบได้ในระดับ sub-second ดู Direct Delivery Mode สำหรับ use cases

Full example

platforms:
  webhook:
    enabled: true
    extra:
      port: 8644
      secret: "global-fallback-secret"
      routes:
        github-pr:
          events: ["pull_request"]
          secret: "github-webhook-secret"
          prompt: |
            Review this pull request:
            Repository: {repository.full_name}
            PR #{number}: {pull_request.title}
            Author: {pull_request.user.login}
            URL: {pull_request.html_url}
            Diff URL: {pull_request.diff_url}
            Action: {action}
          skills: ["github-code-review"]
          deliver: "github_comment"
          deliver_extra:
            repo: "{repository.full_name}"
            pr_number: "{number}"
        deploy-notify:
          events: ["push"]
          secret: "deploy-secret"
          prompt: "New push to {repository.full_name} branch {ref}: {head_commit.message}"
          deliver: "telegram"

Prompt Templates

Prompts ใช้ dot-notation เพื่อเข้าถึง fields ซ้อนกันใน webhook payload:

  • {pull_request.title} resolve เป็น payload["pull_request"]["title"]
  • {repository.full_name} resolve เป็น payload["repository"]["full_name"]
  • {__raw__} - token พิเศษที่ dump entire payload ทั้งหมดเป็น JSON แบบ indented (ถูกตัดที่ 4000 characters) มีประโยชน์สำหรับการแจ้งเตือนแบบ monitoring หรือ webhooks ทั่วไปที่ agent ต้องการ full context
  • Keys ที่ขาดหายไปจะถูกทิ้งไว้เป็น string literal {key} (ไม่มี error)
  • Nested dicts และ lists จะถูก JSON-serialized และถูกตัดที่ 2000 characters

คุณสามารถผสม {__raw__} เข้ากับตัวแปร template ทั่วไปได้:

prompt: "PR #{pull_request.number} by {pull_request.user.login}: {__raw__}"

หากไม่มีการกำหนด prompt template สำหรับ route ใดๆ payload ทั้งหมดจะถูก dump เป็น JSON แบบ indented (ถูกตัดที่ 4000 characters)

template แบบ dot-notation เดียวกันสามารถใช้ได้ในค่า deliver_extra

Forum Topic Delivery

เมื่อส่ง webhook responses ไปยัง Telegram คุณสามารถกำหนดเป้าหมายไปยัง forum topic เฉพาะได้โดยการรวม message_thread_id (หรือ thread_id) ใน deliver_extra:

webhooks:
  routes:
    alerts:
      events: ["alert"]
      prompt: "Alert: {__raw__}"
      deliver: "telegram"
      deliver_extra:
        chat_id: "-1001234567890"
        message_thread_id: "42"

หากไม่ได้ให้ chat_id ใน deliver_extra การส่งมอบจะ fallback ไปยัง home channel ที่กำหนดค่าไว้สำหรับ target platform


GitHub PR Review (Step by Step) {#github-pr-review}

คู่มือนี้ตั้งค่าการตรวจสอบโค้ดอัตโนมัติสำหรับทุก pull request

1. สร้าง webhook ใน GitHub

  1. ไปที่ repository ของคุณ -> Settings -> Webhooks -> Add webhook
  2. ตั้งค่า Payload URL เป็น http://your-server:8644/webhooks/github-pr
  3. ตั้งค่า Content type เป็น application/json
  4. ตั้งค่า Secret ให้ตรงกับ route config ของคุณ (เช่น github-webhook-secret)
  5. ภายใต้ Which events?, เลือก Let me select individual events และเลือก Pull requests
  6. คลิก Add webhook

2. เพิ่ม route config

เพิ่ม route github-pr ในไฟล์ ~/.hermes/config.yaml ของคุณตามตัวอย่างข้างต้น

3. ตรวจสอบว่า gh CLI ได้รับการ authenticate

delivery type github_comment ใช้ GitHub CLI ในการโพสต์ comment:

gh auth login

4. ทดสอบ

เปิด pull request บน repository webhook จะทำงาน, Hermes จะประมวลผล event, และโพสต์ review comment บน PR


GitLab Webhook Setup {#gitlab-webhook-setup}

GitLab webhooks ทำงานคล้ายกัน แต่ใช้กลไกการตรวจสอบสิทธิ์ที่แตกต่างกัน GitLab จะส่ง secret เป็น header X-Gitlab-Token แบบ plain (ต้องตรงกับ string เป๊ะๆ ไม่ใช่ HMAC)

1. สร้าง webhook ใน GitLab

  1. ไปที่ project ของคุณ -> Settings -> Webhooks
  2. ตั้งค่า URL เป็น http://your-server:8644/webhooks/gitlab-mr
  3. ป้อน Secret token ของคุณ
  4. เลือก Merge request events (และ event อื่นๆ ที่คุณต้องการ)
  5. คลิก Add webhook

2. เพิ่ม route config

platforms:
  webhook:
    enabled: true
    extra:
      routes:
        gitlab-mr:
          events: ["merge_request"]
          secret: "your-gitlab-secret-token"
          prompt: |
            Review this merge request:
            Project: {project.path_with_namespace}
            MR !{object_attributes.iid}: {object_attributes.title}
            Author: {object_attributes.last_commit.author.name}
            URL: {object_attributes.url}
            Action: {object_attributes.action}
          deliver: "log"

Delivery Options {#delivery-options}

field deliver ควบคุมว่า response ของ agent จะไปที่ใดหลังจากประมวลผล webhook event

Deliver TypeDescription
logบันทึก response ไปยัง gateway log output นี่คือค่า default และมีประโยชน์สำหรับการทดสอบ
github_commentโพสต์ response เป็น PR/issue comment ผ่าน gh CLI ต้องใช้ deliver_extra.repo และ deliver_extra.pr_number ต้องติดตั้งและ authenticate gh CLI บน gateway host (gh auth login)
telegramส่ง response ไปยัง Telegram ใช้ home channel หรือระบุ chat_id ใน deliver_extra
discordส่ง response ไปยัง Discord ใช้ home channel หรือระบุ chat_id ใน deliver_extra
slackส่ง response ไปยัง Slack ใช้ home channel หรือระบุ chat_id ใน deliver_extra
signalส่ง response ไปยัง Signal ใช้ home channel หรือระบุ chat_id ใน deliver_extra
smsส่ง response ไปยัง SMS ผ่าน Twilio ใช้ home channel หรือระบุ chat_id ใน deliver_extra
whatsappส่ง response ไปยัง WhatsApp ใช้ home channel หรือระบุ chat_id ใน deliver_extra
matrixส่ง response ไปยัง Matrix ใช้ home channel หรือระบุ chat_id ใน deliver_extra
mattermostส่ง response ไปยัง Mattermost ใช้ home channel หรือระบุ chat_id ใน deliver_extra
homeassistantส่ง response ไปยัง Home Assistant ใช้ home channel หรือระบุ chat_id ใน deliver_extra
emailส่ง response ไปยัง Email ใช้ home channel หรือระบุ chat_id ใน deliver_extra
dingtalkส่ง response ไปยัง DingTalk ใช้ home channel หรือระบุ chat_id ใน deliver_extra
feishuส่ง response ไปยัง Feishu/Lark ใช้ home channel หรือระบุ chat_id ใน deliver_extra
wecomส่ง response ไปยัง WeCom ใช้ home channel หรือระบุ chat_id ใน deliver_extra
weixinส่ง response ไปยัง Weixin (WeChat) ใช้ home channel หรือระบุ chat_id ใน deliver_extra
bluebubblesส่ง response ไปยัง BlueBubbles (iMessage) ใช้ home channel หรือระบุ chat_id ใน deliver_extra

สำหรับการส่งมอบข้ามแพลตฟอร์ม target platform ต้องถูกเปิดใช้งานและเชื่อมต่อใน gateway หากไม่ได้ให้ chat_id ใน deliver_extra response จะถูกส่งไปยัง home channel ที่กำหนดค่าไว้สำหรับแพลตฟอร์มนั้นๆ


Direct Delivery Mode {#direct-delivery-mode}

โดยค่าเริ่มต้น ทุก POST webhook จะเรียกใช้ agent run - payload จะกลายเป็น prompt, agent จะประมวลผล, และ response ของ agent จะถูกส่งมอบ สิ่งนี้มีค่าใช้จ่าย LLM tokens ในทุก event

สำหรับ use cases ที่คุณต้องการเพียงแค่ push notification ธรรมดา - ไม่มี reasoning, ไม่มี agent loop, เพียงแค่ส่งมอบข้อความ - ให้ตั้งค่า deliver_only: true บน route template template prompt ที่ถูก render จะกลายเป็น message body literal และ adapter จะส่งมันโดยตรงไปยัง delivery target ที่กำหนดค่าไว้

When to use direct delivery

  • External service push - Supabase/Firebase webhook ทำงานเมื่อมีการเปลี่ยนแปลงฐานข้อมูล -> แจ้งเตือนผู้ใช้ใน Telegram ทันที
  • Monitoring alerts - Datadog/Grafana alert webhook -> push ไปยังช่อง Discord
  • Inter-agent pings - Agent A แจ้งเตือนผู้ใช้ของ Agent B ว่างานที่รันนานเสร็จแล้ว
  • Background job completion - Cron job เสร็จสิ้น -> โพสต์ผลลัพธ์ไปยัง Slack

Benefits:

  • Zero LLM tokens - agent จะไม่ถูกเรียกใช้เลย
  • Sub-second delivery - การเรียก adapter ครั้งเดียว, ไม่มี reasoning loop
  • Same security as agent mode - HMAC auth, rate limits, idempotency, และ body-size limits ยังคงใช้ได้ทั้งหมด
  • Synchronous response - POST จะคืนค่า 200 OK เมื่อการส่งมอบสำเร็จ หรือ 502 หาก target ปฏิเสธ เพื่อให้บริการต้นทางของคุณสามารถ retry ได้อย่างชาญฉลาด

Example: Telegram push from Supabase

platforms:
  webhook:
    enabled: true
    extra:
      port: 8644
      secret: "global-secret"
      routes:
        antenna-matches:
          secret: "antenna-webhook-secret"
          deliver: "telegram"
          deliver_only: true
          prompt: "🎉 New match: {match.user_name} matched with you!"
          deliver_extra:
            chat_id: "{match.telegram_chat_id}"

Supabase edge function จะ sign payload ด้วย HMAC-SHA256 และ POST ไปที่ https://your-server:8644/webhooks/antenna-matches webhook adapter จะตรวจสอบ signature, render template จาก payload, ส่งมอบไปยัง Telegram, และคืนค่า 200 OK

Example: Dynamic subscription via CLI

hermes webhook subscribe antenna-matches \
  --deliver telegram \
  --deliver-chat-id "123456789" \
  --deliver-only \
  --prompt "🎉 New match: {match.user_name} matched with you!" \
  --description "Antenna match notifications"

Response codes

StatusMeaning
200 OKส่งมอบสำเร็จ Body: {"status": "delivered", "route": "...", "target": "...", "delivery_id": "..."}
200 OK (status=duplicate)Duplicate X-GitHub-Delivery ID ภายในช่วงเวลา idempotency TTL (1 ชั่วโมง) ไม่มีการส่งมอบซ้ำ
401 UnauthorizedHMAC signature ไม่ถูกต้องหรือขาดหายไป
400 Bad RequestJSON body รูปแบบไม่ถูกต้อง
404 Not Foundชื่อ route ไม่รู้จัก
413 Payload Too LargeBody เกิน max_body_bytes
429 Too Many Requestsเกิน rate limit ของ route
502 Bad GatewayTarget adapter ปฏิเสธข้อความหรือเกิดข้อผิดพลาด ข้อผิดพลาดจะถูกบันทึกที่ฝั่ง server; response body จะเป็น Delivery failed ทั่วไปเพื่อหลีกเลี่ยงการเปิดเผย internal adapter details

Configuration gotchas

  • deliver_only: true ต้องใช้ deliver เป็น target จริง deliver: log (หรือละเว้น deliver) จะถูกปฏิเสธเมื่อเริ่มต้น - adapter จะปฏิเสธที่จะเริ่มทำงานหากพบ route ที่กำหนดค่าผิดพลาด
  • field skills จะถูกละเลยใน direct delivery mode (ไม่มีการรัน agent, ดังนั้นจึงไม่มีอะไรให้ inject skills)
  • การ render template ใช้ syntax {dot.notation} เดียวกันกับ agent mode รวมถึง token {__raw__}
  • Idempotency ใช้ header X-GitHub-Delivery / X-Request-ID เดียวกัน - การ retry ด้วย ID เดียวกันจะคืนค่า status=duplicate และจะไม่ส่งมอบซ้ำ

Dynamic Subscriptions (CLI) {#dynamic-subscriptions}

นอกจาก routes แบบ static ใน config.yaml แล้ว คุณยังสามารถสร้าง webhook subscriptions แบบไดนามิกโดยใช้คำสั่ง CLI hermes webhook นี้มีประโยชน์อย่างยิ่งเมื่อ agent เองจำเป็นต้องตั้งค่า triggers แบบ event-driven

Create a subscription

hermes webhook subscribe github-issues \
  --events "issues" \
  --prompt "New issue #{issue.number}: {issue.title}\nBy: {issue.user.login}\n\n{issue.body}" \
  --deliver telegram \
  --deliver-chat-id "-100123456789" \
  --description "Triage new GitHub issues"

สิ่งนี้จะคืนค่า webhook URL และ HMAC secret ที่สร้างขึ้นโดยอัตโนมัติ กำหนดค่าบริการของคุณให้ POST ไปยัง URL นั้น

List subscriptions

hermes webhook list

Remove a subscription

hermes webhook remove github-issues

Test a subscription

hermes webhook test github-issues
hermes webhook test github-issues --payload '{"issue": {"number": 42, "title": "Test"}}'

How dynamic subscriptions work

  • Subscriptions ถูกจัดเก็บใน ~/.hermes/webhook_subscriptions.json
  • webhook adapter จะ hot-reload ไฟล์นี้เมื่อมี request เข้ามาแต่ละครั้ง (mtime-gated, overhead น้อยมาก)
  • Static routes จาก config.yaml จะมีลำดับความสำคัญเหนือกว่า dynamic routes ที่มีชื่อเดียวกันเสมอ
  • Dynamic subscriptions ใช้รูปแบบและขีดความสามารถเดียวกับ static routes (events, prompt templates, skills, delivery)
  • ไม่จำเป็นต้อง restart gateway - subscribe และพร้อมใช้งานทันที

Agent-driven subscriptions

agent สามารถสร้าง subscriptions ผ่าน terminal tool เมื่อได้รับคำแนะนำจาก webhook-subscriptions skill ให้ขอ agent ว่า "set up a webhook for GitHub issues" และมันจะรันคำสั่ง hermes webhook subscribe ที่เหมาะสม


Security {#security}

webhook adapter มีหลายชั้นของความปลอดภัย:

HMAC signature validation

adapter ตรวจสอบ webhook signatures ที่เข้ามาโดยใช้ method ที่เหมาะสมสำหรับแต่ละ source:

  • GitHub: header X-Hub-Signature-256 - HMAC-SHA256 hex digest ที่ขึ้นต้นด้วย sha256=
  • GitLab: header X-Gitlab-Token - plain secret string match
  • Generic: header X-Webhook-Signature - raw HMAC-SHA256 hex digest

หากมีการกำหนด secret แต่ไม่มี header signature ที่รู้จัก ระบบจะปฏิเสธ request

Secret is required

ทุก route ต้องมี secret - ไม่ว่าจะกำหนดโดยตรงบน route หรือสืบทอดมาจาก global secret Routes ที่ไม่มี secret จะทำให้ adapter ล้มเหลวเมื่อเริ่มต้นทำงาน สำหรับการพัฒนา/ทดสอบเท่านั้น คุณสามารถตั้งค่า secret เป็น "INSECURE_NO_AUTH" เพื่อข้ามการตรวจสอบทั้งหมด

Rate limiting

แต่ละ route ถูกจำกัด rate limit ที่ 30 requests per minute โดยค่า default (fixed-window) กำหนดค่านี้ทั่วโลก:

platforms:
  webhook:
    extra:
      rate_limit: 60  # requests per minute

Requests ที่เกิน limit จะได้รับ response 429 Too Many Requests

Idempotency

Delivery IDs (จาก X-GitHub-Delivery, X-Request-ID, หรือ timestamp fallback) จะถูกแคชเป็นเวลา 1 hour การส่งมอบซ้ำ (เช่น webhook retries) จะถูกข้ามอย่างเงียบๆ ด้วย response 200 ป้องกันการรัน agent ซ้ำ

Body size limits

Payloads ที่เกิน 1 MB จะถูกปฏิเสธก่อนที่จะอ่าน body กำหนดค่านี้:

platforms:
  webhook:
    extra:
      max_body_bytes: 2097152  # 2 MB

Prompt injection risk

:::warning Webhook payloads มีข้อมูลที่ควบคุมโดยผู้โจมตี - PR titles, commit messages, issue descriptions, ฯลฯ อาจมีคำสั่งที่เป็นอันตรายได้ทั้งหมด ควรรัน gateway ในสภาพแวดล้อมที่ถูกจำกัด (Docker, VM) เมื่อเปิดเผยต่ออินเทอร์เน็ต พิจารณาใช้ Docker หรือ SSH terminal backend เพื่อการแยกส่วน :::


Troubleshooting {#troubleshooting}

Webhook not arriving

  • ตรวจสอบว่า port ถูกเปิดและเข้าถึงได้จาก webhook source
  • ตรวจสอบ firewall rules - port 8644 (หรือ port ที่กำหนดค่า) ต้องเปิดอยู่
  • ตรวจสอบว่า URL path ตรงกัน: http://your-server:8644/webhooks/<route-name>
  • ใช้ endpoint /health เพื่อยืนยันว่า server กำลังทำงาน

Signature validation failing

  • ตรวจสอบให้แน่ใจว่า secret ใน route config ตรงกับ secret ที่กำหนดค่าใน webhook source อย่างแน่นอน
  • สำหรับ GitHub, secret เป็นแบบ HMAC - ตรวจสอบ X-Hub-Signature-256
  • สำหรับ GitLab, secret เป็นแบบ plain token match - ตรวจสอบ X-Gitlab-Token
  • ตรวจสอบ gateway logs สำหรับคำเตือน Invalid signature

Event being ignored

  • ตรวจสอบว่า event type อยู่ในรายการ events ของ route คุณ
  • GitHub events ใช้ค่าเช่น pull_request, push, issues (ค่า header X-GitHub-Event)
  • GitLab events ใช้ค่าเช่น merge_request, push (ค่า header X-Gitlab-Event)
  • หาก events ว่างเปล่าหรือไม่ถูกตั้งค่า จะรับทุก event

Agent not responding

  • รัน gateway ใน foreground เพื่อดู logs: hermes gateway run
  • ตรวจสอบว่า prompt template render อย่างถูกต้อง
  • ตรวจสอบว่า delivery target ถูกกำหนดค่าและเชื่อมต่อแล้ว

Duplicate responses

  • Idempotency cache ควรป้องกันสิ่งนี้ - ตรวจสอบว่า webhook source ส่ง delivery ID header (X-GitHub-Delivery หรือ X-Request-ID)
  • Delivery IDs ถูกแคชเป็นเวลา 1 hour

gh CLI errors (GitHub comment delivery)

  • รัน gh auth login บน gateway host
  • ตรวจสอบว่า GitHub user ที่ authenticate มี write access ถึง repository
  • ตรวจสอบว่า gh ถูกติดตั้งและอยู่ใน PATH

Environment Variables {#environment-variables}

VariableDescriptionDefault
WEBHOOK_ENABLEDเปิดใช้งาน webhook platform adapterfalse
WEBHOOK_PORTHTTP server port สำหรับรับ webhooks8644
WEBHOOK_SECRETGlobal HMAC secret (ใช้เป็น fallback เมื่อ route ไม่ได้ระบุของตัวเอง)(none)

📄 user-guide/messaging/wecom.md


sidebar_position: 14 title: "WeCom (Enterprise WeChat)" description: "Connect Hermes Agent to WeCom via the AI Bot WebSocket gateway"

WeCom (Enterprise WeChat)

เชื่อมต่อ Hermes เข้ากับ WeCom (企业微信) ซึ่งเป็นแพลตฟอร์มการสื่อสารองค์กรของ Tencent อะแดปเตอร์นี้ใช้ WeCom's AI Bot WebSocket gateway สำหรับการสื่อสารแบบสองทิศทางแบบเรียลไทม์ ไม่จำเป็นต้องมี public endpoint หรือ webhook ใดๆ

Prerequisites

  • บัญชีองค์กร WeCom
  • AI Bot ที่สร้างขึ้นใน WeCom Admin Console
  • Bot ID และ Secret จากหน้าข้อมูลประจำตัวของบอท
  • Python packages: aiohttp และ httpx

Setup

Step 1: Create an AI Bot

Recommended: Scan-to-Create (one command)

hermes gateway setup

เลือก WeCom และสแกน QR code ด้วยแอป WeCom บนมือถือของคุณ Hermes จะสร้างแอปพลิเคชันบอทโดยอัตโนมัติพร้อมสิทธิ์ที่ถูกต้องและบันทึก credentials ให้

Setup wizard จะ:

  1. แสดง QR code ใน terminal ของคุณ
  2. รอให้คุณสแกนด้วยแอป WeCom บนมือถือ
  3. ดึง Bot ID และ Secret โดยอัตโนมัติ
  4. แนะนำคุณตลอดการตั้งค่า access control

Alternative: Manual Setup

หากไม่สามารถใช้ scan-to-create ได้ wizard จะกลับไปใช้วิธีป้อนข้อมูลด้วยตนเอง:

  1. เข้าสู่ระบบ WeCom Admin Console
  2. ไปที่ ApplicationsCreate ApplicationAI Bot
  3. กำหนดชื่อและคำอธิบายของบอท
  4. คัดลอก Bot ID และ Secret จากหน้า credentials
  5. รัน hermes gateway setup, เลือก WeCom, และป้อน credentials เมื่อระบบแจ้ง

:::warning เก็บ Bot Secret เป็นความลับ ใครก็ตามที่มีสิ่งนี้สามารถปลอมตัวเป็นบอทของคุณได้ :::

Step 2: Configure Hermes

Option A: Interactive Setup (Recommended)

hermes gateway setup

เลือก WeCom และทำตามคำแนะนำ wizard จะแนะนำคุณตลอดการตั้งค่า:

  • Bot credentials (ผ่านการสแกน QR หรือการป้อนข้อมูลด้วยตนเอง)
  • Access control settings (allowlist, pairing mode, หรือ open access)
  • 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: Start the gateway

hermes gateway

Features

  • WebSocket transport - การเชื่อมต่อแบบ persistent ไม่จำเป็นต้องมี public endpoint
  • DM and group messaging - นโยบายการเข้าถึงที่กำหนดค่าได้
  • Per-group sender allowlists - การควบคุมระดับละเอียดว่าใครสามารถโต้ตอบในแต่ละกลุ่มได้
  • Media support - การอัปโหลดและดาวน์โหลดรูปภาพ ไฟล์ เสียง และวิดีโอ
  • AES-encrypted media - การถอดรหัสอัตโนมัติสำหรับไฟล์แนบขาเข้า
  • Quote context - รักษาการสนทนาแบบ thread เมื่อมีการตอบกลับ
  • Markdown rendering - การตอบกลับแบบ rich text
  • Reply-mode streaming - เชื่อมโยงการตอบกลับกับบริบทข้อความขาเข้า
  • Auto-reconnect - การหน่วงเวลาแบบ exponential backoff เมื่อการเชื่อมต่อหลุด

Configuration Options

กำหนดค่าเหล่านี้ใน config.yaml ภายใต้ platforms.wecom.extra:

KeyDefaultDescription
bot_idWeCom AI Bot ID (required)
secretWeCom AI Bot Secret (required)
websocket_urlwss://openws.work.weixin.qq.comURL ของ WebSocket gateway
dm_policyopenการเข้าถึง DM: open, allowlist, disabled, pairing
group_policyopenการเข้าถึงกลุ่ม: open, allowlist, disabled
allow_from[]User IDs ที่ได้รับอนุญาตสำหรับ DM (เมื่อ dm_policy=allowlist)
group_allow_from[]Group IDs ที่ได้รับอนุญาต (เมื่อ group_policy=allowlist)
groups{}การกำหนดค่าต่อกลุ่ม (ดูด้านล่าง)

Access Policies

DM Policy

ควบคุมว่าใครสามารถส่งข้อความส่วนตัว (DM) ถึงบอทได้:

ValueBehavior
openทุกคนสามารถ DM บอทได้ (ค่าเริ่มต้น)
allowlistเฉพาะ user IDs ใน allow_from เท่านั้นที่สามารถ DM ได้
disabledเพิกเฉยต่อ DM ทั้งหมด
pairingโหมดจับคู่ (สำหรับการตั้งค่าเริ่มต้น)
WECOM_DM_POLICY=allowlist

Group Policy

ควบคุมว่าบอทจะตอบกลับในกลุ่มใด:

ValueBehavior
openบอทตอบกลับในทุกกลุ่ม (ค่าเริ่มต้น)
allowlistบอทจะตอบกลับเฉพาะ group IDs ที่ระบุใน group_allow_from
disabledเพิกเฉยต่อข้อความกลุ่มทั้งหมด
WECOM_GROUP_POLICY=allowlist

Per-Group Sender Allowlists

สำหรับการควบคุมระดับละเอียด คุณสามารถจำกัดผู้ใช้ที่ได้รับอนุญาตให้โต้ตอบกับบอทภายในกลุ่มเฉพาะได้ สิ่งนี้กำหนดค่าใน 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"

How it works:

  1. group_policy และ group_allow_from ควบคุมว่ากลุ่มนั้นได้รับอนุญาตหรือไม่
  2. หากกลุ่มผ่านการตรวจสอบระดับบนสุดแล้ว รายการ groups.<group_id>.allow_from (ถ้ามี) จะจำกัดผู้ส่งภายในกลุ่มนั้นๆ ที่สามารถโต้ตอบกับบอทได้เพิ่มเติม
  3. การระบุกลุ่มแบบ wildcard "*" ทำหน้าที่เป็นค่าเริ่มต้นสำหรับกลุ่มที่ไม่ได้ระบุอย่างชัดเจน
  4. รายการ allowlist รองรับ wildcard * เพื่ออนุญาตผู้ใช้ทั้งหมด และรายการเหล่านี้ไม่คำนึงถึงตัวพิมพ์เล็ก-ใหญ่ (case-insensitive)
  5. รายการสามารถใช้ prefix รูปแบบ wecom:user: หรือ wecom:group: ได้ตามความสมัครใจ - prefix จะถูกลบออกโดยอัตโนมัติ

หากไม่มีการกำหนด allow_from สำหรับกลุ่ม ผู้ใช้ทุกคนในกลุ่มนั้นจะได้รับอนุญาต (โดยสมมติว่าตัวกลุ่มเองผ่านการตรวจสอบนโยบายระดับบนสุดแล้ว)

Media Support

Inbound (receiving)

อะแดปเตอร์จะรับไฟล์แนบสื่อจากผู้ใช้และแคชไว้ในเครื่องสำหรับการประมวลผลของ agent:

TypeHow it's handled
Imagesดาวน์โหลดและแคชในเครื่อง รองรับทั้งรูปภาพที่อ้างอิงจาก URL และรูปภาพที่เข้ารหัส base64
Filesดาวน์โหลดและแคช ชื่อไฟล์จะถูกเก็บรักษาจากข้อความต้นฉบับ
Voiceจะมีการดึงข้อความถอดเสียงจากข้อความเสียงหากมีให้
Mixed messagesข้อความประเภทผสมของ WeCom (ข้อความ + รูปภาพ) จะถูกแยกวิเคราะห์และดึงส่วนประกอบทั้งหมดออกมา

Quoted messages: สื่อจากข้อความที่ถูกอ้างถึง (replied-to) จะถูกดึงออกมาด้วย ทำให้ agent มีบริบทว่าผู้ใช้กำลังตอบกลับอะไร

AES-Encrypted Media Decryption

WeCom จะเข้ารหัสไฟล์แนบสื่อขาเข้าบางส่วนด้วย AES-256-CBC อะแดปเตอร์จะจัดการสิ่งนี้โดยอัตโนมัติ:

  • เมื่อรายการสื่อขาเข้ามี field aeskey อะแดปเตอร์จะดาวน์โหลดไบต์ที่เข้ารหัสและถอดรหัสด้วย AES-256-CBC พร้อม PKCS#7 padding
  • AES key คือค่าที่ถอดรหัส base64 ของ field aeskey (ต้องมีขนาด 32 ไบต์พอดี)
  • IV จะถูกดึงมาจาก 16 ไบต์แรกของ key
  • สิ่งนี้ต้องใช้ Python package cryptography (pip install cryptography)

ไม่จำเป็นต้องมีการกำหนดค่า - การถอดรหัสจะเกิดขึ้นอย่างโปร่งใสเมื่อได้รับสื่อที่เข้ารหัส

Outbound (sending)

MethodWhat it sendsSize limit
sendข้อความข้อความ Markdown4000 chars
send_image / send_image_fileข้อความรูปภาพแบบ Native10 MB
send_documentไฟล์แนบ20 MB
send_voiceข้อความเสียง (รองรับเฉพาะรูปแบบ AMR สำหรับเสียง Native)2 MB
send_videoข้อความวิดีโอ10 MB

Chunked upload: ไฟล์จะถูกอัปโหลดเป็น chunks ขนาด 512 KB ผ่านโปรโตคอลสามขั้นตอน (init -> chunks -> finish) อะแดปเตอร์จัดการสิ่งนี้โดยอัตโนมัติ

Automatic downgrade: เมื่อสื่อมีขนาดเกินขีดจำกัดของประเภท Native แต่ยังอยู่ในขีดจำกัดไฟล์ 20 MB โดยรวม จะถูกส่งเป็นไฟล์แนบทั่วไปแทนโดยอัตโนมัติ:

  • Images > 10 MB → ส่งเป็นไฟล์
  • Videos > 10 MB → ส่งเป็นไฟล์
  • Voice > 2 MB → ส่งเป็นไฟล์
  • Non-AMR audio → ส่งเป็นไฟล์ (WeCom รองรับเฉพาะ AMR สำหรับเสียง Native)

ไฟล์ที่เกินขีดจำกัด 20 MB โดยรวมจะถูกปฏิเสธพร้อมข้อความแจ้งข้อมูลที่ส่งไปยังแชท

Reply-Mode Stream Responses

เมื่อบอทได้รับข้อความผ่าน WeCom callback อะแดปเตอร์จะจดจำ request ID ขาเข้า หากมีการส่งการตอบกลับในขณะที่บริบทของ request ยังทำงานอยู่ อะแดปเตอร์จะใช้ reply-mode ของ WeCom (aibot_respond_msg) พร้อม streaming เพื่อเชื่อมโยงการตอบกลับโดยตรงกับข้อความขาเข้า สิ่งนี้ให้ประสบการณ์การสนทนาที่เป็นธรรมชาติยิ่งขึ้นใน WeCom client

หากบริบทของ request ขาเข้าหมดอายุหรือไม่พร้อมใช้งาน อะแดปเตอร์จะย้อนกลับไปใช้การส่งข้อความเชิงรุกผ่าน aibot_send_msg

Reply-mode ยังใช้ได้กับสื่อ: สื่อที่อัปโหลดสามารถถูกส่งเป็น reply ไปยังข้อความต้นทางได้

Connection and Reconnection

อะแดปเตอร์จะรักษาการเชื่อมต่อ WebSocket แบบ persistent ไปยัง gateway ของ WeCom ที่ wss://openws.work.weixin.qq.com

Connection Lifecycle

  1. Connect: เปิดการเชื่อมต่อ WebSocket และส่ง frame การตรวจสอบสิทธิ์ aibot_subscribe พร้อม bot_id และ secret
  2. Heartbeat: ส่ง frame ping ระดับแอปพลิเคชันทุก 30 วินาทีเพื่อรักษาการเชื่อมต่อให้มีชีวิตอยู่
  3. Listen: อ่าน frame ขาเข้าอย่างต่อเนื่องและกระจาย message callbacks

Reconnection Behavior

เมื่อการเชื่อมต่อหลุด อะแดปเตอร์จะใช้ exponential backoff เพื่อเชื่อมต่อใหม่:

AttemptDelay
1st retry2 seconds
2nd retry5 seconds
3rd retry10 seconds
4th retry30 seconds
5th+ retry60 seconds

หลังจากการเชื่อมต่อใหม่ที่สำเร็จแต่ละครั้ง ตัวนับ backoff จะรีเซ็ตเป็นศูนย์ Future ของ request ที่ค้างอยู่ทั้งหมดจะล้มเหลวเมื่อ disconnect เพื่อไม่ให้ caller ค้างไปเรื่อยๆ

Deduplication

ข้อความขาเข้าจะถูกทำ deduplication โดยใช้ message IDs ภายในหน้าต่าง 5 นาที และแคชสูงสุด 1000 รายการ สิ่งนี้ป้องกันการประมวลผลข้อความซ้ำซ้อนระหว่างการเชื่อมต่อใหม่หรือปัญหาเครือข่าย

All Environment Variables

VariableRequiredDefaultDescription
WECOM_BOT_IDWeCom AI Bot ID
WECOM_SECRETWeCom AI Bot Secret
WECOM_ALLOWED_USERS(empty)User IDs ที่คั่นด้วยเครื่องหมายคอมม่าสำหรับ allowlist ระดับ gateway
WECOM_HOME_CHANNELChat ID สำหรับ output ของ cron/notification
WECOM_WEBSOCKET_URLwss://openws.work.weixin.qq.comURL ของ WebSocket gateway
WECOM_DM_POLICYopenนโยบายการเข้าถึง DM
WECOM_GROUP_POLICYopenนโยบายการเข้าถึงกลุ่ม

Troubleshooting

ProblemFix
WECOM_BOT_ID and WECOM_SECRET are requiredกำหนด env vars ทั้งคู่ หรือกำหนดค่าใน setup wizard
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 ตรงกับ credentials ของบอทหรือไม่
Timed out waiting for subscribe acknowledgementตรวจสอบการเชื่อมต่อเครือข่ายไปยัง openws.work.weixin.qq.com
Bot doesn't respond in groupsตรวจสอบการตั้งค่า group_policy และตรวจสอบให้แน่ใจว่า group ID อยู่ใน group_allow_from
Bot ignores certain users in a groupตรวจสอบรายการ allow_from ต่อกลุ่มในส่วน groups config section
Media decryption failsติดตั้ง cryptography: pip install cryptography
cryptography is required for WeCom media decryptionสื่อขาเข้าถูกเข้ารหัส AES. ติดตั้ง: pip install cryptography
Voice messages sent as filesWeCom รองรับเฉพาะรูปแบบ AMR สำหรับเสียง Native. รูปแบบอื่นจะถูกลดระดับเป็นไฟล์โดยอัตโนมัติ
File too large errorWeCom มีขีดจำกัดสูงสุด 20 MB สำหรับการอัปโหลดไฟล์ทั้งหมด. ให้บีบอัดหรือแบ่งไฟล์
Images sent as filesรูปภาพ > 10 MB เกินขีดจำกัดรูปภาพ Native และถูกลดระดับเป็นไฟล์แนบโดยอัตโนมัติ
Timeout sending message to WeComWebSocket อาจหลุด ตรวจสอบ logs สำหรับข้อความการเชื่อมต่อใหม่
WeCom websocket closed during authenticationปัญหาเครือข่ายหรือ credentials ไม่ถูกต้อง. ตรวจสอบ bot_id และ secret

📄 user-guide/messaging/wecom-callback.md


sidebar_position: 15

WeCom Callback (Self-Built App)

การเชื่อมต่อ Hermes เข้ากับ WeCom (Enterprise WeChat) ในรูปแบบแอปพลิเคชันที่สร้างเอง (self-built) สำหรับองค์กร โดยใช้โมเดล callback/webhook

:::info WeCom Bot vs WeCom Callback Hermes รองรับโหมดการเชื่อมต่อ WeCom สองโหมด:

  • WeCom Bot - รูปแบบบอท (bot-style) เชื่อมต่อผ่าน WebSocket มีการตั้งค่าที่ง่ายกว่า และใช้งานได้ในกลุ่มแชท
  • WeCom Callback (หน้านี้) - แอปพลิเคชันที่สร้างเอง (self-built app) รับ callback แบบ XML ที่เข้ารหัส จะแสดงเป็นแอปพลิเคชันหลักในแถบด้านข้างของ WeCom ของผู้ใช้ รองรับการกำหนดเส้นทาง (routing) ข้ามองค์กรหลายแห่ง :::

วิธีการทำงาน

  1. คุณต้องลงทะเบียนแอปพลิเคชันที่สร้างเองใน WeCom Admin Console
  2. WeCom จะส่งข้อมูล XML ที่เข้ารหัสไปยัง HTTP callback endpoint ของคุณ
  3. Hermes จะถอดรหัสข้อความและจัดคิวสำหรับ agent
  4. ตอบรับทันที (silent - ไม่แสดงอะไรแก่ผู้ใช้)
  5. agent จะดำเนินการตามคำขอ (โดยทั่วไปใช้เวลา 3-30 นาที)
  6. การตอบกลับจะถูกส่งออกไปเชิงรุก (proactively) ผ่าน WeCom message/send API

สิ่งที่ต้องเตรียม

  • บัญชีองค์กร WeCom ที่มีสิทธิ์ผู้ดูแลระบบ (admin access)
  • แพ็กเกจ Python aiohttp และ httpx (รวมอยู่ในการติดตั้งเริ่มต้น)
  • เซิร์ฟเวอร์ที่สามารถเข้าถึงได้จากสาธารณะสำหรับ URL callback (หรือใช้ tunnel เช่น ngrok)

การตั้งค่า

1. สร้างแอปพลิเคชันที่สร้างเองใน WeCom

  1. ไปที่ WeCom Admin ConsoleApplicationsCreate App
  2. จด Corp ID ของคุณ (แสดงที่ส่วนบนของ admin console)
  3. ในการตั้งค่าแอป ให้สร้าง Corp Secret
  4. จด Agent ID จากหน้าภาพรวมของแอป
  5. ภายใต้ Receive Messages ให้กำหนดค่า callback URL:
    • URL: http://YOUR_PUBLIC_IP:8645/wecom/callback
    • Token: สร้าง token แบบสุ่ม (WeCom จะให้มา)
    • EncodingAESKey: สร้าง key (WeCom จะให้มา)

2. กำหนดค่า Environment Variables

เพิ่มในไฟล์ .env ของคุณ:

WECOM_CALLBACK_CORP_ID=your-corp-id
WECOM_CALLBACK_CORP_SECRET=your-corp-secret
WECOM_CALLBACK_AGENT_ID=1000002
WECOM_CALLBACK_TOKEN=your-callback-token
WECOM_CALLBACK_ENCODING_AES_KEY=your-43-char-aes-key

# Optional
WECOM_CALLBACK_HOST=0.0.0.0
WECOM_CALLBACK_PORT=8645
WECOM_CALLBACK_ALLOWED_USERS=user1,user2

3. เริ่มต้น Gateway

hermes gateway start

callback adapter จะเริ่ม HTTP server บนพอร์ตที่กำหนด WeCom จะตรวจสอบ callback URL ผ่าน GET request จากนั้นจึงเริ่มส่งข้อความผ่าน POST

Reference การกำหนดค่า

กำหนดค่าเหล่านี้ใน config.yaml ภายใต้ platforms.wecom_callback.extra หรือใช้ environment variables:

SettingDefaultDescription
corp_id-WeCom enterprise Corp ID (required)
corp_secret-Corp secret สำหรับแอปที่สร้างเอง (required)
agent_id-Agent ID ของแอปที่สร้างเอง (required)
token-Callback verification token (required)
encoding_aes_key-43-character AES key สำหรับการเข้ารหัส callback (required)
host0.0.0.0Bind address สำหรับ HTTP callback server
port8645Port สำหรับ HTTP callback server
path/wecom/callbackURL path สำหรับ callback endpoint

Multi-App Routing

สำหรับองค์กรที่รันแอปที่สร้างเองหลายแอป (เช่น ในแผนกหรือบริษัทย่อยที่แตกต่างกัน) ให้กำหนดค่ารายการ apps ใน config.yaml:

platforms:
  wecom_callback:
    enabled: true
    extra:
      host: "0.0.0.0"
      port: 8645
      apps:
        - name: "dept-a"
          corp_id: "ww_corp_a"
          corp_secret: "secret-a"
          agent_id: "1000002"
          token: "token-a"
          encoding_aes_key: "key-a-43-chars..."
        - name: "dept-b"
          corp_id: "ww_corp_b"
          corp_secret: "secret-b"
          agent_id: "1000003"
          token: "token-b"
          encoding_aes_key: "key-b-43-chars..."

ผู้ใช้จะถูกจำกัดขอบเขตด้วย corp_id:user_id เพื่อป้องกันการชนกันข้ามองค์กร เมื่อผู้ใช้ส่งข้อความ adapter จะบันทึกว่าผู้ใช้คนนั้นสังกัดแอป (corp) ใด และจะกำหนดเส้นทางคำตอบกลับผ่าน access token ของแอปที่ถูกต้อง

Access Control

จำกัดว่าผู้ใช้คนใดสามารถโต้ตอบกับแอปได้:

# อนุญาตเฉพาะผู้ใช้ที่ระบุ
WECOM_CALLBACK_ALLOWED_USERS=zhangsan,lisi,wangwu

# หรืออนุญาตผู้ใช้ทั้งหมด
WECOM_CALLBACK_ALLOW_ALL_USERS=true

Endpoints

adapter เปิดเผย:

MethodPathPurpose
GET/wecom/callbackURL verification handshake (WeCom ส่งมาในช่วงตั้งค่า)
POST/wecom/callbackEncrypted message callback (WeCom ส่งข้อความผู้ใช้มาที่นี่)
GET/healthHealth check - คืนค่า {"status": "ok"}

Encryption

Payload callback ทั้งหมดจะถูกเข้ารหัสด้วย AES-CBC โดยใช้ EncodingAESKey adapter จะจัดการ:

  • Inbound: ถอดรหัส XML payload, ตรวจสอบลายเซ็น SHA1
  • Outbound: การตอบกลับที่ส่งผ่าน proactive API (ไม่ใช่การตอบกลับ callback ที่เข้ารหัส)

การใช้งาน crypto นี้เข้ากันได้กับ WXBizMsgCrypt SDK อย่างเป็นทางการของ Tencent

ข้อจำกัด

  • ไม่มีการสตรีม (No streaming) - การตอบกลับจะมาถึงเป็นข้อความที่สมบูรณ์หลังจาก agent ทำงานเสร็จสิ้น
  • ไม่มีตัวบ่งชี้การพิมพ์ (No typing indicators) - โมเดล callback ไม่รองรับสถานะการพิมพ์
  • ข้อความเท่านั้น (Text only) - ปัจจุบันรองรับข้อความตัวอักษรสำหรับการป้อนข้อมูล; ยังไม่ได้ใช้งาน input รูปภาพ/ไฟล์/เสียง agent รับรู้ถึงความสามารถด้านสื่อขาออกผ่าน WeCom platform hint (รูปภาพ, เอกสาร, วิดีโอ, เสียง)
  • ความหน่วงในการตอบกลับ (Response latency) - session ของ agent ใช้เวลา 3-30 นาที; ผู้ใช้จะเห็นการตอบกลับเมื่อการประมวลผลเสร็จสมบูรณ์

📄 user-guide/messaging/weixin.md


sidebar_position: 15 title: "Weixin (WeChat)" description: "Connect Hermes Agent to personal WeChat accounts via the iLink Bot API"

Weixin (WeChat)

เชื่อมต่อ Hermes กับ WeChat (微信) ซึ่งเป็นแพลตฟอร์มส่งข้อความส่วนตัวของ Tencent อะแดปเตอร์นี้ใช้ iLink Bot API ของ Tencent สำหรับบัญชี WeChat ส่วนตัว ซึ่งแตกต่างจาก WeCom (Enterprise WeChat) ข้อความจะถูกส่งผ่าน long-polling ดังนั้นจึงไม่จำเป็นต้องมี public endpoint หรือ webhook

:::info อะแดปเตอร์นี้สำหรับ บัญชี WeChat ส่วนตัว (微信) หากคุณต้องการ WeChat สำหรับองค์กร/บริษัท โปรดดูที่ WeCom adapter แทน :::

Prerequisites

  • บัญชี WeChat ส่วนตัว
  • Python packages: aiohttp และ cryptography
  • มีการรวมการเรนเดอร์ QR code ใน Terminal เมื่อติดตั้ง Hermes ด้วย extra messaging

ติดตั้ง dependencies ที่จำเป็น:

pip install aiohttp cryptography
# Optional: for terminal QR code display
pip install hermes-agent[messaging]

Setup

1. Run the Setup Wizard

วิธีที่ง่ายที่สุดในการเชื่อมต่อบัญชี WeChat ของคุณคือการใช้ setup แบบโต้ตอบ:

hermes gateway setup

เลือก Weixin เมื่อระบบแจ้งเตือน Wizard จะดำเนินการดังนี้:

  1. ขอ QR code จาก iLink Bot API
  2. แสดง QR code ใน terminal ของคุณ (หรือให้ URL)
  3. รอให้คุณสแกน QR code ด้วยแอปพลิเคชัน WeChat บนมือถือ
  4. แจ้งให้คุณยืนยันการเข้าสู่ระบบบนโทรศัพท์ของคุณ
  5. บันทึก credentials ของบัญชีโดยอัตโนมัติไปยัง ~/.hermes/weixin/accounts/

เมื่อยืนยันแล้ว คุณจะเห็นข้อความประมาณว่า:

微信连接成功,account_id=your-account-id

Wizard จะจัดเก็บ account_id, token, และ base_url เพื่อที่คุณจะได้ไม่ต้องกำหนดค่าเหล่านี้ด้วยตนเอง

2. Configure Environment Variables

หลังจากเข้าสู่ระบบด้วย QR code ครั้งแรก ให้ตั้งค่าอย่างน้อย account ID ใน ~/.hermes/.env:

WEIXIN_ACCOUNT_ID=your-account-id

# Optional: override the token (normally auto-saved from QR login)
# WEIXIN_TOKEN=your-bot-token

# Optional: restrict access
WEIXIN_DM_POLICY=open
WEIXIN_ALLOWED_USERS=user_id_1,user_id_2

# Optional: restore legacy multiline splitting behavior
# WEIXIN_SPLIT_MULTILINE_MESSAGES=true

# Optional: home channel for cron/notifications
WEIXIN_HOME_CHANNEL=chat_id
WEIXIN_HOME_CHANNEL_NAME=Home

3. Start the Gateway

hermes gateway

อะแดปเตอร์จะกู้คืน credentials ที่บันทึกไว้ เชื่อมต่อกับ iLink API และเริ่ม long-polling สำหรับข้อความ

Features

  • Long-poll transport - ไม่ต้องมี public endpoint, webhook, หรือ WebSocket
  • QR code login - ตั้งค่าแบบ scan-to-connect ผ่าน hermes gateway setup
  • DM and group messaging - นโยบายการเข้าถึงที่กำหนดค่าได้
  • Media support - รองรับรูปภาพ วิดีโอ ไฟล์ และข้อความเสียง
  • AES-128-ECB encrypted CDN - การเข้ารหัส/ถอดรหัสอัตโนมัติสำหรับการถ่ายโอนสื่อทั้งหมด
  • Context token persistence - ความต่อเนื่องในการตอบกลับที่รองรับ disk-backed แม้จะรีสตาร์ท
  • Markdown formatting - รักษา Markdown รวมถึง headers, tables, และ code blocks ทำให้ WeChat clients ที่รองรับ Markdown สามารถเรนเดอร์ได้โดยธรรมชาติ
  • Smart message chunking - ข้อความจะยังคงเป็น bubble เดียวเมื่ออยู่ภายใต้ขีดจำกัด; มีเพียง payload ที่มีขนาดใหญ่เกินไปเท่านั้นที่ถูกแบ่งที่ขอบเขตเชิงตรรกะ
  • Typing indicators - แสดงสถานะ "กำลังพิมพ์…" ใน WeChat client ขณะที่ agent กำลังประมวลผล
  • SSRF protection - URL สื่อขาออกจะถูกตรวจสอบก่อนดาวน์โหลด
  • Message deduplication - หน้าต่างเลื่อน 5 นาที ป้องกันการประมวลผลซ้ำ
  • Automatic retry with backoff - กู้คืนจากข้อผิดพลาด API ชั่วคราว

Configuration Options

ตั้งค่าเหล่านี้ใน config.yaml ภายใต้ platforms.weixin.extra:

KeyDefaultDescription
account_idiLink Bot account ID (required)
tokeniLink Bot token (required, auto-saved from QR login)
base_urlhttps://ilinkai.weixin.qq.comiLink API base URL
cdn_base_urlhttps://novac2c.cdn.weixin.qq.com/c2cCDN base URL สำหรับการถ่ายโอนสื่อ
dm_policyopenการเข้าถึง DM: open, allowlist, disabled, pairing
group_policydisabledการเข้าถึงกลุ่ม: open, allowlist, disabled
allow_from[]User IDs ที่อนุญาตสำหรับ DM (เมื่อ dm_policy=allowlist)
group_allow_from[]Group IDs ที่อนุญาต (เมื่อ group_policy=allowlist)
split_multiline_messagesfalseเมื่อเป็น true จะแบ่งข้อความหลายบรรทัดเป็นการแชทหลายข้อความ (พฤติกรรมเดิม). เมื่อเป็น false จะเก็บข้อความหลายบรรทัดเป็นข้อความเดียว เว้นแต่จะเกินขีดจำกัดความยาว

Access Policies

DM Policy

ควบคุมว่าใครสามารถส่ง direct messages ถึง bot ได้:

ValueBehavior
openทุกคนสามารถ DM bot ได้ (ค่าเริ่มต้น)
allowlistเฉพาะ user IDs ใน allow_from เท่านั้นที่สามารถ DM ได้
disabledเพิกเฉยต่อ DM ทั้งหมด
pairingโหมดจับคู่ (สำหรับการตั้งค่าเริ่มต้น)
WEIXIN_DM_POLICY=allowlist
WEIXIN_ALLOWED_USERS=user_id_1,user_id_2

Group Policy

ควบคุมว่า bot จะตอบกลับในกลุ่มใด:

ValueBehavior
openbot ตอบกลับในทุกกลุ่ม
allowlistbot ตอบกลับเฉพาะ group IDs ที่ระบุใน group_allow_from
disabledเพิกเฉยต่อข้อความกลุ่มทั้งหมด (ค่าเริ่มต้น)
WEIXIN_GROUP_POLICY=allowlist
WEIXIN_GROUP_ALLOWED_USERS=group_id_1,group_id_2

:::note ค่า default group policy คือ disabled สำหรับ Weixin (แตกต่างจาก WeCom ที่ default เป็น open) นี่เป็นสิ่งที่ตั้งใจไว้ เนื่องจากบัญชี WeChat ส่วนตัวอาจอยู่ในหลายกลุ่ม :::

Media Support

Inbound (รับ)

อะแดปเตอร์จะรับ media attachments จากผู้ใช้ ดาวน์โหลดจาก WeChat CDN ถอดรหัส และแคชไว้ในเครื่องสำหรับการประมวลผลของ agent:

TypeHow it's handled
Imagesดาวน์โหลด, ถอดรหัส AES, และแคชเป็น JPEG
Videoดาวน์โหลด, ถอดรหัส AES, และแคชเป็น MP4
Filesดาวน์โหลด, ถอดรหัส AES, และแคช รักษาชื่อไฟล์เดิม
Voiceหากมี text transcription จะถูกดึงออกมาเป็นข้อความ มิฉะนั้นจะดาวน์โหลดและแคช audio (รูปแบบ SILK)

Quoted messages: สื่อจากข้อความที่ถูกอ้างถึง (replied-to) ก็จะถูกดึงออกมาด้วย เพื่อให้ agent มีบริบทว่าผู้ใช้กำลังตอบกลับอะไร

AES-128-ECB Encrypted CDN

ไฟล์สื่อ WeChat จะถูกถ่ายโอนผ่าน CDN ที่เข้ารหัส อะแดปเตอร์จัดการเรื่องนี้อย่างโปร่งใส:

  • Inbound: สื่อที่เข้ารหัสจะถูกดาวน์โหลดจาก CDN โดยใช้ URL encrypted_query_param จากนั้นถอดรหัสด้วย AES-128-ECB โดยใช้ key ต่อไฟล์ที่ระบุใน message payload
  • Outbound: ไฟล์จะถูกเข้ารหัสในเครื่องด้วย key แบบสุ่ม AES-128-ECB และอัปโหลดไปยัง CDN จากนั้นจึงรวม encrypted reference ไว้ในข้อความขาออก
  • AES key คือ 16 bytes (128-bit). Keys อาจมาในรูปแบบ raw base64 หรือ hex-encoded - อะแดปเตอร์จัดการทั้งสองรูปแบบ
  • สิ่งนี้ต้องใช้ Python package cryptography

ไม่จำเป็นต้องตั้งค่าใดๆ - การเข้ารหัสและการถอดรหัสจะเกิดขึ้นโดยอัตโนมัติ

Outbound (ส่ง)

MethodWhat it sends
sendข้อความข้อความพร้อม Markdown formatting
send_image / send_image_fileข้อความรูปภาพแบบ Native (ผ่านการอัปโหลด CDN)
send_documentไฟล์ attachments (ผ่านการอัปโหลด CDN)
send_videoข้อความวิดีโอ (ผ่านการอัปโหลด CDN)

สื่อขาออกทั้งหมดจะผ่านกระบวนการ upload encrypted CDN:

  1. สร้าง AES-128 key แบบสุ่ม
  2. เข้ารหัสไฟล์ด้วย AES-128-ECB + PKCS#7 padding
  3. ขอ upload URL จาก iLink API (getuploadurl)
  4. อัปโหลด ciphertext ไปยัง CDN
  5. ส่งข้อความพร้อม encrypted media reference

Context Token Persistence

iLink Bot API ต้องการ context_token เพื่อสะท้อนกลับไปพร้อมกับข้อความขาออกแต่ละข้อความสำหรับ peer ที่กำหนด อะแดปเตอร์จะรักษาสถานที่จัดเก็บ context token ที่รองรับ disk:

  • Tokens จะถูกบันทึกต่อ account+peer ที่ ~/.hermes/weixin/accounts/<account_id>.context-tokens.json
  • เมื่อเริ่มต้น จะกู้คืน tokens ที่บันทึกไว้ก่อนหน้า
  • ทุกข้อความขาเข้าจะอัปเดต token ที่จัดเก็บสำหรับผู้ส่งนั้น
  • ข้อความขาออกจะรวม context token ล่าสุดโดยอัตโนมัติ

สิ่งนี้รับประกันความต่อเนื่องในการตอบกลับแม้หลังจากที่ gateway รีสตาร์ท

Markdown Formatting

WeChat clients ที่เชื่อมต่อผ่าน iLink Bot API สามารถเรนเดอร์ Markdown ได้โดยตรง ดังนั้นอะแดปเตอร์จึงรักษา Markdown ไว้แทนการเขียนใหม่:

  • Headers ยังคงเป็น Markdown headings (#, ##, ...)
  • Tables ยังคงเป็น Markdown tables
  • Code fences ยังคงเป็น fenced code blocks
  • Excessive blank lines จะถูกยุบเป็น double newlines นอก fenced code blocks

Message Chunking

ข้อความจะถูกส่งเป็นแชทข้อความเดียวเมื่ออยู่ในขีดจำกัดของแพลตฟอร์ม มีเพียง payload ที่มีขนาดใหญ่เกินไปเท่านั้นที่ถูกแบ่งเพื่อการส่ง:

  • ความยาวข้อความสูงสุด: 4000 characters
  • ข้อความที่ต่ำกว่าขีดจำกัดจะยังคงสมบูรณ์แม้ว่าจะมีหลายย่อหน้าหรือการขึ้นบรรทัดใหม่
  • ข้อความขนาดใหญ่เกินไปจะถูกแบ่งที่ขอบเขตเชิงตรรกะ (ย่อหน้า, บรรทัดว่าง, code fences)
  • Code fences จะถูกเก็บไว้ให้สมบูรณ์เมื่อเป็นไปได้ (จะไม่ถูกแบ่งกลางบล็อก เว้นแต่ตัว fence เองจะเกินขีดจำกัด)
  • บล็อกเดี่ยวที่มีขนาดใหญ่เกินไปจะใช้ logic การตัดทอนของอะแดปเตอร์พื้นฐาน
  • การหน่วงเวลา inter-chunk 0.3 s ป้องกันการลด rate-limit ของ WeChat เมื่อมีการส่งหลาย chunks

Typing Indicators

อะแดปเตอร์แสดงสถานะการพิมพ์ใน WeChat client:

  1. เมื่อข้อความมาถึง อะแดปเตอร์จะดึง typing_ticket ผ่าน API getconfig
  2. Typing tickets จะถูกแคชเป็นเวลา 10 นาทีต่อผู้ใช้
  3. send_typing ส่งสัญญาณเริ่มพิมพ์; stop_typing ส่งสัญญาณหยุดพิมพ์
  4. gateway จะกระตุ้น typing indicators โดยอัตโนมัติในขณะที่ agent กำลังประมวลผลข้อความ

Long-Poll Connection

อะแดปเตอร์ใช้ HTTP long-polling (ไม่ใช่ WebSocket) เพื่อรับข้อความ:

How It Works

  1. Connect: ตรวจสอบ credentials และเริ่ม poll loop
  2. Poll: เรียก getupdates ด้วย timeout 35 วินาที; server จะคงคำขอไว้จนกว่าข้อความจะมาถึงหรือหมดเวลา
  3. Dispatch: ข้อความขาเข้าจะถูก dispatch แบบ concurrent ผ่าน asyncio.create_task
  4. Sync buffer: sync cursor แบบถาวร (get_updates_buf) จะถูกบันทึกใน disk เพื่อให้อะแดปเตอร์สามารถดำเนินการต่อจากตำแหน่งที่ถูกต้องหลังจากการรีสตาร์ท

Retry Behavior

เมื่อเกิดข้อผิดพลาด API อะแดปเตอร์ใช้กลยุทธ์การ retry แบบง่าย:

ConditionBehavior
Transient error (1st–2nd)Retry after 2 seconds
Repeated errors (3+)Back off for 30 seconds, then reset counter
Session expired (errcode=-14)Pause for 10 minutes (อาจต้อง re-login)
TimeoutRe-poll ทันที (พฤติกรรม long-poll ปกติ)

Deduplication

ข้อความขาเข้าจะถูก deduplicate โดยใช้ message IDs พร้อมหน้าต่าง 5 นาที สิ่งนี้ป้องกันการประมวลผลซ้ำระหว่างที่เครือข่ายมีปัญหาหรือการตอบกลับ poll ที่ทับซ้อนกัน

Token Lock

มีเพียง Weixin gateway instance เดียวเท่านั้นที่สามารถใช้ token ที่กำหนดได้ในเวลาใดเวลาหนึ่ง อะแดปเตอร์จะเข้าถึง scoped lock เมื่อเริ่มต้นและปล่อยเมื่อปิด หาก gateway อื่นกำลังใช้ token เดียวกันอยู่ การเริ่มต้นจะล้มเหลวพร้อมข้อความ error ที่ให้ข้อมูล

All Environment Variables

VariableRequiredDefaultDescription
WEIXIN_ACCOUNT_IDiLink Bot account ID (จาก QR login)
WEIXIN_TOKENiLink Bot token (auto-saved from QR login)
WEIXIN_BASE_URLhttps://ilinkai.weixin.qq.comiLink API base URL
WEIXIN_CDN_BASE_URLhttps://novac2c.cdn.weixin.qq.com/c2cCDN base URL สำหรับการถ่ายโอนสื่อ
WEIXIN_DM_POLICYopenนโยบายการเข้าถึง DM: open, allowlist, disabled, pairing
WEIXIN_GROUP_POLICYdisabledนโยบายการเข้าถึงกลุ่ม: open, allowlist, disabled
WEIXIN_ALLOWED_USERS(empty)User IDs คั่นด้วย comma สำหรับ DM allowlist
WEIXIN_GROUP_ALLOWED_USERS(empty)Group IDs คั่นด้วย comma สำหรับ group allowlist
WEIXIN_HOME_CHANNELChat ID สำหรับ output ของ cron/notification
WEIXIN_HOME_CHANNEL_NAMEHomeชื่อที่แสดงสำหรับ home channel
WEIXIN_ALLOW_ALL_USERSFlag ระดับ Gateway เพื่ออนุญาตผู้ใช้ทั้งหมด (ใช้โดย setup wizard)

Troubleshooting

ProblemFix
Weixin startup failed: aiohttp and cryptography are requiredติดตั้งทั้งสองตัว: pip install aiohttp cryptography
Weixin startup failed: WEIXIN_TOKEN is requiredรัน hermes gateway setup เพื่อทำ QR login ให้เสร็จสมบูรณ์ หรือตั้งค่า WEIXIN_TOKEN ด้วยตนเอง
Weixin startup failed: WEIXIN_ACCOUNT_ID is requiredตั้งค่า WEIXIN_ACCOUNT_ID ใน .env ของคุณ หรือรัน hermes gateway setup
Another local Hermes gateway is already using this Weixin tokenหยุด instance gateway อื่นก่อน - อนุญาตให้มี poller ได้เพียงตัวเดียวต่อ token
Session expired (errcode=-14)session login ของคุณหมดอายุแล้ว รัน hermes gateway setup ใหม่เพื่อสแกน QR code ใหม่
QR code expired during setupQR code จะรีเฟรชอัตโนมัติสูงสุด 3 ครั้ง หากยังคงหมดอายุ ให้ตรวจสอบการเชื่อมต่อเครือข่ายของคุณ
Bot doesn't respond to DMsตรวจสอบ WEIXIN_DM_POLICY - หากตั้งค่าเป็น allowlist ผู้ส่งต้องอยู่ใน WEIXIN_ALLOWED_USERS
Bot ignores group messagesgroup policy default เป็น disabled ตั้งค่า WEIXIN_GROUP_POLICY=open หรือ allowlist
Media download/upload failsตรวจสอบว่าติดตั้ง cryptography แล้วหรือไม่ ตรวจสอบการเข้าถึงเครือข่ายไปยัง novac2c.cdn.weixin.qq.com
Blocked unsafe URL (SSRF protection)URL สื่อขาออกชี้ไปยังที่อยู่ส่วนตัว/ภายใน มีเพียง URL สาธารณะเท่านั้นที่อนุญาต
Voice messages show as textหาก WeChat ให้ transcription อะแดปเตอร์จะใช้ข้อความนี้ นี่คือพฤติกรรมที่คาดหวัง
Messages appear duplicatedอะแดปเตอร์จะ deduplicate ด้วย message ID หากคุณเห็นข้อความซ้ำ ให้ตรวจสอบว่ามี gateway instance หลายตัวกำลังทำงานอยู่หรือไม่
iLink POST ... HTTP 4xx/5xxข้อผิดพลาด API จากบริการ iLink ตรวจสอบความถูกต้องของ token และการเชื่อมต่อเครือข่ายของคุณ
Terminal QR code doesn't renderติดตั้งใหม่ด้วย extra messaging: pip install hermes-agent[messaging]. หรือเปิด URL ที่พิมพ์ไว้เหนือ QR code

📄 user-guide/messaging/whatsapp.md


sidebar_position: 5 title: "WhatsApp" description: "ตั้งค่า Hermes Agent เป็นบอท WhatsApp ผ่าน Baileys bridge ที่ติดตั้งมาให้"

การตั้งค่า WhatsApp

Hermes เชื่อมต่อกับ WhatsApp ผ่าน bridge ที่ติดตั้งมาให้ ซึ่งอ้างอิงจาก Baileys วิธีการทำงานคือการจำลองเซสชัน WhatsApp Web — ไม่ใช่ผ่าน WhatsApp Business API อย่างเป็นทางการ ไม่จำเป็นต้องมีบัญชี Meta developer หรือการยืนยันธุรกิจใดๆ

:::warning API ที่ไม่เป็นทางการ - ความเสี่ยงในการถูกแบน WhatsApp ไม่ได้รองรับบอทจากบุคคลที่สามอย่างเป็นทางการนอกเหนือจาก Business API การใช้ bridge จากบุคคลที่สามมีความเสี่ยงเล็กน้อยที่บัญชีจะถูกจำกัดการใช้งาน เพื่อลดความเสี่ยง:

  • ใช้หมายเลขโทรศัพท์เฉพาะ สำหรับบอท (ไม่ใช่หมายเลขส่วนตัวของคุณ)
  • ห้ามส่งข้อความจำนวนมาก/สแปม - ควรคงการใช้งานแบบสนทนา
  • ห้ามทำระบบอัตโนมัติในการส่งข้อความขาออก ไปยังผู้ที่ไม่ได้ส่งข้อความมาก่อน :::

:::warning การอัปเดตโปรโตคอล WhatsApp Web WhatsApp มีการอัปเดตโปรโตคอล Web เป็นระยะ ซึ่งอาจทำให้ความเข้ากันได้กับ bridge จากบุคคลที่สามหยุดชะงักชั่วคราว เมื่อเกิดเหตุการณ์นี้ Hermes จะทำการอัปเดต dependency ของ bridge หากบอทหยุดทำงานหลังจากการอัปเดตของ WhatsApp ให้ดึงเวอร์ชันล่าสุดของ Hermes และทำการจับคู่ (re-pair) ใหม่ :::

สองโหมด

โหมดวิธีการทำงานเหมาะสำหรับ
หมายเลขบอทแยก (แนะนำ)กำหนดหมายเลขโทรศัพท์สำหรับบอทโดยเฉพาะ ผู้คนจะส่งข้อความไปยังหมายเลขนั้นโดยตรงประสบการณ์ผู้ใช้ที่สะอาด, ผู้ใช้หลายคน, ความเสี่ยงในการถูกแบนต่ำ
แชทส่วนตัวด้วยตนเองใช้ WhatsApp ของคุณเอง คุณส่งข้อความหาตัวเองเพื่อพูดคุยกับ agentการตั้งค่าที่รวดเร็ว, ผู้ใช้คนเดียว, การทดสอบ

สิ่งที่ต้องเตรียม (Prerequisites)

  • Node.js v18+ และ npm - bridge WhatsApp ทำงานเป็น process ของ Node.js
  • โทรศัพท์ที่มี WhatsApp ติดตั้งอยู่ (สำหรับสแกน QR code)

แตกต่างจาก bridge ที่ขับเคลื่อนด้วยเบราว์เซอร์รุ่นเก่า, bridge ที่ใช้ Baileys ในปัจจุบัน ไม่จำเป็นต้องมี dependency stack ของ Chromium หรือ Puppeteer ในเครื่อง


ขั้นตอนที่ 1: รัน Setup Wizard

hermes whatsapp

Wizard จะ:

  1. สอบถามว่าต้องการโหมดใด (bot หรือ self-chat)
  2. ติดตั้ง dependency ของ bridge หากจำเป็น
  3. แสดง QR code ใน terminal ของคุณ
  4. รอให้คุณสแกน

วิธีสแกน QR code:

  1. เปิด WhatsApp บนโทรศัพท์ของคุณ
  2. ไปที่ Settings → Linked Devices
  3. แตะ Link a Device
  4. เล็งกล้องไปที่ QR code ใน terminal

เมื่อจับคู่สำเร็จ Wizard จะยืนยันการเชื่อมต่อและออกจากระบบ เซสชันของคุณจะถูกบันทึกโดยอัตโนมัติ

:::tip หาก QR code ดูผิดเพี้ยน ให้แน่ใจว่า terminal ของคุณมีความกว้างอย่างน้อย 60 คอลัมน์ และรองรับ Unicode คุณยังสามารถลองใช้ terminal emulator อื่นได้ :::


ขั้นตอนที่ 2: การหาหมายเลขโทรศัพท์เครื่องที่สอง (Bot Mode)

สำหรับโหมดบอท คุณต้องใช้หมายเลขโทรศัพท์ที่ยังไม่ได้ลงทะเบียนกับ WhatsApp มีสามตัวเลือก:

ตัวเลือกค่าใช้จ่ายหมายเหตุ
Google Voiceฟรีสำหรับสหรัฐฯ เท่านั้น รับหมายเลขได้ที่ voice.google.com ยืนยัน WhatsApp ผ่าน SMS ด้วยแอป Google Voice
ซิมเติมเงิน$5–15 ครั้งเดียวผู้ให้บริการรายใดก็ได้ เปิดใช้งาน ยืนยัน WhatsApp จากนั้นซิมสามารถเก็บไว้ในลิ้นชักได้ หมายเลขต้องยังใช้งานอยู่ (โทรออกทุก 90 วัน)
บริการ VoIPฟรี–$5/เดือนTextNow, TextFree หรือที่คล้ายกัน หมายเลข VoIP บางหมายเลขถูก WhatsApp บล็อก - ลองหลายๆ ตัวหากตัวแรกใช้ไม่ได้

หลังจากได้หมายเลขแล้ว:

  1. ติดตั้ง WhatsApp บนโทรศัพท์ (หรือใช้แอป WhatsApp Business แบบ dual-SIM)
  2. ลงทะเบียนหมายเลขใหม่กับ WhatsApp
  3. รัน hermes whatsapp และสแกน QR code จากบัญชี WhatsApp นั้น

ขั้นตอนที่ 3: กำหนดค่า Hermes

เพิ่มสิ่งต่อไปนี้ในไฟล์ ~/.hermes/.env ของคุณ:

# Required
WHATSAPP_ENABLED=true
WHATSAPP_MODE=bot                          # "bot" หรือ "self-chat"

# การควบคุมการเข้าถึง - เลือกตัวเลือกใดตัวเลือกหนึ่ง:
WHATSAPP_ALLOWED_USERS=15551234567         # หมายเลขโทรศัพท์ที่คั่นด้วยเครื่องหมายจุลภาค (พร้อมรหัสประเทศ, ห้ามมี +)
# WHATSAPP_ALLOWED_USERS=*                 # หรือใช้ * เพื่ออนุญาตทุกคน
# WHATSAPP_ALLOW_ALL_USERS=true            # หรือตั้งค่า flag นี้แทน (ผลลัพธ์เหมือนกับ *)

:::tip Allow-all shorthand การตั้งค่า WHATSAPP_ALLOWED_USERS=* อนุญาตให้ผู้ส่ง ทุกคน (เทียบเท่ากับ WHATSAPP_ALLOW_ALL_USERS=true) สิ่งนี้สอดคล้องกับ Signal group allowlists หากต้องการใช้ flow การจับคู่ (pairing flow) แทน ให้ลบทั้งสองตัวแปรออกและพึ่งพา ระบบการจับคู่ DM :::

การตั้งค่าพฤติกรรมเสริมใน ~/.hermes/config.yaml:

unauthorized_dm_behavior: pair

whatsapp:
  unauthorized_dm_behavior: ignore
  • unauthorized_dm_behavior: pair คือค่าเริ่มต้นทั่วโลก ผู้ส่ง DM ที่ไม่รู้จักจะได้รับรหัสจับคู่
  • whatsapp.unauthorized_dm_behavior: ignore ทำให้ WhatsApp เงียบสำหรับ DM ที่ไม่ได้รับอนุญาต ซึ่งโดยทั่วไปเป็นตัวเลือกที่ดีกว่าสำหรับหมายเลขส่วนตัว

จากนั้นเริ่ม gateway:

hermes gateway              # Foreground
hermes gateway install      # ติดตั้งเป็น user service
sudo hermes gateway install --system   # สำหรับ Linux เท่านั้น: system service ที่บูตเครื่อง

gateway จะเริ่ม bridge WhatsApp โดยอัตโนมัติโดยใช้เซสชันที่บันทึกไว้


การคงอยู่ของเซสชัน (Session Persistence)

bridge Baileys จะบันทึกเซสชันภายใต้ ~/.hermes/platforms/whatsapp/session ซึ่งหมายความว่า:

  • เซสชันอยู่รอดแม้จะรีสตาร์ท - คุณไม่จำเป็นต้องสแกน QR code ใหม่ทุกครั้ง
  • ข้อมูลเซสชันรวมถึงคีย์การเข้ารหัสและ credentials ของอุปกรณ์
  • ห้ามแชร์หรือ commit directory เซสชันนี้ - เพราะมันให้สิทธิ์การเข้าถึงบัญชี WhatsApp อย่างสมบูรณ์

การจับคู่ใหม่ (Re-pairing)

หากเซสชันขาด (รีเซ็ตโทรศัพท์, อัปเดต WhatsApp, ยกเลิกการเชื่อมต่อด้วยตนเอง) คุณจะเห็นข้อผิดพลาดการเชื่อมต่อใน log ของ gateway หากต้องการแก้ไข:

hermes whatsapp

สิ่งนี้จะสร้าง QR code ใหม่ สแกนอีกครั้งและเซสชันจะถูกสร้างขึ้นใหม่ gateway จะจัดการการตัดการเชื่อมต่อ ชั่วคราว (network blips, โทรศัพท์ออฟไลน์ชั่วคราว) โดยอัตโนมัติด้วย logic การเชื่อมต่อใหม่


ข้อความเสียง (Voice Messages)

Hermes รองรับข้อความเสียงบน WhatsApp:

  • ขาเข้า: ข้อความเสียง (.ogg opus) จะถูกถอดเสียงโดยอัตโนมัติโดยใช้ STT provider ที่กำหนดค่าไว้: local faster-whisper, Groq Whisper (GROQ_API_KEY), หรือ OpenAI Whisper (VOICE_TOOLS_OPENAI_KEY)
  • ขาออก: การตอบกลับแบบ TTS จะถูกส่งเป็นไฟล์แนบเสียง MP3
  • การตอบกลับของ Agent จะมีคำนำหน้าด้วย "⚕ Hermes Agent" โดยค่าเริ่มต้น คุณสามารถปรับแต่งหรือปิดใช้งานสิ่งนี้ได้ใน config.yaml:
# ~/.hermes/config.yaml
whatsapp:
  reply_prefix: ""                          # ข้อความว่างเปล่าจะปิด header
  # reply_prefix: "🤖 *My Bot*\n──────\n"  # Custom prefix (รองรับ \n สำหรับการขึ้นบรรทัดใหม่)

การจัดรูปแบบและการส่งข้อความ (Message Formatting & Delivery)

WhatsApp รองรับ การตอบกลับแบบสตรีมมิง (progressive responses) - บอทจะแก้ไขข้อความของตัวเองแบบเรียลไทม์ขณะที่ AI สร้างข้อความ เช่นเดียวกับ Discord และ Telegram ภายในแล้ว WhatsApp ถูกจัดประเภทเป็นแพลตฟอร์ม TIER_MEDIUM สำหรับความสามารถในการส่งมอบ

Chunking

การตอบกลับที่ยาวจะถูกแบ่งออกเป็นหลายข้อความโดยอัตโนมัติที่ 4,096 ตัวอักษร ต่อ chunk (ขีดจำกัดการแสดงผลที่ใช้งานได้จริงของ WhatsApp) คุณไม่จำเป็นต้องกำหนดค่าใดๆ - gateway จะจัดการการแบ่งและส่ง chunks ตามลำดับ

Markdown ที่เข้ากันได้กับ WhatsApp

Markdown มาตรฐานในการตอบกลับ AI จะถูกแปลงเป็นรูปแบบเนทีฟของ WhatsApp โดยอัตโนมัติ:

MarkdownWhatsAppแสดงผลเป็น
**bold***bold*bold
~~strikethrough~~~strikethrough~strikethrough
# Heading*Heading*ข้อความตัวหนา (ไม่มี heading เนทีฟ)
[link text](url)link text (url)Inline URL

Code blocks และ inline code จะถูกเก็บไว้ตามเดิม เนื่องจาก WhatsApp รองรับการจัดรูปแบบแบบ triple-backtick โดยเนทีฟ

Tool Progress

เมื่อ agent เรียกใช้ tools (web search, file operations, etc.) WhatsApp จะแสดงตัวบ่งชี้ความคืบหน้าแบบเรียลไทม์ว่า tool ใดกำลังทำงานอยู่ สิ่งนี้เปิดใช้งานโดยค่าเริ่มต้น - ไม่ต้องกำหนดค่าใดๆ


การแก้ไขปัญหา (Troubleshooting)

ปัญหาวิธีแก้ไข
QR code สแกนไม่ได้ตรวจสอบให้แน่ใจว่า terminal กว้างพอ (60+ คอลัมน์) ลองใช้ terminal อื่น ตรวจสอบให้แน่ใจว่าคุณสแกนจากบัญชี WhatsApp ที่ถูกต้อง (หมายเลขบอท ไม่ใช่ส่วนตัว)
QR code หมดอายุQR code จะรีเฟรชทุกๆ ~20 วินาที หากหมดเวลา ให้รีสตาร์ท hermes whatsapp
เซสชันไม่คงอยู่ตรวจสอบว่า ~/.hermes/platforms/whatsapp/session มีอยู่และสามารถเขียนได้ หากใช้ container ให้ mount เป็น persistent volume
ออกจากระบบโดยไม่คาดคิดWhatsApp จะยกเลิกการเชื่อมต่ออุปกรณ์หลังจากไม่มีการใช้งานเป็นเวลานาน ให้เปิดโทรศัพท์และเชื่อมต่อเครือข่ายไว้ จากนั้นทำการจับคู่ใหม่ด้วย hermes whatsapp หากจำเป็น
Bridge ล่มหรือวนลูปการเชื่อมต่อใหม่รีสตาร์ท gateway, อัปเดต Hermes, และจับคู่ใหม่หากเซสชันถูกทำให้เป็นโมฆะโดยการเปลี่ยนแปลงโปรโตคอลของ WhatsApp
บอทหยุดทำงานหลังการอัปเดต WhatsAppอัปเดต Hermes เพื่อรับ bridge เวอร์ชันล่าสุด จากนั้นทำการจับคู่ใหม่
macOS: "Node.js not installed" แต่ node ทำงานใน terminalบริการ launchd ไม่ได้สืบทอด PATH ของ shell ของคุณ รัน hermes gateway install เพื่อบันทึก PATH ปัจจุบันของคุณลงใน plist จากนั้น hermes gateway start ดู Gateway Service docs สำหรับรายละเอียด
ไม่ได้รับข้อความตรวจสอบว่า WHATSAPP_ALLOWED_USERS รวมถึงหมายเลขของผู้ส่ง (พร้อมรหัสประเทศ, ห้ามมี + หรือช่องว่าง) หรือตั้งค่าเป็น * เพื่ออนุญาตทุกคน ตั้งค่า WHATSAPP_DEBUG=true ใน .env และรีสตาร์ท gateway เพื่อดู event ข้อความดิบใน bridge.log
บอทตอบผู้แปลกหน้าด้วยรหัสจับคู่ตั้งค่า whatsapp.unauthorized_dm_behavior: ignore ใน ~/.hermes/config.yaml หากคุณต้องการให้ DM ที่ไม่ได้รับอนุญาตถูกเพิกเฉยอย่างเงียบๆ แทน

ความปลอดภัย (Security)

:::warning กำหนดค่าการควบคุมการเข้าถึง ก่อนใช้งานจริง ตั้งค่า WHATSAPP_ALLOWED_USERS ด้วยหมายเลขโทรศัพท์ที่เฉพาะเจาะจง (รวมรหัสประเทศ, ห้ามมี +) ใช้ * เพื่ออนุญาตทุกคน หรือตั้งค่า WHATSAPP_ALLOW_ALL_USERS=true หากไม่มีการตั้งค่าใดๆ gateway จะ ปฏิเสธข้อความขาเข้าทั้งหมด เพื่อความปลอดภัย :::

โดยค่าเริ่มต้น, DM ที่ไม่ได้รับอนุญาตยังคงได้รับคำตอบเป็นรหัสจับคู่ หากคุณต้องการให้หมายเลข WhatsApp ส่วนตัวเงียบสนิทกับคนแปลกหน้า ให้ตั้งค่า:

whatsapp:
  unauthorized_dm_behavior: ignore
  • directory ~/.hermes/platforms/whatsapp/session บรรจุ credentials ของเซสชันทั้งหมด - โปรดปกป้องมันเหมือนรหัสผ่าน
  • ตั้งค่าสิทธิ์ไฟล์: chmod 700 ~/.hermes/platforms/whatsapp/session
  • ใช้ หมายเลขโทรศัพท์เฉพาะ สำหรับบอทเพื่อแยกความเสี่ยงจากบัญชีส่วนตัวของคุณ
  • หากสงสัยว่าถูกบุกรุก ให้ยกเลิกการเชื่อมต่ออุปกรณ์จาก WhatsApp → Settings → Linked Devices
  • หมายเลขโทรศัพท์ใน log จะถูกปกปิดบางส่วน แต่โปรดตรวจสอบนโยบายการเก็บรักษา log ของคุณ

extent analysis

TL;DR

  • แก้ไขปัญหาการเชื่อมต่อ WhatsApp โดยตรวจสอบให้แน่ใจว่า QR code ถูกสแกนจากบัญชี WhatsApp ที่ถูกต้อง (หมายเลขบอท ไม่ใช่ส่วนตัว) และตรวจสอบว่า ~/.hermes/platforms/whatsapp/session มีอยู่และสามารถเขียนได้

Guidance

  1. ตรวจสอบการเชื่อมต่ออุปกรณ์: ตรวจสอบว่าอุปกรณ์โทรศัพท์ของคุณเชื่อมต่อกับอินเทอร์เน็ตและเปิดใช้งาน WhatsApp
  2. รีสตาร์ท gateway: รีสตาร์ท gateway และทำการจับคู่ใหม่ด้วย hermes whatsapp หากจำเป็น
  3. อัปเดต Hermes: อัปเดต Hermes เพื่อรับ bridge เวอร์ชันล่าสุด
  4. ตรวจสอบ log: ตรวจสอบ log ของ gateway เพื่อหาสาเหตุของปัญหา

Example

hermes whatsapp

สิ่งนี้จะสร้าง QR code ใหม่และช่วยให้คุณจับคู่อีกครั้ง

Notes

  • ตรวจสอบให้แน่ใจว่าได้ตั้งค่า WHATSAPP_ALLOWED_USERS ด้วยหมายเลขโทรศัพท์ที่เฉพาะเจาะจง (รวมรหัสประเทศ, ห้ามมี +) หรือตั้งค่าเป็น * เพื่ออนุญาตทุกคน
  • ใช้ WHATSAPP_ALLOW_ALL_USERS=true หากต้องการอนุญาตทุกคน

Recommendation

  • อัปเดต Hermes เพื่อรับ bridge เวอร์ชันล่าสุดและแก้ไขปัญหาการเชื่อมต่อ WhatsApp
  • ตรวจสอบให้แน่ใจว

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING