@pawastation/wechat-kf 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +291 -0
- package/README.zh-CN.md +401 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/src/accounts.d.ts +37 -0
- package/dist/src/accounts.js +205 -0
- package/dist/src/accounts.js.map +1 -0
- package/dist/src/api.d.ts +29 -0
- package/dist/src/api.js +172 -0
- package/dist/src/api.js.map +1 -0
- package/dist/src/bot.d.ts +35 -0
- package/dist/src/bot.js +379 -0
- package/dist/src/bot.js.map +1 -0
- package/dist/src/channel.d.ts +113 -0
- package/dist/src/channel.js +183 -0
- package/dist/src/channel.js.map +1 -0
- package/dist/src/chunk-utils.d.ts +18 -0
- package/dist/src/chunk-utils.js +58 -0
- package/dist/src/chunk-utils.js.map +1 -0
- package/dist/src/config-schema.d.ts +56 -0
- package/dist/src/config-schema.js +38 -0
- package/dist/src/config-schema.js.map +1 -0
- package/dist/src/constants.d.ts +19 -0
- package/dist/src/constants.js +20 -0
- package/dist/src/constants.js.map +1 -0
- package/dist/src/crypto.d.ts +18 -0
- package/dist/src/crypto.js +80 -0
- package/dist/src/crypto.js.map +1 -0
- package/dist/src/fs-utils.d.ts +7 -0
- package/dist/src/fs-utils.js +13 -0
- package/dist/src/fs-utils.js.map +1 -0
- package/dist/src/monitor.d.ts +18 -0
- package/dist/src/monitor.js +131 -0
- package/dist/src/monitor.js.map +1 -0
- package/dist/src/outbound.d.ts +66 -0
- package/dist/src/outbound.js +234 -0
- package/dist/src/outbound.js.map +1 -0
- package/dist/src/reply-dispatcher.d.ts +40 -0
- package/dist/src/reply-dispatcher.js +120 -0
- package/dist/src/reply-dispatcher.js.map +1 -0
- package/dist/src/runtime.d.ts +130 -0
- package/dist/src/runtime.js +22 -0
- package/dist/src/runtime.js.map +1 -0
- package/dist/src/send-utils.d.ts +30 -0
- package/dist/src/send-utils.js +89 -0
- package/dist/src/send-utils.js.map +1 -0
- package/dist/src/send.d.ts +7 -0
- package/dist/src/send.js +13 -0
- package/dist/src/send.js.map +1 -0
- package/dist/src/token.d.ts +8 -0
- package/dist/src/token.js +57 -0
- package/dist/src/token.js.map +1 -0
- package/dist/src/types.d.ts +173 -0
- package/dist/src/types.js +3 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/unicode-format.d.ts +26 -0
- package/dist/src/unicode-format.js +157 -0
- package/dist/src/unicode-format.js.map +1 -0
- package/dist/src/webhook.d.ts +22 -0
- package/dist/src/webhook.js +138 -0
- package/dist/src/webhook.js.map +1 -0
- package/dist/src/wechat-kf-directives.d.ts +34 -0
- package/dist/src/wechat-kf-directives.js +65 -0
- package/dist/src/wechat-kf-directives.js.map +1 -0
- package/index.ts +32 -0
- package/openclaw.plugin.json +31 -0
- package/package.json +91 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 pawaca
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
**English** | [中文](./README.zh-CN.md)
|
|
2
|
+
|
|
3
|
+
# WeChat KF for OpenClaw
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@pawastation/wechat-kf)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://openclaw.dev)
|
|
8
|
+
|
|
9
|
+
**WeChat Customer Service channel plugin for OpenClaw** — let WeChat users chat with your AI agent via the WeCom KF API. Zero runtime dependencies — uses only Node.js built-ins.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- **Inbound message handling** — receive text, image, voice, video, file, location, link, mini-program, channels, business card, and forwarded chat history from WeChat users (11+ message types)
|
|
16
|
+
- **Event handling** — processes enter_session, msg_send_fail, and servicer_status_change events
|
|
17
|
+
- **Rich outbound messaging** — send text, image, voice, video, file, and link messages back to users
|
|
18
|
+
- **Media upload & download** — automatically downloads inbound media and uploads outbound media via the WeCom temporary media API; supports HTTP URL download for outbound media
|
|
19
|
+
- **Markdown to Unicode formatting** — converts markdown bold/italic/headings/lists to Unicode Mathematical Alphanumeric symbols for styled plain-text display in WeChat
|
|
20
|
+
- **AES-256-CBC encryption** — full WeChat callback encryption/decryption with SHA-1 signature verification and PKCS#7 padding validation
|
|
21
|
+
- **Webhook + polling fallback** — HTTP webhook server for real-time callbacks, with automatic 30-second polling fallback for reliability; hardened with body size limits, method validation, and error responses
|
|
22
|
+
- **Dynamic KF account discovery** — KF account IDs (open_kfid) are automatically discovered from webhook callbacks with enable/disable/delete lifecycle management
|
|
23
|
+
- **Cursor-based incremental sync** — persists sync cursors per KF account with atomic file writes for crash safety
|
|
24
|
+
- **Access token auto-caching** — tokens cached in memory with hashed keys, automatic refresh 5 minutes before expiry, and auto-retry on token expiry
|
|
25
|
+
- **Multi-KF-account isolation** — each KF account gets its own session, cursor, and routing context with per-kfId processing mutex
|
|
26
|
+
- **DM policy control** — configurable access control: `open` or `allowlist` with security adapter (resolveDmPolicy, collectWarnings). `pairing` mode is not yet implemented.
|
|
27
|
+
- **Text chunking** — automatically splits long replies to respect WeChat's 2000-character message size limit, with chunker declaration for framework integration
|
|
28
|
+
- **Session limit awareness** — detects and gracefully handles WeChat's 48-hour reply window and 5-message-per-window limits
|
|
29
|
+
- **Race condition safety** — per-kfId mutex and msgid deduplication prevent duplicate message processing
|
|
30
|
+
- **Human-like reply delays** — configurable typing delay simulation for natural conversation pacing
|
|
31
|
+
- **Graceful shutdown** — responds to abort signals with pre-check guards, cleanly stopping the webhook server and polling
|
|
32
|
+
|
|
33
|
+
## Prerequisites
|
|
34
|
+
|
|
35
|
+
1. A **WeCom account** (企业微信) with admin privileges — [Register here](https://work.weixin.qq.com/)
|
|
36
|
+
2. At least one **Customer Service account** (客服账号) created in WeCom's WeChat Customer Service module
|
|
37
|
+
3. A **publicly accessible URL** for receiving callbacks — you can use [ngrok](https://ngrok.com/), [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/), or a server with a public IP
|
|
38
|
+
4. **OpenClaw Gateway** installed and running (`openclaw gateway start`)
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
openclaw plugins install @pawastation/wechat-kf
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## WeCom Setup Guide
|
|
47
|
+
|
|
48
|
+
The WeChat KF API supports **two integration methods**. Both share the same underlying API (`sync_msg`, `send_msg`, etc.) and this plugin is fully compatible with either.
|
|
49
|
+
|
|
50
|
+
### Method comparison
|
|
51
|
+
|
|
52
|
+
| | Method 1: WeCom Admin Self-built App | Method 2: WeChat KF Admin API Hosting |
|
|
53
|
+
| ---------------------------- | ------------------------------------------------------------------------------- | -------------------------------------------------- |
|
|
54
|
+
| **Admin console** | [WeCom Admin](https://work.weixin.qq.com/wework_admin/frame) | [WeChat KF Admin](https://work.weixin.qq.com/kf/) |
|
|
55
|
+
| **Secret source** | Self-built app secret | WeChat KF dedicated secret |
|
|
56
|
+
| **Callback config location** | WeCom Admin > WeChat KF > API > Callback settings | WeChat KF Admin > Dev Config > Callback settings |
|
|
57
|
+
| **Callback URL requirement** | Must use a verified corporate domain (trusted domain configured in WeCom Admin) | No restriction — any publicly accessible URL works |
|
|
58
|
+
| **Requires self-built app** | Yes — create an app and grant KF API permissions | No — configure directly in the KF admin console |
|
|
59
|
+
| **IP whitelist** | Required (self-built app security requirement) | Not required |
|
|
60
|
+
| **API scope** | Full — can call other WeCom APIs alongside KF | Limited to WeChat KF APIs only |
|
|
61
|
+
| **Best for** | Teams with existing WeCom integrations | Developers who only need AI customer service |
|
|
62
|
+
| **Complexity** | Higher — create app, grant permissions, configure IP whitelist | Lower — enable API and go |
|
|
63
|
+
|
|
64
|
+
> **Important:** The two methods are **mutually exclusive** — a KF account can only be managed through one method at a time. To switch, you must first unbind the current API integration.
|
|
65
|
+
|
|
66
|
+
### Required credentials
|
|
67
|
+
|
|
68
|
+
Regardless of which method you choose, you need these four values for the plugin configuration:
|
|
69
|
+
|
|
70
|
+
| Credential | Where to find it |
|
|
71
|
+
| ------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
|
|
72
|
+
| **Corp ID** (`corpId`) | WeCom Admin > My Enterprise > Enterprise ID (format: `wwXXXXXXXXXXXXXXXX`) |
|
|
73
|
+
| **App Secret** (`appSecret`) | Method 1: WeCom Admin > App Management > App Details > Secret; Method 2: WeChat KF Admin > Dev Config > Secret |
|
|
74
|
+
| **Token** (`token`) | Generated when configuring callback URL (any random string, up to 32 chars) |
|
|
75
|
+
| **EncodingAESKey** (`encodingAESKey`) | Generated when configuring callback URL (43-char string) |
|
|
76
|
+
|
|
77
|
+
### Detailed setup instructions
|
|
78
|
+
|
|
79
|
+
Since the WeCom and WeChat KF admin consoles are entirely in Chinese, detailed step-by-step setup instructions are provided in the [Chinese guide](./README.zh-CN.md#企业微信客服接入指南). The guide covers:
|
|
80
|
+
|
|
81
|
+
- **Method 1** (企业微信后台自建应用): Creating a self-built app, granting KF permissions, configuring callback URL and IP whitelist (6 steps)
|
|
82
|
+
- **Method 2** (微信客服后台 API 托管): Enabling the API directly from the KF admin console (5 steps)
|
|
83
|
+
- A detailed comparison table of both methods
|
|
84
|
+
|
|
85
|
+
## Configuration
|
|
86
|
+
|
|
87
|
+
Add the following to your OpenClaw config (`~/.openclaw/openclaw.yaml` or via `openclaw config`):
|
|
88
|
+
|
|
89
|
+
```yaml
|
|
90
|
+
channels:
|
|
91
|
+
wechat-kf:
|
|
92
|
+
enabled: true
|
|
93
|
+
corpId: "wwXXXXXXXXXXXXXXXX" # Your Corp ID
|
|
94
|
+
appSecret: "your-app-secret-here" # App Secret (self-built app or WeChat KF secret)
|
|
95
|
+
token: "your-callback-token" # Callback Token
|
|
96
|
+
encodingAESKey: "your-43-char-key" # Callback EncodingAESKey (43 characters)
|
|
97
|
+
webhookPort: 9999 # Local port for webhook server (default: 9999)
|
|
98
|
+
webhookPath: "/wechat-kf" # URL path for webhook (default: /wechat-kf)
|
|
99
|
+
dmPolicy: "open" # Access control: open | allowlist (pairing: not yet implemented)
|
|
100
|
+
# allowFrom: # Only used with dmPolicy: allowlist
|
|
101
|
+
# - "external_userid_1"
|
|
102
|
+
# - "external_userid_2"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Configuration reference
|
|
106
|
+
|
|
107
|
+
| Field | Type | Required | Default | Description |
|
|
108
|
+
| ---------------- | -------- | -------- | ------------ | ------------------------------------------------------- |
|
|
109
|
+
| `enabled` | boolean | No | `false` | Enable the channel |
|
|
110
|
+
| `corpId` | string | **Yes** | — | WeCom Corp ID |
|
|
111
|
+
| `appSecret` | string | **Yes** | — | Self-built app secret or WeChat KF secret |
|
|
112
|
+
| `token` | string | **Yes** | — | Webhook callback token |
|
|
113
|
+
| `encodingAESKey` | string | **Yes** | — | 43-char AES key for message encryption |
|
|
114
|
+
| `webhookPort` | integer | No | `9999` | Port for the HTTP webhook server |
|
|
115
|
+
| `webhookPath` | string | No | `/wechat-kf` | URL path for webhook callbacks |
|
|
116
|
+
| `dmPolicy` | string | No | `"open"` | `open` / `allowlist` (`pairing` not yet implemented) |
|
|
117
|
+
| `allowFrom` | string[] | No | `[]` | Allowed external_userids (when dmPolicy is `allowlist`) |
|
|
118
|
+
|
|
119
|
+
## Verification
|
|
120
|
+
|
|
121
|
+
1. Start the gateway:
|
|
122
|
+
```bash
|
|
123
|
+
openclaw gateway start
|
|
124
|
+
```
|
|
125
|
+
2. Expose the webhook port (if not on a public server):
|
|
126
|
+
```bash
|
|
127
|
+
ngrok http 9999
|
|
128
|
+
```
|
|
129
|
+
3. Copy the HTTPS URL (e.g. `https://xxxx.ngrok-free.app`) and set the callback URL in WeCom:
|
|
130
|
+
```
|
|
131
|
+
https://xxxx.ngrok-free.app/wechat-kf
|
|
132
|
+
```
|
|
133
|
+
4. WeCom sends a GET verification request — the plugin decrypts the `echostr` and responds automatically
|
|
134
|
+
5. Send a test message from WeChat (via the KF link) and confirm the agent responds
|
|
135
|
+
|
|
136
|
+
## Usage
|
|
137
|
+
|
|
138
|
+
Once configured and running, the plugin works automatically:
|
|
139
|
+
|
|
140
|
+
1. **Users** tap your Customer Service link in WeChat to start a conversation
|
|
141
|
+
2. **Inbound messages** arrive via webhook — the plugin decrypts, syncs messages via `sync_msg`, downloads any media, and dispatches to your OpenClaw agent
|
|
142
|
+
3. **The agent** processes the message and generates a reply
|
|
143
|
+
4. **Outbound replies** are sent back via the WeCom `send_msg` API, with markdown automatically converted to Unicode-styled plain text
|
|
144
|
+
|
|
145
|
+
### Sending messages from the agent
|
|
146
|
+
|
|
147
|
+
The agent can use the `message` tool to send messages:
|
|
148
|
+
|
|
149
|
+
- **Reply to current conversation** — omit `target`; the reply goes to whoever messaged
|
|
150
|
+
- **Send to a specific user** — set `target` to the user's `external_userid`
|
|
151
|
+
- **Send media** — use `filePath` or `media` to attach images, voice, video, or files
|
|
152
|
+
|
|
153
|
+
### Supported inbound message types
|
|
154
|
+
|
|
155
|
+
| WeChat Type | How it's handled |
|
|
156
|
+
| ------------------------ | --------------------------------------------------------------------- |
|
|
157
|
+
| Text | Passed as-is to the agent |
|
|
158
|
+
| Image | Downloaded, saved as media attachment, placeholder text sent to agent |
|
|
159
|
+
| Voice | Downloaded as AMR, saved as media attachment |
|
|
160
|
+
| Video | Downloaded as MP4, saved as media attachment |
|
|
161
|
+
| File | Downloaded, saved as media attachment |
|
|
162
|
+
| Location | Converted to text: `[Location: name address]` |
|
|
163
|
+
| Link | Converted to text: `[Link: title url]` |
|
|
164
|
+
| Mini Program | Converted to text with title and appid |
|
|
165
|
+
| Channels (Video Account) | Converted to text with type, nickname, title |
|
|
166
|
+
| Business Card | Converted to text with userid |
|
|
167
|
+
| Forwarded Messages | Parsed and expanded into readable text |
|
|
168
|
+
|
|
169
|
+
### Supported outbound message types
|
|
170
|
+
|
|
171
|
+
Text, image, voice, video, file, and link messages. Local files are automatically uploaded to WeChat's temporary media storage before sending.
|
|
172
|
+
|
|
173
|
+
## Architecture
|
|
174
|
+
|
|
175
|
+
```
|
|
176
|
+
WeChat User
|
|
177
|
+
|
|
|
178
|
+
v
|
|
179
|
+
WeCom Server (Tencent)
|
|
180
|
+
|
|
|
181
|
+
|--- POST callback ---> webhook.ts ---> verify signature + size/method guards
|
|
182
|
+
| (encrypted XML) | decrypt AES-256-CBC
|
|
183
|
+
| | extract OpenKfId + Token
|
|
184
|
+
| v
|
|
185
|
+
| bot.ts ---> DM policy check
|
|
186
|
+
| | per-kfId mutex + msgid dedup
|
|
187
|
+
| | sync_msg API (pull messages)
|
|
188
|
+
| | cursor-based incremental sync
|
|
189
|
+
| | handle events (enter_session, etc.)
|
|
190
|
+
| | download media attachments
|
|
191
|
+
| v
|
|
192
|
+
| OpenClaw Agent (dispatch via runtime)
|
|
193
|
+
| |
|
|
194
|
+
| +-----------+-----------+
|
|
195
|
+
| v v
|
|
196
|
+
| outbound.ts reply-dispatcher.ts
|
|
197
|
+
| (framework-driven) (plugin-internal streaming)
|
|
198
|
+
| chunker declaration markdown -> unicode
|
|
199
|
+
| sendText / sendMedia text chunking + delay
|
|
200
|
+
| | |
|
|
201
|
+
| +-----------+-----------+
|
|
202
|
+
| v
|
|
203
|
+
| send-utils.ts
|
|
204
|
+
| formatText, detectMediaType
|
|
205
|
+
| uploadAndSendMedia
|
|
206
|
+
| downloadMediaFromUrl
|
|
207
|
+
| v
|
|
208
|
+
+--- send_msg API <--- api.ts
|
|
209
|
+
(JSON)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Key modules
|
|
213
|
+
|
|
214
|
+
| Module | Role |
|
|
215
|
+
| --------------------- | ------------------------------------------------------------------------------------------------- |
|
|
216
|
+
| `webhook.ts` | HTTP server — GET verification, POST event handling, size/method guards |
|
|
217
|
+
| `crypto.ts` | AES-256-CBC encrypt/decrypt, SHA-1 signature, full PKCS#7 validation |
|
|
218
|
+
| `token.ts` | Access token cache with hashed key and auto-refresh |
|
|
219
|
+
| `api.ts` | WeCom API client (sync_msg, send_msg, media upload/download) with token auto-retry |
|
|
220
|
+
| `accounts.ts` | Dynamic KF account discovery, resolution, enable/disable/delete lifecycle |
|
|
221
|
+
| `bot.ts` | Message sync with mutex + dedup, DM policy check, event handling, agent dispatch |
|
|
222
|
+
| `monitor.ts` | Webhook + polling lifecycle management with AbortSignal guards |
|
|
223
|
+
| `reply-dispatcher.ts` | Plugin-internal streaming reply delivery with chunking, formatting, delays |
|
|
224
|
+
| `outbound.ts` | Framework-driven outbound adapter with chunker declaration |
|
|
225
|
+
| `send-utils.ts` | Shared outbound utilities (formatText, detectMediaType, uploadAndSendMedia, downloadMediaFromUrl) |
|
|
226
|
+
| `chunk-utils.ts` | Text chunking with natural boundary splitting (newline, whitespace, hard-cut) |
|
|
227
|
+
| `constants.ts` | Shared constants (WECHAT_TEXT_CHUNK_LIMIT, timeouts, error codes) |
|
|
228
|
+
| `fs-utils.ts` | Atomic file operations (temp file + rename) |
|
|
229
|
+
| `unicode-format.ts` | Markdown to Unicode Mathematical styled text |
|
|
230
|
+
| `channel.ts` | ChannelPlugin interface with security adapter (resolveDmPolicy, collectWarnings) |
|
|
231
|
+
| `runtime.ts` | OpenClaw runtime reference holder |
|
|
232
|
+
|
|
233
|
+
### State persistence
|
|
234
|
+
|
|
235
|
+
- **Sync cursors** — saved per KF account in `~/.openclaw/state/wechat-kf/wechat-kf-cursor-{kfid}.txt` (atomic writes)
|
|
236
|
+
- **Discovered KF IDs** — saved in `~/.openclaw/state/wechat-kf/wechat-kf-kfids.json` (atomic writes)
|
|
237
|
+
- **Access tokens** — in-memory only with hashed cache key (re-fetched on restart)
|
|
238
|
+
|
|
239
|
+
## Limitations / Known Issues
|
|
240
|
+
|
|
241
|
+
- **Open access by design** — WeChat Customer Service is inherently a public-facing service within the WeChat ecosystem. Anyone who obtains the KF contact link (URL or QR code) can send messages to your KF account — this cannot be prevented at the WeChat platform level. The plugin's `dmPolicy: "allowlist"` mode can restrict which users the agent actually responds to (non-allowlisted messages are silently dropped), but it cannot prevent unknown users from reaching the KF entry point itself. Please be aware of this public-facing nature when deploying in production.
|
|
242
|
+
- **48-hour reply window** — WeChat only allows replies within 48 hours of the user's last message. The plugin detects this (errcode 95026) and logs a clear warning.
|
|
243
|
+
- **5 messages per window** — you can send at most 5 replies before the user sends another message. The plugin detects this limit and logs accordingly.
|
|
244
|
+
- **Voice format** — inbound voice messages are AMR format; transcription depends on the OpenClaw agent's media processing capabilities.
|
|
245
|
+
- **Temporary media only** — uploaded media uses WeChat's temporary media API (3-day expiry). Permanent media upload is not implemented.
|
|
246
|
+
- **Single webhook endpoint** — all KF accounts share the same webhook port and path. This is by design (WeCom sends all callbacks to one URL per enterprise).
|
|
247
|
+
- **No group chat** — WeChat KF is direct messaging only. The plugin only supports `direct` chat type.
|
|
248
|
+
- **IP whitelist drift** — if your server's public IP changes, API calls will fail silently. Monitor your IP or use a static IP.
|
|
249
|
+
|
|
250
|
+
## Development
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
# Install dependencies
|
|
254
|
+
pnpm install
|
|
255
|
+
|
|
256
|
+
# Build
|
|
257
|
+
pnpm run build
|
|
258
|
+
|
|
259
|
+
# Type check
|
|
260
|
+
pnpm run typecheck
|
|
261
|
+
|
|
262
|
+
# Run tests (363 tests across 16 files)
|
|
263
|
+
pnpm test
|
|
264
|
+
|
|
265
|
+
# Watch mode
|
|
266
|
+
pnpm run test:watch
|
|
267
|
+
|
|
268
|
+
# Lint (Biome)
|
|
269
|
+
pnpm run lint
|
|
270
|
+
|
|
271
|
+
# Lint + auto-fix (Biome)
|
|
272
|
+
pnpm run lint:fix
|
|
273
|
+
|
|
274
|
+
# Format (Biome)
|
|
275
|
+
pnpm run format
|
|
276
|
+
|
|
277
|
+
# Combined Biome check (lint + format)
|
|
278
|
+
pnpm run check
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## Contributing
|
|
282
|
+
|
|
283
|
+
1. Fork the repository
|
|
284
|
+
2. Create a feature branch (`git checkout -b feature/my-feature`)
|
|
285
|
+
3. Make your changes and add tests
|
|
286
|
+
4. Run `pnpm run check && pnpm run typecheck && pnpm test` to verify
|
|
287
|
+
5. Submit a pull request
|
|
288
|
+
|
|
289
|
+
## License
|
|
290
|
+
|
|
291
|
+
MIT
|