본문으로 건너뛰기

Weixin (WeChat)

Hermes를 Tencent의 personal messaging platform인 WeChat(微信)에 연결합니다. adapter는 personal WeChat account용 Tencent iLink Bot API를 사용합니다. 이는 WeCom(Enterprise WeChat)과 별개입니다. message는 long-polling으로 전달되므로 public endpoint나 webhook이 필요 없습니다.

정보

이 adapter는 personal WeChat account(微信)용입니다. enterprise/corporate WeChat이 필요하다면 WeCom adapter를 사용하세요.

iLink bot identity - 일반 WeChat group은 동작하지 않을 수 있음

QR login은 Hermes를 fully scriptable ordinary personal WeChat account가 아니라 iLink bot identity(예: a5ace6fd482e@im.bot)에 연결합니다. 결과:

  • iLink bot identity는 보통 normal contact처럼 일반 WeChat group에 invite될 수 없습니다.
  • iLink는 대부분의 bot-type account에서 ordinary WeChat group event(personal account에 대한 @ mention 포함)를 gateway로 전달하지 않는 경우가 많습니다.
  • QR code를 scan한 personal WeChat account를 @ mention하는 것은 iLink bot을 @ mention하는 것과 다릅니다. bot은 별도 identity입니다.
  • 아래 WEIXIN_GROUP_POLICY / WEIXIN_GROUP_ALLOWED_USERS setting은 iLink가 실제로 해당 account type의 group event를 반환할 때만 효과가 있습니다. 반환하지 않는다면 policy와 관계없이 group message는 Hermes에 도달하지 않습니다.

실무에서는 대부분 DM to iLink bot만 안정적으로 동작합니다. 설정 후에도 group delivery가 되지 않는다면 제약은 Hermes가 아니라 iLink 쪽에 있습니다. gateway는 WEIXIN_GROUP_POLICYdisabled가 아닌 값으로 설정되어 있으면 startup 시 WARNING을 log합니다.

prerequisites

  • personal WeChat account
  • Python package: aiohttp, cryptography
  • terminal QR rendering은 Hermes를 messaging extra와 함께 설치하면 포함됩니다.

필요한 dependency 설치:

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

setup

1. setup wizard 실행

WeChat account를 연결하는 가장 쉬운 방법은 interactive setup입니다.

hermes gateway setup

prompt가 나오면 Weixin을 선택합니다. wizard는 다음을 수행합니다.

  1. iLink Bot API에서 QR code 요청
  2. terminal에 QR code 표시 또는 URL 제공
  3. WeChat mobile app으로 QR code를 scan할 때까지 대기
  4. phone에서 login confirm 요청
  5. account credential을 ~/.hermes/weixin/accounts/에 자동 저장

confirm되면 다음과 비슷한 message가 표시됩니다.

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

wizard는 account_id, token, base_url을 저장하므로 수동으로 설정할 필요가 없습니다.

2. environment variable 설정

초기 QR login 후 최소한 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. gateway 시작

hermes gateway

adapter가 저장된 credential을 restore하고 iLink API에 연결한 뒤 message long-polling을 시작합니다.

features

  • Long-poll transport - public endpoint, webhook, WebSocket 필요 없음
  • QR code login - hermes gateway setup을 통한 scan-to-connect setup
  • DM messaging - configurable access policy. group messaging은 연결된 identity에 대해 iLink가 실제 group event를 deliver하는지에 달려 있습니다. iLink bot account에서는 보통 그렇지 않습니다.
  • Media support - image, video, file, voice message
  • AES-128-ECB encrypted CDN - 모든 media transfer 자동 encryption/decryption
  • Context token persistence - restart를 넘어 disk-backed reply continuity 유지
  • Markdown formatting - header, table, code block을 포함한 Markdown 보존. Markdown을 지원하는 WeChat client에서는 native rendering 가능
  • Smart message chunking - limit 아래에서는 single bubble 유지, 너무 큰 payload만 logical boundary에서 split
  • Typing indicators - agent 처리 중 WeChat client에 "typing…" status 표시
  • SSRF protection - outbound media URL download 전 validation
  • Message deduplication - 5분 sliding window로 double-processing 방지
  • Automatic retry with backoff - transient API error에서 recovery

configuration options

config.yamlplatforms.weixin.extra 아래에 설정합니다.

KeyDefaultDescription
account_idiLink Bot account ID(required)
tokeniLink Bot token(required, QR login에서 auto-saved)
base_urlhttps://ilinkai.weixin.qq.comiLink API base URL
cdn_base_urlhttps://novac2c.cdn.weixin.qq.com/c2cmedia transfer용 CDN base URL
dm_policyopenDM access: open, allowlist, disabled, pairing
group_policydisabledGroup access: open, allowlist, disabled
allow_from[]DM 허용 user ID(dm_policy=allowlist일 때)
group_allow_from[]허용 group ID(group_policy=allowlist일 때)
split_multiline_messagesfalsetrue이면 multi-line reply를 여러 chat message로 split합니다(legacy behavior). false이면 length limit을 넘지 않는 한 multi-line reply를 하나의 message로 유지합니다.

access policies

DM policy

bot에게 direct message를 보낼 수 있는 사람을 제어합니다.

ValueBehavior
open누구나 bot에게 DM 가능(기본값)
allowlistallow_from에 있는 user ID만 DM 가능
disabled모든 DM 무시
pairinginitial setup용 pairing mode
WEIXIN_DM_POLICY=allowlist
WEIXIN_ALLOWED_USERS=user_id_1,user_id_2

group policy

connected identity에 대해 iLink가 group event를 deliver하는 경우에만 bot이 어느 group에서 respond할지 제어합니다. QR-login iLink bot identity(예: ...@im.bot)에서는 group event가 보통 아예 deliver되지 않으므로, 이 policy가 효과가 없을 수 있습니다. 위 iLink bot limitation warning을 참고하세요.

ValueBehavior
openevent가 deliver되는 모든 group에서 bot이 respond
allowlistgroup_allow_from에 listed된 group ID에서만 respond(event가 deliver될 때)
disabled모든 group message 무시(기본값)
WEIXIN_GROUP_POLICY=allowlist
# NOTE: this is a comma-separated list of group chat IDs, NOT member user IDs,
# despite the variable name containing "USERS". Keep this in mind when configuring.
WEIXIN_GROUP_ALLOWED_USERS=group_id_1,group_id_2
노트

Weixin의 default group policy는 disabled입니다. WeCom의 기본값이 open인 것과 다릅니다. personal WeChat account는 많은 group에 들어가 있을 수 있고, iLink bot identity는 보통 ordinary WeChat group message를 받을 수 없기 때문입니다. WEIXIN_GROUP_POLICYdisabled 외 값으로 설정하면 gateway가 startup 시 WARNING을 log합니다.

media support

inbound(receiving)

adapter는 user의 media attachment를 받고, WeChat CDN에서 download한 뒤 decrypt하여 agent processing을 위해 local에 cache합니다.

Type처리 방식
Imagesdownload, AES decrypt 후 JPEG로 cache
Videodownload, AES decrypt 후 MP4로 cache
Filesdownload, AES decrypt 후 cache. original filename 보존
Voicetext transcription이 있으면 text로 추출. 없으면 audio(SILK format)를 download/cache

Quoted messages: quoted(replied-to) message의 media도 추출되므로 agent는 사용자가 무엇에 reply하는지 context를 갖습니다.

AES-128-ECB encrypted CDN

WeChat media file은 encrypted CDN을 통해 transfer됩니다. adapter가 이를 transparent하게 처리합니다.

  • Inbound: encrypted media를 encrypted_query_param URL로 CDN에서 download한 뒤, message payload의 per-file key로 AES-128-ECB decrypt합니다.
  • Outbound: file을 random AES-128-ECB key로 local encrypt하고 CDN에 upload한 뒤 encrypted reference를 outbound message에 포함합니다.
  • AES key는 16 bytes(128-bit)입니다. key는 raw base64 또는 hex-encoded로 올 수 있으며 adapter가 둘 다 처리합니다.
  • cryptography Python package가 필요합니다.

별도 configuration은 필요 없습니다. encryption/decryption은 자동으로 수행됩니다.

outbound(sending)

MethodWhat it sends
sendMarkdown formatting이 있는 text messages
send_image / send_image_filenative image messages(CDN upload 사용)
send_documentfile attachments(CDN upload 사용)
send_videovideo messages(CDN upload 사용)

모든 outbound media는 encrypted CDN upload flow를 거칩니다.

  1. random AES-128 key 생성
  2. AES-128-ECB + PKCS#7 padding으로 file encrypt
  3. iLink API(getuploadurl)에서 upload URL 요청
  4. ciphertext를 CDN에 upload
  5. encrypted media reference로 message 전송

context token persistence

iLink Bot API는 given peer에 대한 outbound message마다 context_token을 되돌려 보내야 합니다. adapter는 disk-backed context token store를 유지합니다.

  • token은 account+peer별로 ~/.hermes/weixin/accounts/<account_id>.context-tokens.json에 저장됩니다.
  • startup 시 이전에 저장한 token을 restore합니다.
  • inbound message가 올 때마다 sender의 stored token을 update합니다.
  • outbound message에는 latest context token이 자동으로 포함됩니다.

이렇게 하면 gateway restart 후에도 reply continuity가 유지됩니다.

Markdown formatting

iLink Bot API로 연결된 WeChat client는 Markdown을 직접 render할 수 있으므로, adapter는 Markdown을 rewrite하지 않고 보존합니다.

  • Headers는 Markdown heading(#, ##, ...) 그대로 유지
  • Tables는 Markdown table 그대로 유지
  • Code fences는 fenced code block 그대로 유지
  • fenced code block 밖의 과도한 blank line은 double newline으로 collapse

message chunking

message는 platform limit 안에 들어가면 single chat message로 전달됩니다. oversized payload만 split됩니다.

  • 최대 message length: 4000 characters
  • limit 아래의 message는 여러 paragraph나 line break가 있어도 그대로 유지
  • oversized message는 logical boundary(paragraph, blank line, code fence)에서 split
  • code fence는 가능한 한 intact하게 유지. fence 자체가 limit을 넘는 경우가 아니면 mid-block split하지 않음
  • oversized individual block은 base adapter의 truncation logic으로 fallback
  • 여러 chunk를 보낼 때 WeChat rate-limit drop을 막기 위해 0.3초 inter-chunk delay 적용

typing indicators

adapter는 WeChat client에 typing status를 표시합니다.

  1. message가 오면 adapter가 getconfig API로 typing_ticket을 가져옵니다.
  2. typing ticket은 user별로 10분 cache됩니다.
  3. send_typing은 typing-start signal을 보내고, stop_typing은 typing-stop signal을 보냅니다.
  4. gateway는 agent가 message를 처리하는 동안 typing indicator를 자동 trigger합니다.

long-poll connection

adapter는 WebSocket이 아니라 HTTP long-polling으로 message를 받습니다.

동작 방식

  1. Connect: credential을 validate하고 poll loop를 시작합니다.
  2. Poll: getupdates를 35초 timeout으로 호출합니다. server는 message가 오거나 timeout될 때까지 request를 hold합니다.
  3. Dispatch: inbound message는 asyncio.create_task로 concurrently dispatch됩니다.
  4. Sync buffer: persistent sync cursor(get_updates_buf)가 disk에 저장되어 restart 후에도 올바른 위치부터 resume합니다.

retry behavior

API error가 나면 adapter는 단순한 retry strategy를 사용합니다.

ConditionBehavior
Transient error(1st-2nd)2초 뒤 retry
Repeated errors(3+)30초 backoff 후 counter reset
Session expired(errcode=-14)10분 pause(re-login 필요 가능)
Timeout즉시 re-poll(normal long-poll behavior)

deduplication

inbound message는 message ID로 5분 window 안에서 deduplicate됩니다. network hiccup이나 overlapping poll response 중 double-processing을 막습니다.

token lock

하나의 Weixin gateway instance만 given token을 사용할 수 있습니다. adapter는 startup 시 scoped lock을 획득하고 shutdown 시 release합니다. 다른 gateway가 이미 같은 token을 사용 중이면 startup이 informative error message와 함께 실패합니다.

all environment variables

VariableRequiredDefaultDescription
WEIXIN_ACCOUNT_IDiLink Bot account ID(QR login에서 얻음)
WEIXIN_TOKENiLink Bot token(QR login에서 auto-saved)
WEIXIN_BASE_URLhttps://ilinkai.weixin.qq.comiLink API base URL
WEIXIN_CDN_BASE_URLhttps://novac2c.cdn.weixin.qq.com/c2cmedia transfer용 CDN base URL
WEIXIN_DM_POLICYopenDM access policy: open, allowlist, disabled, pairing
WEIXIN_GROUP_POLICYdisabledGroup access policy: open, allowlist, disabled
WEIXIN_ALLOWED_USERS(empty)DM allowlist용 comma-separated user IDs
WEIXIN_GROUP_ALLOWED_USERS(empty)group allowlist용 comma-separated group chat IDs(member user IDs 아님). variable name은 legacy입니다.
WEIXIN_HOME_CHANNELcron/notification output용 Chat ID
WEIXIN_HOME_CHANNEL_NAMEHomehome channel display name
WEIXIN_ALLOW_ALL_USERSsetup wizard가 사용하는 gateway-level allow-all flag

troubleshooting

ProblemFix
Weixin startup failed: aiohttp and cryptography are required둘 다 설치: pip install aiohttp cryptography
Weixin startup failed: WEIXIN_TOKEN is requiredhermes gateway setup으로 QR login을 완료하거나 WEIXIN_TOKEN을 수동 설정
Weixin startup failed: WEIXIN_ACCOUNT_ID is required.envWEIXIN_ACCOUNT_ID를 설정하거나 hermes gateway setup 실행
Another local Hermes gateway is already using this Weixin token먼저 다른 gateway instance를 중지하세요. token당 poller 하나만 허용됩니다.
Session expired(errcode=-14)login session이 만료되었습니다. hermes gateway setup을 다시 실행해 새 QR code를 scan하세요.
QR code expired during setupQR은 최대 3번 auto-refresh됩니다. 계속 만료되면 network connection을 확인하세요.
Bot doesn't respond to DMsWEIXIN_DM_POLICY 확인. allowlist이면 sender가 WEIXIN_ALLOWED_USERS에 있어야 합니다.
Bot ignores group messagesgroup policy 기본값은 disabled입니다. WEIXIN_GROUP_POLICY=open 또는 allowlist로 설정하세요. 단, QR-login iLink bot identity(...@im.bot)는 보통 ordinary WeChat group message를 받을 수 없습니다. gateway log에 group raw inbound event가 없다면 제약은 Hermes가 아니라 iLink 쪽입니다.
Media download/upload failscryptography가 설치되어 있는지 확인하고 novac2c.cdn.weixin.qq.com network access를 확인하세요.
Blocked unsafe URL (SSRF protection)outbound media URL이 private/internal address를 가리킵니다. public URL만 허용됩니다.
Voice messages show as textWeChat이 transcription을 제공하면 adapter는 text를 사용합니다. 정상 동작입니다.
Messages appear duplicatedadapter는 message ID로 deduplicate합니다. 중복이 보이면 여러 gateway instance가 실행 중인지 확인하세요.
iLink POST ... HTTP 4xx/5xxiLink service의 API error입니다. token validity와 network connectivity를 확인하세요.
Terminal QR code doesn't rendermessaging extra로 재설치하세요. pip install hermes-agent[messaging] 또는 QR 위에 출력된 URL을 여세요.