@zhin.js/adapter-wechat-mp 0.1.0

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 凉菜
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,478 @@
1
+ # WeChat Official Account 适配器
2
+
3
+ 基于微信公众号开发者API的 Zhin 机器人适配器,支持接收和发送微信公众号消息。
4
+
5
+ ## 功能特性
6
+
7
+ - 🔌 **完整协议支持**: 支持微信公众号开发者模式
8
+ - 📨 **消息处理**: 支持文本、图片、语音、视频、地理位置等消息类型
9
+ - 🎯 **事件处理**: 支持关注、取关、菜单点击等事件
10
+ - 🔐 **安全验证**: 完整的签名验证和加密支持
11
+ - 🔄 **Token管理**: 自动获取和刷新access_token
12
+ - 💬 **双向通信**: 支持被动回复和主动推送
13
+ - 🎛️ **多媒体支持**: 支持图片、语音、视频等多媒体消息
14
+ - 🌐 **集成化**: 集成 zhin-next HTTP 服务,无需独立服务器
15
+
16
+ ## 安装
17
+
18
+ ```bash
19
+ pnpm add @zhin.js/adapter-wechat-mp
20
+ ```
21
+
22
+ ## 依赖库
23
+
24
+ 本适配器使用以下库:
25
+ - `xml2js` - XML解析和生成
26
+ - `axios` - HTTP请求
27
+ - `crypto` - 签名验证 (Node.js 内置)
28
+ - `@zhin.js/http` - HTTP 服务和路由 (peer dependency)
29
+
30
+ ## 前置准备
31
+
32
+ ### 1. 申请微信公众号
33
+
34
+ 1. 前往 [微信公众平台](https://mp.weixin.qq.com/) 注册账号
35
+ 2. 选择订阅号或服务号(服务号功能更丰富)
36
+ 3. 完成认证(可选,但认证后功能更多)
37
+
38
+ ### 2. 获取开发者信息
39
+
40
+ 1. 登录微信公众平台
41
+ 2. 进入「开发」->「基本配置」
42
+ 3. 获取以下信息:
43
+ - **AppID** (应用ID)
44
+ - **AppSecret** (应用密钥)
45
+ - 设置 **Token** (自定义,用于验证)
46
+ - 设置 **EncodingAESKey** (可选,用于加密)
47
+
48
+ ### 3. 配置服务器
49
+
50
+ 1. 在「基本配置」中设置服务器地址:
51
+ ```
52
+ URL: http://your-domain.com/wechat
53
+ Token: 你设置的token
54
+ ```
55
+ 2. 选择消息加解密方式(明文模式或安全模式)
56
+
57
+ ## 配置
58
+
59
+ ### 基础配置
60
+
61
+ ```typescript
62
+ import { WeChatMPConfig } from '@zhin.js/adapter-wechat-mp'
63
+
64
+ const wechatConfig: WeChatMPConfig = {
65
+ context: 'wechat-mp',
66
+ name: 'my-wechat-bot',
67
+ appId: 'your-app-id',
68
+ appSecret: 'your-app-secret',
69
+ token: 'your-token',
70
+ path: '/wechat'
71
+ }
72
+ ```
73
+
74
+ ### 完整配置
75
+
76
+ ```typescript
77
+ const wechatConfig: WeChatMPConfig = {
78
+ context: 'wechat-mp',
79
+ name: 'advanced-wechat-bot',
80
+ appId: 'wx1234567890abcdef',
81
+ appSecret: 'your-app-secret-key',
82
+ token: 'your-verification-token',
83
+ encodingAESKey: 'your-encoding-aes-key', // 加密模式需要
84
+ encrypt: false, // 是否启用加密模式
85
+ path: '/wechat/webhook' // Webhook路径(在 HTTP 服务上)
86
+ }
87
+ ```
88
+
89
+ > **注意**: 这个适配器依赖于 `@zhin.js/http` 插件。需要在配置中同时启用 HTTP 插件。
90
+
91
+ ## 使用示例
92
+
93
+ ### 基础使用
94
+
95
+ ```typescript
96
+ import { createApp } from 'zhin.js'
97
+ import WeChatMPAdapter from '@zhin.js/adapter-wechat-mp'
98
+
99
+ const app = createApp({
100
+ // 必须启用 HTTP 插件
101
+ plugins: ['@zhin.js/http'],
102
+ adapters: {
103
+ 'wechat-mp': {
104
+ context: 'wechat-mp',
105
+ name: 'my-wechat-bot',
106
+ appId: 'your-app-id',
107
+ appSecret: 'your-app-secret',
108
+ token: 'your-token',
109
+ path: '/wechat'
110
+ }
111
+ }
112
+ })
113
+
114
+ // 处理文本消息
115
+ app.on('message.receive', (message) => {
116
+ if (message.$adapter === 'wechat-mp') {
117
+ console.log('收到微信消息:', message.$content)
118
+
119
+ // 自动回复
120
+ message.$reply('感谢您的消息!')
121
+ }
122
+ })
123
+
124
+ // 处理关注事件
125
+ app.on('message.receive', (message) => {
126
+ if (message.$adapter === 'wechat-mp' &&
127
+ message.$content.some(seg => seg.type === 'event' && seg.data.event === 'subscribe')) {
128
+ message.$reply('欢迎关注我们的公众号!')
129
+ }
130
+ })
131
+
132
+ app.start()
133
+ ```
134
+
135
+ ### 发送不同类型的消息
136
+
137
+ ```typescript
138
+ // 发送文本消息
139
+ await app.sendMessage({
140
+ context: 'wechat-mp',
141
+ bot: 'my-wechat-bot',
142
+ id: 'user-openid',
143
+ type: 'private',
144
+ content: '这是一条文本消息'
145
+ })
146
+
147
+ // 发送图片消息
148
+ await app.sendMessage({
149
+ context: 'wechat-mp',
150
+ bot: 'my-wechat-bot',
151
+ id: 'user-openid',
152
+ type: 'private',
153
+ content: [
154
+ { type: 'image', data: { mediaId: 'uploaded-media-id' } }
155
+ ]
156
+ })
157
+
158
+ // 发送语音消息
159
+ await app.sendMessage({
160
+ context: 'wechat-mp',
161
+ bot: 'my-wechat-bot',
162
+ id: 'user-openid',
163
+ type: 'private',
164
+ content: [
165
+ { type: 'voice', data: { mediaId: 'voice-media-id' } }
166
+ ]
167
+ })
168
+ ```
169
+
170
+ ### 处理不同消息类型
171
+
172
+ ```typescript
173
+ app.on('message.receive', (message) => {
174
+ if (message.$adapter !== 'wechat-mp') return;
175
+
176
+ for (const segment of message.$content) {
177
+ switch (segment.type) {
178
+ case 'text':
179
+ console.log('文本消息:', segment.data.text);
180
+ break;
181
+
182
+ case 'image':
183
+ console.log('图片消息:', segment.data.url, segment.data.mediaId);
184
+ break;
185
+
186
+ case 'voice':
187
+ console.log('语音消息:', segment.data.mediaId, segment.data.recognition);
188
+ break;
189
+
190
+ case 'video':
191
+ console.log('视频消息:', segment.data.mediaId);
192
+ break;
193
+
194
+ case 'location':
195
+ console.log('位置消息:', segment.data.latitude, segment.data.longitude);
196
+ break;
197
+
198
+ case 'link':
199
+ console.log('链接消息:', segment.data.title, segment.data.url);
200
+ break;
201
+
202
+ case 'event':
203
+ console.log('事件:', segment.data.event, segment.data.eventKey);
204
+ break;
205
+ }
206
+ }
207
+ })
208
+ ```
209
+
210
+ ### 使用公众号API
211
+
212
+ ```typescript
213
+ import { WeChatMPBot } from '@zhin.js/adapter-wechat-mp'
214
+
215
+ // 获取bot实例
216
+ const bot = app.getContext('wechat-mp')?.['my-wechat-bot'] as WeChatMPBot;
217
+
218
+ // 获取用户信息
219
+ const userInfo = await bot.getUserInfo('user-openid');
220
+ console.log('用户信息:', userInfo);
221
+
222
+ // 上传多媒体文件
223
+ const mediaId = await bot.uploadMedia('image', imageBuffer);
224
+ console.log('媒体ID:', mediaId);
225
+ ```
226
+
227
+ ## 支持的消息类型
228
+
229
+ ### 接收消息
230
+
231
+ | 微信消息类型 | MessageSegment 类型 | 说明 |
232
+ |------------|-------------------|------|
233
+ | text | `text` | 文本消息 |
234
+ | image | `image` | 图片消息 |
235
+ | voice | `voice` | 语音消息 |
236
+ | video | `video` | 视频消息 |
237
+ | shortvideo | `video` | 小视频消息 |
238
+ | location | `location` | 地理位置消息 |
239
+ | link | `link` | 链接消息 |
240
+ | event | `event` | 事件消息 |
241
+
242
+ ### 发送消息
243
+
244
+ | MessageSegment 类型 | 微信API | 说明 |
245
+ |-------------------|---------|------|
246
+ | `text` | 客服消息 | 文本消息 |
247
+ | `image` | 客服消息 | 图片消息(需要mediaId) |
248
+ | `voice` | 客服消息 | 语音消息(需要mediaId) |
249
+ | `video` | 客服消息 | 视频消息(需要mediaId) |
250
+
251
+ ### 事件类型
252
+
253
+ | 事件类型 | 说明 |
254
+ |---------|------|
255
+ | subscribe | 关注事件 |
256
+ | unsubscribe | 取关事件 |
257
+ | CLICK | 菜单点击事件 |
258
+ | VIEW | 菜单链接事件 |
259
+ | LOCATION | 地理位置事件 |
260
+
261
+ ## 频道类型
262
+
263
+ | 类型 | 说明 | channel_id 格式 |
264
+ |------|------|----------------|
265
+ | `private` | 用户私聊 | 用户OpenID |
266
+
267
+ 注意:微信公众号只支持 `private` 类型,因为所有消息都是用户与公众号的私聊。
268
+
269
+ ## 开发模式设置
270
+
271
+ ### 1. 本地开发
272
+
273
+ 使用内网穿透工具(如ngrok):
274
+
275
+ ```bash
276
+ # 安装ngrok
277
+ npm install -g ngrok
278
+
279
+ # 启动内网穿透
280
+ ngrok http 3000
281
+
282
+ # 将生成的https地址设置为微信服务器URL
283
+ # 例如: https://abc123.ngrok.io/wechat
284
+ ```
285
+
286
+ ### 2. 服务器部署
287
+
288
+ ```typescript
289
+ // 生产环境配置
290
+ const wechatConfig = {
291
+ context: 'wechat-mp',
292
+ name: 'production-bot',
293
+ appId: process.env.WECHAT_APP_ID,
294
+ appSecret: process.env.WECHAT_APP_SECRET,
295
+ token: process.env.WECHAT_TOKEN,
296
+ port: process.env.PORT || 80,
297
+ path: '/wechat'
298
+ }
299
+ ```
300
+
301
+ ### 3. 使用Nginx反向代理
302
+
303
+ ```nginx
304
+ server {
305
+ listen 80;
306
+ server_name your-domain.com;
307
+
308
+ location /wechat {
309
+ proxy_pass http://localhost:3000/wechat;
310
+ proxy_set_header Host $host;
311
+ proxy_set_header X-Real-IP $remote_addr;
312
+ }
313
+ }
314
+ ```
315
+
316
+ ## 高级功能
317
+
318
+ ### 1. 自定义菜单
319
+
320
+ ```typescript
321
+ // 创建菜单需要通过微信API
322
+ const menuData = {
323
+ "button": [
324
+ {
325
+ "type": "click",
326
+ "name": "功能1",
327
+ "key": "MENU_KEY_1"
328
+ },
329
+ {
330
+ "type": "view",
331
+ "name": "官网",
332
+ "url": "https://your-website.com"
333
+ }
334
+ ]
335
+ }
336
+ ```
337
+
338
+ ### 2. 模板消息
339
+
340
+ ```typescript
341
+ // 发送模板消息
342
+ const templateMessage = {
343
+ touser: 'user-openid',
344
+ template_id: 'template-id',
345
+ data: {
346
+ first: { value: '标题' },
347
+ keyword1: { value: '内容1' },
348
+ keyword2: { value: '内容2' },
349
+ remark: { value: '备注' }
350
+ }
351
+ }
352
+ ```
353
+
354
+ ### 3. 素材管理
355
+
356
+ ```typescript
357
+ // 上传永久素材
358
+ const permanentMedia = await bot.uploadPermanentMedia('image', buffer);
359
+
360
+ // 获取素材列表
361
+ const mediaList = await bot.getMediaList('image', 0, 20);
362
+ ```
363
+
364
+ ## 错误处理
365
+
366
+ ### 常见错误码
367
+
368
+ | 错误码 | 说明 | 解决方案 |
369
+ |--------|------|----------|
370
+ | 40001 | AppSecret错误 | 检查AppSecret配置 |
371
+ | 40002 | 不合法的凭证类型 | 检查access_token |
372
+ | 40003 | 不合法的OpenID | 检查用户OpenID |
373
+ | 40004 | 不合法的媒体文件类型 | 检查上传文件格式 |
374
+ | 40013 | 不合法的AppID | 检查AppID配置 |
375
+ | 42001 | access_token超时 | 会自动刷新token |
376
+
377
+ ### 调试建议
378
+
379
+ 1. **检查签名验证**:
380
+ ```javascript
381
+ // 验证微信服务器配置时的日志
382
+ console.log('Signature verification:', { signature, timestamp, nonce });
383
+ ```
384
+
385
+ 2. **查看XML消息**:
386
+ ```javascript
387
+ // 打印接收到的原始XML
388
+ console.log('Received XML:', xmlBody);
389
+ ```
390
+
391
+ 3. **监控Token状态**:
392
+ ```javascript
393
+ // 定期检查token有效性
394
+ console.log('Access Token:', this.accessToken);
395
+ console.log('Expires at:', new Date(this.tokenExpireTime));
396
+ ```
397
+
398
+ ## 安全注意事项
399
+
400
+ 1. **保护敏感信息**: 不要在代码中硬编码AppSecret
401
+ 2. **使用HTTPS**: 生产环境建议使用HTTPS
402
+ 3. **签名验证**: 始终验证微信请求的签名
403
+ 4. **加密通信**: 敏感场景可启用消息加密
404
+ 5. **频率限制**: 注意微信API的调用频率限制
405
+
406
+ ## API限制
407
+
408
+ 1. **消息发送**: 每天主动推送消息有限制
409
+ 2. **API调用**: 大部分API每分钟调用次数有限制
410
+ 3. **媒体上传**: 临时素材3天后失效
411
+ 4. **用户信息**: 只能获取关注用户的信息
412
+
413
+ ## 故障排除
414
+
415
+ ### 1. 服务器验证失败
416
+ - 检查Token配置是否正确
417
+ - 确认URL可以正常访问
418
+ - 检查签名算法实现
419
+
420
+ ### 2. 消息接收异常
421
+ - 检查HTTP服务器是否正常启动
422
+ - 验证防火墙和端口配置
423
+ - 查看微信开发者工具的错误日志
424
+
425
+ ### 3. 消息发送失败
426
+ - 检查access_token是否有效
427
+ - 确认用户已关注公众号
428
+ - 检查消息格式是否正确
429
+
430
+ ### 4. 多媒体消息问题
431
+ - 确认文件格式和大小符合要求
432
+ - 检查mediaId是否有效
433
+ - 验证文件上传是否成功
434
+
435
+ ## 示例项目
436
+
437
+ 完整的使用示例可以在 `example/` 目录中找到。
438
+
439
+ ## API 参考
440
+
441
+ ### WeChatMPBot 类
442
+
443
+ #### 配置选项
444
+
445
+ ```typescript
446
+ interface WeChatMPConfig {
447
+ context: 'wechat-mp'
448
+ name: string
449
+ appId: string
450
+ appSecret: string
451
+ token: string
452
+ encodingAESKey?: string
453
+ port?: number
454
+ path?: string
455
+ encrypt?: boolean
456
+ }
457
+ ```
458
+
459
+ #### 主要方法
460
+
461
+ - `$connect()`: 启动HTTP服务器和Token管理
462
+ - `$disconnect()`: 关闭服务器连接
463
+ - `$formatMessage(wechatMsg)`: 格式化微信消息
464
+ - `$sendMessage(options)`: 发送消息
465
+ - `getUserInfo(openid)`: 获取用户信息
466
+ - `uploadMedia(type, buffer)`: 上传多媒体文件
467
+
468
+ #### 事件
469
+
470
+ - `message.receive`: 接收到微信消息时触发
471
+
472
+ ## 更新日志
473
+
474
+ ### v0.1.0
475
+ - 支持基础消息收发
476
+ - 实现签名验证和Token管理
477
+ - 支持多种消息类型和事件
478
+ - 提供完整的API封装
package/lib/index.d.ts ADDED
@@ -0,0 +1,73 @@
1
+ import { EventEmitter } from 'events';
2
+ import { Bot, BotConfig, Adapter, Plugin, Message, SendOptions, MessageSegment } from "zhin.js";
3
+ import type { Context } from 'koa';
4
+ interface Router {
5
+ get(path: string, handler: (ctx: Context) => void): void;
6
+ post(path: string, handler: (ctx: Context) => void): void;
7
+ }
8
+ declare module 'zhin.js' {
9
+ interface RegisteredAdapters {
10
+ 'wechat-mp': Adapter<WeChatMPBot>;
11
+ }
12
+ }
13
+ export interface WeChatMPConfig extends BotConfig {
14
+ context: 'wechat-mp';
15
+ name: string;
16
+ appId: string;
17
+ appSecret: string;
18
+ token: string;
19
+ encodingAESKey?: string;
20
+ path: string;
21
+ encrypt?: boolean;
22
+ }
23
+ export interface WeChatMessage {
24
+ ToUserName: string;
25
+ FromUserName: string;
26
+ CreateTime: number;
27
+ MsgType: string;
28
+ MsgId?: string;
29
+ Content?: string;
30
+ PicUrl?: string;
31
+ MediaId?: string;
32
+ Format?: string;
33
+ Recognition?: string;
34
+ ThumbMediaId?: string;
35
+ Location_X?: string;
36
+ Location_Y?: string;
37
+ Scale?: string;
38
+ Label?: string;
39
+ Title?: string;
40
+ Description?: string;
41
+ Url?: string;
42
+ Event?: string;
43
+ EventKey?: string;
44
+ }
45
+ export declare class WeChatMPBot extends EventEmitter implements Bot<WeChatMessage, WeChatMPConfig> {
46
+ $config: WeChatMPConfig;
47
+ plugin: Plugin;
48
+ router: Router;
49
+ private accessToken;
50
+ private tokenExpireTime;
51
+ private isConnected;
52
+ constructor(plugin: Plugin, router: Router, config: WeChatMPConfig);
53
+ private setupRoutes;
54
+ $connect(): Promise<void>;
55
+ $disconnect(): Promise<void>;
56
+ private handleVerification;
57
+ private handleMessage;
58
+ private verifySignature;
59
+ private parseXMLMessage;
60
+ $formatMessage(wechatMsg: WeChatMessage): Message<WeChatMessage>;
61
+ static parseMessageContent(wechatMsg: WeChatMessage): MessageSegment[];
62
+ $sendMessage(options: SendOptions): Promise<void>;
63
+ private sendCustomerServiceMessage;
64
+ private formatSendContent;
65
+ private handlePassiveReply;
66
+ private buildTextReply;
67
+ private refreshAccessToken;
68
+ private startTokenRefreshTimer;
69
+ getUserInfo(openid: string): Promise<any>;
70
+ uploadMedia(type: 'image' | 'voice' | 'video' | 'thumb', buffer: Buffer): Promise<string>;
71
+ }
72
+ export {};
73
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/lib/index.js ADDED
@@ -0,0 +1,363 @@
1
+ import axios from 'axios';
2
+ import * as xml2js from 'xml2js';
3
+ import { createHash } from 'crypto';
4
+ import { EventEmitter } from 'events';
5
+ import { Adapter, registerAdapter, Message, segment, useContext } from "zhin.js";
6
+ export class WeChatMPBot extends EventEmitter {
7
+ $config;
8
+ plugin;
9
+ router;
10
+ accessToken = null;
11
+ tokenExpireTime = 0;
12
+ isConnected = false;
13
+ constructor(plugin, router, config) {
14
+ super();
15
+ this.$config = config;
16
+ this.plugin = plugin;
17
+ this.router = router;
18
+ // 设置默认值
19
+ this.$config.encrypt = this.$config.encrypt || false;
20
+ }
21
+ setupRoutes() {
22
+ const path = this.$config.path;
23
+ // 微信服务器验证 (GET)
24
+ this.router.get(path, (ctx) => {
25
+ this.handleVerification(ctx);
26
+ });
27
+ // 接收微信消息 (POST)
28
+ this.router.post(path, (ctx) => {
29
+ this.handleMessage(ctx);
30
+ });
31
+ }
32
+ async $connect() {
33
+ try {
34
+ // 获取access_token
35
+ await this.refreshAccessToken();
36
+ // 设置路由
37
+ this.setupRoutes();
38
+ // 定期刷新access_token
39
+ this.startTokenRefreshTimer();
40
+ this.isConnected = true;
41
+ this.plugin.logger.info(`WeChat MP bot connected: ${this.$config.name}`);
42
+ this.plugin.logger.info(`Webhook URL: ${this.$config.path}`);
43
+ }
44
+ catch (error) {
45
+ this.plugin.logger.error('Failed to connect WeChat MP bot:', error);
46
+ throw error;
47
+ }
48
+ }
49
+ async $disconnect() {
50
+ this.isConnected = false;
51
+ this.plugin.logger.info('WeChat MP bot disconnected');
52
+ }
53
+ handleVerification(ctx) {
54
+ const { signature, timestamp, nonce, echostr } = ctx.query;
55
+ if (this.verifySignature(signature, timestamp, nonce)) {
56
+ this.plugin.logger.info('WeChat verification successful');
57
+ ctx.body = echostr;
58
+ }
59
+ else {
60
+ this.plugin.logger.error('WeChat verification failed');
61
+ ctx.status = 403;
62
+ ctx.body = 'Forbidden';
63
+ }
64
+ }
65
+ async handleMessage(ctx) {
66
+ try {
67
+ const { signature, timestamp, nonce } = ctx.query;
68
+ // 验证签名
69
+ if (!this.verifySignature(signature, timestamp, nonce)) {
70
+ this.plugin.logger.error('Invalid signature');
71
+ ctx.status = 403;
72
+ ctx.body = 'Forbidden';
73
+ return;
74
+ }
75
+ // 获取原始XML数据
76
+ const xmlBody = ctx.request.rawBody || ctx.body;
77
+ const wechatMessage = await this.parseXMLMessage(xmlBody?.toString() || '');
78
+ if (wechatMessage) {
79
+ const message = this.$formatMessage(wechatMessage);
80
+ this.plugin.dispatch('message.receive', message);
81
+ // 处理被动回复
82
+ const replyXML = await this.handlePassiveReply(wechatMessage, message);
83
+ ctx.set('Content-Type', 'text/xml');
84
+ ctx.body = replyXML || 'success';
85
+ }
86
+ else {
87
+ ctx.body = 'success';
88
+ }
89
+ }
90
+ catch (error) {
91
+ this.plugin.logger.error('Error handling WeChat message:', error);
92
+ ctx.body = 'success';
93
+ }
94
+ }
95
+ verifySignature(signature, timestamp, nonce) {
96
+ const token = this.$config.token;
97
+ const arr = [token, timestamp, nonce].sort();
98
+ const str = arr.join('');
99
+ const hash = createHash('sha1').update(str).digest('hex');
100
+ return hash === signature;
101
+ }
102
+ async parseXMLMessage(xmlString) {
103
+ try {
104
+ const parser = new xml2js.Parser({ explicitArray: false, ignoreAttrs: true });
105
+ const result = await parser.parseStringPromise(xmlString);
106
+ return result.xml;
107
+ }
108
+ catch (error) {
109
+ this.plugin.logger.error('Error parsing XML:', error);
110
+ return null;
111
+ }
112
+ }
113
+ $formatMessage(wechatMsg) {
114
+ const channelType = 'private'; // 公众号消息都是私聊
115
+ const channelId = wechatMsg.FromUserName;
116
+ // 解析消息内容
117
+ const content = WeChatMPBot.parseMessageContent(wechatMsg);
118
+ const result = Message.from(wechatMsg, {
119
+ $id: wechatMsg.MsgId || `${wechatMsg.CreateTime}`,
120
+ $adapter: 'wechat-mp',
121
+ $bot: this.$config.name,
122
+ $sender: {
123
+ id: wechatMsg.FromUserName,
124
+ name: wechatMsg.FromUserName
125
+ },
126
+ $channel: {
127
+ id: channelId,
128
+ type: channelType
129
+ },
130
+ $raw: JSON.stringify(wechatMsg),
131
+ $timestamp: wechatMsg.CreateTime * 1000,
132
+ $content: content,
133
+ $reply: async (content) => {
134
+ this.plugin.dispatch('message.send', {
135
+ context: this.$config.context,
136
+ bot: this.$config.name,
137
+ id: wechatMsg.FromUserName,
138
+ type: 'private',
139
+ content
140
+ });
141
+ }
142
+ });
143
+ return result;
144
+ }
145
+ static parseMessageContent(wechatMsg) {
146
+ const segments = [];
147
+ switch (wechatMsg.MsgType) {
148
+ case 'text':
149
+ if (wechatMsg.Content) {
150
+ segments.push(segment.text(wechatMsg.Content));
151
+ }
152
+ break;
153
+ case 'image':
154
+ segments.push(segment('image', {
155
+ url: wechatMsg.PicUrl,
156
+ mediaId: wechatMsg.MediaId
157
+ }));
158
+ break;
159
+ case 'voice':
160
+ segments.push(segment('voice', {
161
+ mediaId: wechatMsg.MediaId,
162
+ format: wechatMsg.Format,
163
+ recognition: wechatMsg.Recognition
164
+ }));
165
+ break;
166
+ case 'video':
167
+ case 'shortvideo':
168
+ segments.push(segment('video', {
169
+ mediaId: wechatMsg.MediaId,
170
+ thumbMediaId: wechatMsg.ThumbMediaId
171
+ }));
172
+ break;
173
+ case 'location':
174
+ segments.push(segment('location', {
175
+ latitude: wechatMsg.Location_X,
176
+ longitude: wechatMsg.Location_Y,
177
+ scale: wechatMsg.Scale,
178
+ label: wechatMsg.Label
179
+ }));
180
+ break;
181
+ case 'link':
182
+ segments.push(segment('link', {
183
+ title: wechatMsg.Title,
184
+ description: wechatMsg.Description,
185
+ url: wechatMsg.Url
186
+ }));
187
+ break;
188
+ case 'event':
189
+ segments.push(segment('event', {
190
+ event: wechatMsg.Event,
191
+ eventKey: wechatMsg.EventKey
192
+ }));
193
+ break;
194
+ default:
195
+ segments.push(segment.text(`[不支持的消息类型: ${wechatMsg.MsgType}]`));
196
+ }
197
+ return segments.length > 0 ? segments : [segment.text('(空消息)')];
198
+ }
199
+ async $sendMessage(options) {
200
+ try {
201
+ // 公众号主动发送消息需要通过模板消息或客服消息API
202
+ await this.sendCustomerServiceMessage(options);
203
+ }
204
+ catch (error) {
205
+ this.plugin.logger.error('Failed to send WeChat message:', error);
206
+ throw error;
207
+ }
208
+ }
209
+ async sendCustomerServiceMessage(options) {
210
+ if (!this.accessToken) {
211
+ await this.refreshAccessToken();
212
+ }
213
+ const url = `https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=${this.accessToken}`;
214
+ const messageData = this.formatSendContent(options);
215
+ const response = await axios.post(url, messageData);
216
+ const result = response.data;
217
+ if (result.errcode && result.errcode !== 0) {
218
+ throw new Error(`WeChat API error: ${result.errcode} - ${result.errmsg}`);
219
+ }
220
+ }
221
+ formatSendContent(options) {
222
+ const messageData = {
223
+ touser: options.id,
224
+ msgtype: 'text',
225
+ text: {
226
+ content: ''
227
+ }
228
+ };
229
+ if (typeof options.content === 'string') {
230
+ messageData.text.content = options.content;
231
+ }
232
+ else if (Array.isArray(options.content)) {
233
+ const textParts = [];
234
+ let hasMedia = false;
235
+ for (const item of options.content) {
236
+ if (typeof item === 'string') {
237
+ textParts.push(item);
238
+ }
239
+ else {
240
+ const segment = item;
241
+ switch (segment.type) {
242
+ case 'text':
243
+ const textContent = segment.data.text || segment.data.content || '';
244
+ textParts.push(textContent);
245
+ break;
246
+ case 'image':
247
+ if (!hasMedia && segment.data.mediaId) {
248
+ messageData.msgtype = 'image';
249
+ messageData.image = { media_id: segment.data.mediaId };
250
+ delete messageData.text;
251
+ hasMedia = true;
252
+ }
253
+ break;
254
+ case 'voice':
255
+ if (!hasMedia && segment.data.mediaId) {
256
+ messageData.msgtype = 'voice';
257
+ messageData.voice = { media_id: segment.data.mediaId };
258
+ delete messageData.text;
259
+ hasMedia = true;
260
+ }
261
+ break;
262
+ case 'video':
263
+ if (!hasMedia && segment.data.mediaId) {
264
+ messageData.msgtype = 'video';
265
+ messageData.video = {
266
+ media_id: segment.data.mediaId,
267
+ title: segment.data.title || '',
268
+ description: segment.data.description || ''
269
+ };
270
+ delete messageData.text;
271
+ hasMedia = true;
272
+ }
273
+ break;
274
+ }
275
+ }
276
+ }
277
+ if (!hasMedia && textParts.length > 0) {
278
+ messageData.text.content = textParts.join('\n');
279
+ }
280
+ }
281
+ return messageData;
282
+ }
283
+ async handlePassiveReply(wechatMsg, message) {
284
+ // 事件类型消息的自动回复
285
+ if (wechatMsg.MsgType === 'event') {
286
+ switch (wechatMsg.Event) {
287
+ case 'subscribe':
288
+ return this.buildTextReply(wechatMsg, '感谢关注!');
289
+ case 'unsubscribe':
290
+ // 取关事件不能回复消息
291
+ return '';
292
+ }
293
+ }
294
+ // 这里可以添加自定义的被动回复逻辑
295
+ // 返回空字符串表示不回复
296
+ return '';
297
+ }
298
+ buildTextReply(wechatMsg, content) {
299
+ const replyMsg = {
300
+ xml: {
301
+ ToUserName: wechatMsg.FromUserName,
302
+ FromUserName: wechatMsg.ToUserName,
303
+ CreateTime: Math.floor(Date.now() / 1000),
304
+ MsgType: 'text',
305
+ Content: content
306
+ }
307
+ };
308
+ const builder = new xml2js.Builder({ rootName: 'xml', headless: true });
309
+ return builder.buildObject(replyMsg);
310
+ }
311
+ async refreshAccessToken() {
312
+ const url = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${this.$config.appId}&secret=${this.$config.appSecret}`;
313
+ try {
314
+ const response = await axios.get(url);
315
+ const data = response.data;
316
+ if (data.access_token) {
317
+ this.accessToken = data.access_token;
318
+ this.tokenExpireTime = Date.now() + (data.expires_in - 300) * 1000; // 提前5分钟刷新
319
+ this.plugin.logger.info('Access token refreshed successfully');
320
+ }
321
+ else {
322
+ throw new Error('Failed to get access token');
323
+ }
324
+ }
325
+ catch (error) {
326
+ this.plugin.logger.error('Failed to refresh access token:', error);
327
+ throw error;
328
+ }
329
+ }
330
+ startTokenRefreshTimer() {
331
+ // 每小时检查一次token是否需要刷新
332
+ setInterval(async () => {
333
+ if (Date.now() >= this.tokenExpireTime) {
334
+ try {
335
+ await this.refreshAccessToken();
336
+ }
337
+ catch (error) {
338
+ this.plugin.logger.error('Failed to refresh access token in timer:', error);
339
+ }
340
+ }
341
+ }, 3600000); // 1小时
342
+ }
343
+ // 获取用户信息
344
+ async getUserInfo(openid) {
345
+ if (!this.accessToken) {
346
+ await this.refreshAccessToken();
347
+ }
348
+ const url = `https://api.weixin.qq.com/cgi-bin/user/info?access_token=${this.accessToken}&openid=${openid}&lang=zh_CN`;
349
+ const response = await axios.get(url);
350
+ return response.data;
351
+ }
352
+ // 上传多媒体文件 (暂时禁用,需要处理 FormData 兼容性)
353
+ async uploadMedia(type, buffer) {
354
+ // TODO: 实现文件上传功能
355
+ // 需要处理 Node.js FormData 与浏览器 FormData 的兼容性问题
356
+ throw new Error('Media upload feature is not implemented yet');
357
+ }
358
+ }
359
+ // 使用路由服务注册适配器
360
+ useContext('router', (router) => {
361
+ registerAdapter(new Adapter('wechat-mp', (plugin, config) => new WeChatMPBot(plugin, router, config)));
362
+ });
363
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@zhin.js/adapter-wechat-mp",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "WeChat Official Account adapter for zhin.js",
6
+ "main": "lib/index.js",
7
+ "types": "lib/index.d.ts",
8
+ "dependencies": {
9
+ "xml2js": "^0.6.2",
10
+ "axios": "^1.6.2",
11
+ "@types/xml2js": "^0.4.14"
12
+ },
13
+ "peerDependencies": {
14
+ "@zhin.js/types": "^1.0.1",
15
+ "@zhin.js/http": "^1.0.1",
16
+ "zhin.js": "^1.0.1"
17
+ },
18
+ "devDependencies": {
19
+ "typescript": "^5.3.3",
20
+ "@types/node": "^20.10.6",
21
+ "@types/koa": "^2.15.0"
22
+ },
23
+ "keywords": [
24
+ "zhin",
25
+ "adapter",
26
+ "wechat",
27
+ "weixin",
28
+ "official-account",
29
+ "mp"
30
+ ],
31
+ "author": "",
32
+ "license": "MIT",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "https://github.com/zhinjs/zhin-next",
36
+ "directory": "adapters/wechat-mp"
37
+ },
38
+ "files": [
39
+ "lib"
40
+ ],
41
+ "scripts": {
42
+ "build": "tsc",
43
+ "dev": "tsc --watch",
44
+ "clean": "rm -rf lib"
45
+ }
46
+ }