wx2tg-puppet 2.0.9 → 2.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/dist/core/Room.js CHANGED
@@ -56,7 +56,7 @@ class Room {
56
56
  */
57
57
  static fetchAndUpsertRoomDetail(chatroomId) {
58
58
  return __awaiter(this, void 0, void 0, function* () {
59
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
59
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
60
60
  const config = yield config_service_1.configService.get();
61
61
  if (!config)
62
62
  return null;
@@ -80,7 +80,7 @@ class Room {
80
80
  chatRoomNotify: (_j = respData.ChatRoomNotify) !== null && _j !== void 0 ? _j : 0,
81
81
  chatRoomOwner: respData.ChatRoomOwner || '',
82
82
  smallHeadImgUrl: respData.SmallHeadImgUrl || '',
83
- memberList: JSON.stringify(((_k = respData.NewChatroomData) === null || _k === void 0 ? void 0 : _k.ChatRoomMember.map((item) => item.UserName)) || [])
83
+ memberList: JSON.stringify(((_l = (_k = respData.NewChatroomData) === null || _k === void 0 ? void 0 : _k.ChatRoomMember) === null || _l === void 0 ? void 0 : _l.map((item) => item.UserName)) || [])
84
84
  };
85
85
  yield room_service_1.roomService.upsertByChatroomId(roomEntity.chatroomId, roomEntity);
86
86
  return roomEntity;
package/dist/index.d.ts CHANGED
@@ -13,18 +13,24 @@ export interface WxBotOptions {
13
13
  baseUrl: string;
14
14
  databasePath: string;
15
15
  deviceType: 'ipad' | 'car' | 'mac' | 'pad' | 'win' | 'ipadX' | string;
16
+ mode?: 'polling' | 'callback' | string;
17
+ callbackPort?: number;
16
18
  }
17
19
  export declare class WxBot extends EventEmitter {
18
20
  baseUrl: string;
19
21
  databasePath: string;
20
22
  deviceType: 'ipad' | 'car' | 'mac' | 'pad' | 'win' | 'ipadX' | string;
23
+ mode: 'polling' | 'callback' | string;
24
+ callbackPort: number;
21
25
  private uuid;
22
26
  private interval;
23
27
  private heartbeatInterval;
28
+ private callbackInterval;
24
29
  private msgInterval;
25
30
  private recentMsgIds;
26
31
  private syncFailureCount;
27
- constructor({ baseUrl, databasePath, deviceType }: WxBotOptions);
32
+ private httpServer;
33
+ constructor({ baseUrl, databasePath, deviceType, mode, callbackPort }: WxBotOptions);
28
34
  qrcode(style?: number): Promise<{
29
35
  qrCode: any;
30
36
  } | null>;
@@ -34,6 +40,10 @@ export declare class WxBot extends EventEmitter {
34
40
  info(): Promise<Contact | null>;
35
41
  private clearAllIntervals;
36
42
  private loginSuccess;
43
+ private startCallbackServer;
44
+ private handleCallbackMessage;
45
+ private startPollingMode;
46
+ private handleMsg;
37
47
  private logOut;
38
48
  /**
39
49
  * 清空登录信息
package/dist/index.js CHANGED
@@ -1,4 +1,37 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
36
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
37
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -43,18 +76,23 @@ const UserApi_1 = require("./api/UserApi");
43
76
  const ContactEntity_1 = require("./entities/ContactEntity");
44
77
  const room_service_1 = require("./services/room.service");
45
78
  const message_service_1 = require("./services/message.service");
79
+ const http = __importStar(require("http"));
46
80
  class WxBot extends events_1.EventEmitter {
47
- constructor({ baseUrl, databasePath, deviceType = 'ipad' }) {
81
+ constructor({ baseUrl, databasePath, deviceType = 'ipad', mode = 'polling', callbackPort = 8056 }) {
48
82
  super();
49
83
  this.uuid = '';
50
84
  this.interval = null;
51
85
  this.heartbeatInterval = null;
86
+ this.callbackInterval = null;
52
87
  this.msgInterval = null;
53
88
  this.recentMsgIds = []; // Store recent message IDs
54
89
  this.syncFailureCount = 0; // Sync failure counter
90
+ this.httpServer = null; // HTTP回调服务器
55
91
  this.baseUrl = baseUrl;
56
92
  this.databasePath = databasePath;
57
93
  this.deviceType = deviceType;
94
+ this.mode = mode;
95
+ this.callbackPort = callbackPort;
58
96
  (0, axiosInstance_1.setBaseUrl)(baseUrl);
59
97
  }
60
98
  qrcode(style) {
@@ -196,6 +234,10 @@ class WxBot extends events_1.EventEmitter {
196
234
  return __awaiter(this, void 0, void 0, function* () {
197
235
  var _a;
198
236
  try {
237
+ if (!this.uuid) {
238
+ yield this.fetchAndShowQRCode();
239
+ return;
240
+ }
199
241
  const statusResp = yield LoginApi_1.loginApi.LoginCheckQR(this.uuid);
200
242
  console.log('CheckQR response:', statusResp.data);
201
243
  const statusData = statusResp.data;
@@ -246,6 +288,14 @@ class WxBot extends events_1.EventEmitter {
246
288
  clearInterval(this.msgInterval);
247
289
  this.msgInterval = null;
248
290
  }
291
+ if (this.callbackInterval) {
292
+ clearInterval(this.callbackInterval);
293
+ this.callbackInterval = null;
294
+ }
295
+ if (this.httpServer) {
296
+ this.httpServer.close();
297
+ this.httpServer = null;
298
+ }
249
299
  }
250
300
  loginSuccess(userName) {
251
301
  return __awaiter(this, void 0, void 0, function* () {
@@ -285,113 +335,244 @@ class WxBot extends events_1.EventEmitter {
285
335
  });
286
336
  // 设置心跳包
287
337
  this.setupHeartbeat(userName);
288
- // 防止重复定时
289
- if (this.msgInterval) {
290
- clearInterval(this.msgInterval);
291
- this.msgInterval = null;
292
- }
293
- // 监听消息,每秒掉一次
294
- this.msgInterval = setInterval(() => __awaiter(this, void 0, void 0, function* () {
295
- var _a;
296
- const resp = yield MsgApi_1.msgApi.Sync({
297
- "Wxid": userName,
298
- "Scene": 0,
299
- "Synckey": ""
300
- });
301
- const data = resp.data;
302
- if (data.Code !== 0) {
303
- // Code不为0,增加失败计数
304
- this.syncFailureCount++;
305
- if (this.syncFailureCount >= 3) {
306
- // 连续失败3次,退出登录
307
- this.syncFailureCount = 0; // 重置计数器
338
+ // 根据模式选择消息监听方式
339
+ if (this.mode === 'callback') {
340
+ // 循环调用至无新消息
341
+ while (true) {
342
+ const resp = yield MsgApi_1.msgApi.Sync({
343
+ "Wxid": userName,
344
+ "Scene": 0,
345
+ "Synckey": ""
346
+ });
347
+ if (resp.data.Code !== 0) {
348
+ // Code不为0,增加失败计数
349
+ this.syncFailureCount++;
350
+ if (this.syncFailureCount >= 10) {
351
+ // 连续失败10次,退出登录
352
+ this.syncFailureCount = 0; // 重置计数器
353
+ // 先尝试二次登录
354
+ const loginTwice = yield LoginApi_1.loginApi.LoginTwiceAutoAuth(userName);
355
+ if (loginTwice.data.Message === '登录成功') {
356
+ return;
357
+ }
358
+ yield this.logOut();
359
+ yield this.login();
360
+ return;
361
+ }
362
+ // 失败次数不足3次,继续等待下次sync
363
+ return;
364
+ }
365
+ if (resp.data.Message === '用户可能退出') {
366
+ // 先尝试二次登录
367
+ const loginTwice = yield LoginApi_1.loginApi.LoginTwiceAutoAuth(userName);
368
+ if (loginTwice.data.Message === '登录成功') {
369
+ return;
370
+ }
371
+ // 用户可能退出,立即退出登录
308
372
  yield this.logOut();
309
373
  yield this.login();
310
374
  return;
311
375
  }
312
- // 失败次数不足3次,继续等待下次sync
313
- return;
376
+ // 成功情况,重置失败计数器
377
+ this.syncFailureCount = 0;
378
+ if (resp.data.Success) {
379
+ if (resp.data.Message === '当前未有新消息') {
380
+ break;
381
+ }
382
+ }
314
383
  }
315
- if (data.Message === '用户可能退出') {
316
- // 用户可能退出,立即退出登录
384
+ this.callbackInterval = setInterval(() => {
385
+ this.handleCallbackMessage(userName);
386
+ }, 1000 * 60 * 5);
387
+ this.startCallbackServer(userName);
388
+ }
389
+ else {
390
+ this.startPollingMode(userName);
391
+ }
392
+ });
393
+ }
394
+ startCallbackServer(userName) {
395
+ if (this.httpServer) {
396
+ this.httpServer.close();
397
+ this.httpServer = null;
398
+ }
399
+ this.httpServer = http.createServer((req, res) => {
400
+ // 设置CORS头
401
+ res.setHeader('Access-Control-Allow-Origin', '*');
402
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
403
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
404
+ if (req.method === 'OPTIONS') {
405
+ res.writeHead(200);
406
+ res.end();
407
+ return;
408
+ }
409
+ const url = req.url || '';
410
+ const callbackPattern = `/message/callback/${userName}`;
411
+ if (req.method === 'POST' && url === callbackPattern) {
412
+ let body = '';
413
+ let chunks = [];
414
+ req.on('data', (chunk) => {
415
+ chunks.push(chunk);
416
+ body += chunk.toString('utf8');
417
+ });
418
+ req.on('end', () => {
419
+ try {
420
+ this.handleCallbackMessage(userName);
421
+ res.writeHead(200, { 'Content-Type': 'application/json' });
422
+ res.end(JSON.stringify({ success: true }));
423
+ }
424
+ catch (error) {
425
+ console.error('JSON解析错误:', error);
426
+ console.error('原始内容:', body);
427
+ res.writeHead(400, { 'Content-Type': 'application/json' });
428
+ res.end(JSON.stringify({ error: 'Invalid JSON', body: body }));
429
+ }
430
+ });
431
+ req.on('error', (error) => {
432
+ console.error('请求处理错误:', error);
433
+ res.writeHead(500, { 'Content-Type': 'application/json' });
434
+ res.end(JSON.stringify({ error: 'Request error' }));
435
+ });
436
+ }
437
+ else {
438
+ res.writeHead(404, { 'Content-Type': 'application/json' });
439
+ res.end(JSON.stringify({ error: 'Not Found' }));
440
+ }
441
+ });
442
+ this.httpServer.listen(this.callbackPort, '0.0.0.0', () => {
443
+ console.log(`回调服务器启动成功,监听地址: http://0.0.0.0:${this.callbackPort}${`/message/callback/${userName}`}`);
444
+ });
445
+ this.httpServer.on('error', (error) => {
446
+ console.error('回调服务器启动失败:', error);
447
+ });
448
+ }
449
+ handleCallbackMessage(userName) {
450
+ return __awaiter(this, void 0, void 0, function* () {
451
+ yield this.handleMsg(userName);
452
+ });
453
+ }
454
+ startPollingMode(userName) {
455
+ // 防止重复定时
456
+ if (this.msgInterval) {
457
+ clearInterval(this.msgInterval);
458
+ this.msgInterval = null;
459
+ }
460
+ // 监听消息,每秒掉一次
461
+ this.msgInterval = setInterval(() => __awaiter(this, void 0, void 0, function* () {
462
+ yield this.handleMsg(userName);
463
+ }), 1000);
464
+ this.emit('login', userName);
465
+ }
466
+ handleMsg(userName) {
467
+ return __awaiter(this, void 0, void 0, function* () {
468
+ var _a;
469
+ const resp = yield MsgApi_1.msgApi.Sync({
470
+ "Wxid": userName,
471
+ "Scene": 0,
472
+ "Synckey": ""
473
+ });
474
+ const data = resp.data;
475
+ if (data.Code !== 0) {
476
+ // Code不为0,增加失败计数
477
+ this.syncFailureCount++;
478
+ if (this.syncFailureCount >= 10) {
479
+ // 连续失败10次,退出登录
480
+ this.syncFailureCount = 0; // 重置计数器
481
+ // 先尝试二次登录
482
+ const loginTwice = yield LoginApi_1.loginApi.LoginTwiceAutoAuth(userName);
483
+ if (loginTwice.data.Message === '登录成功') {
484
+ return;
485
+ }
317
486
  yield this.logOut();
318
487
  yield this.login();
319
488
  return;
320
489
  }
321
- // 成功情况,重置失败计数器
322
- this.syncFailureCount = 0;
323
- if (data.Success) {
324
- if (data.Message === '当前未有新消息') {
325
- return;
490
+ // 失败次数不足3次,继续等待下次sync
491
+ return;
492
+ }
493
+ if (data.Message === '用户可能退出') {
494
+ // 先尝试二次登录
495
+ const loginTwice = yield LoginApi_1.loginApi.LoginTwiceAutoAuth(userName);
496
+ if (loginTwice.data.Message === '登录成功') {
497
+ return;
498
+ }
499
+ // 用户可能退出,立即退出登录
500
+ yield this.logOut();
501
+ yield this.login();
502
+ return;
503
+ }
504
+ // 成功情况,重置失败计数器
505
+ this.syncFailureCount = 0;
506
+ if (data.Success) {
507
+ if (data.Message === '当前未有新消息') {
508
+ return;
509
+ }
510
+ if (!data.Data.AddMsgs) {
511
+ return;
512
+ }
513
+ // 更新联系人或者群组信息
514
+ // 新增:触发 message 事件
515
+ for (const msg of data.Data.AddMsgs) {
516
+ // Check for duplicate messages
517
+ const newMsgId = BigInt(msg.NewMsgId);
518
+ if (this.recentMsgIds.includes(newMsgId)) {
519
+ continue; // Skip duplicate message
326
520
  }
327
- if (!data.Data.AddMsgs) {
328
- return;
521
+ // Add to recent messages and maintain only last 40
522
+ this.recentMsgIds.push(newMsgId);
523
+ if (this.recentMsgIds.length > 40) {
524
+ this.recentMsgIds.shift(); // Remove oldest message ID
329
525
  }
330
- // 更新联系人或者群组信息
331
- // 新增:触发 message 事件
332
- for (const msg of data.Data.AddMsgs) {
333
- // Check for duplicate messages
334
- const newMsgId = BigInt(msg.NewMsgId);
335
- if (this.recentMsgIds.includes(newMsgId)) {
336
- continue; // Skip duplicate message
337
- }
338
- // Add to recent messages and maintain only last 10
339
- this.recentMsgIds.push(newMsgId);
340
- if (this.recentMsgIds.length > 10) {
341
- this.recentMsgIds.shift(); // Remove oldest message ID
526
+ // 将msg转换为Message类型
527
+ const message = new Message_1.Message({
528
+ MsgId: msg.MsgId,
529
+ FromUserName: msg.FromUserName.string.endsWith('@chatroom') ? msg.Content.string.split(':\n')[0] : msg.FromUserName.string,
530
+ ToUserName: msg.FromUserName.string.endsWith('@chatroom') ? msg.FromUserName.string : msg.ToUserName.string,
531
+ MsgType: msg.MsgType,
532
+ Content: msg.FromUserName.string.endsWith('@chatroom') ? msg.Content.string.split(':\n').slice(1).join(':\n') : msg.Content.string,
533
+ Status: msg.Status,
534
+ ImgStatus: msg.ImgStatus,
535
+ ImgBuf: msg.ImgBuf.iLen,
536
+ CreateTime: msg.CreateTime,
537
+ MsgSource: msg.MsgSource,
538
+ NewMsgId: msg.NewMsgId,
539
+ MsgSeq: msg.MsgSeq,
540
+ RoomId: (msg.FromUserName.string.endsWith('@chatroom') ? msg.FromUserName.string : '') || (msg.ToUserName.string.endsWith('@chatroom') ? msg.ToUserName.string : ''),
541
+ IsSelf: msg.FromUserName.string === userName,
542
+ xml: msg.Content.string
543
+ });
544
+ // 处理企业微信
545
+ if ((_a = message.fromId) === null || _a === void 0 ? void 0 : _a.includes('@openim')) {
546
+ let contactE = yield contact_service_1.contactService.getByUserName(message.fromId);
547
+ if (!contactE) {
548
+ contactE = new ContactEntity_1.ContactEntity();
342
549
  }
343
- // 将msg转换为Message类型
344
- const message = new Message_1.Message({
345
- MsgId: msg.MsgId,
346
- FromUserName: msg.FromUserName.string.endsWith('@chatroom') ? msg.Content.string.split(':\n')[0] : msg.FromUserName.string,
347
- ToUserName: msg.FromUserName.string.endsWith('@chatroom') ? msg.FromUserName.string : msg.ToUserName.string,
348
- MsgType: msg.MsgType,
349
- Content: msg.FromUserName.string.endsWith('@chatroom') ? msg.Content.string.split(':\n').slice(1).join(':\n') : msg.Content.string,
350
- Status: msg.Status,
351
- ImgStatus: msg.ImgStatus,
352
- ImgBuf: msg.ImgBuf.iLen,
353
- CreateTime: msg.CreateTime,
354
- MsgSource: msg.MsgSource,
355
- NewMsgId: msg.NewMsgId,
356
- MsgSeq: msg.MsgSeq,
357
- RoomId: (msg.FromUserName.string.endsWith('@chatroom') ? msg.FromUserName.string : '') || (msg.ToUserName.string.endsWith('@chatroom') ? msg.ToUserName.string : ''),
358
- IsSelf: msg.FromUserName.string === userName,
359
- xml: msg.Content.string
550
+ contactE.nickName = msg.PushContent.split(':')[0].slice(0, -1);
551
+ contactE.userName = message.fromId;
552
+ contactE.smallHeadImgUrl = 'https://raw.githubusercontent.com/finalpi/wechat2tg/wx2tg-pad/qywx.jpg';
553
+ contactE.bigHeadImgUrl = 'https://raw.githubusercontent.com/finalpi/wechat2tg/wx2tg-pad/qywx.jpg';
554
+ contact_service_1.contactService.upsertByUsername(contactE.userName, contactE);
555
+ }
556
+ if (message.type() === MessageType_1.MessageType.AddFriend) { // 好友请求
557
+ const obj = Message_1.Message.getXmlToJson(message.text());
558
+ console.log(obj);
559
+ const friendship = new Friendship_1.Friendship({
560
+ fromnickname: obj.msg.fromnickname,
561
+ v3: obj.msg.encryptusername,
562
+ v4: obj.msg.ticket,
563
+ scene: obj.msg.scene,
564
+ fromusername: obj.msg.fromusername,
565
+ bigHeadImgUrl: obj.msg.bigHeadImgUrl,
566
+ smallHeadImgUrl: obj.msg.smallHeadImgUrl,
567
+ content: obj.msg.content
360
568
  });
361
- // 处理企业微信
362
- if ((_a = message.fromId) === null || _a === void 0 ? void 0 : _a.includes('@openim')) {
363
- let contactE = yield contact_service_1.contactService.getByUserName(message.fromId);
364
- if (!contactE) {
365
- contactE = new ContactEntity_1.ContactEntity();
366
- }
367
- contactE.nickName = msg.PushContent.split(':')[0].slice(0, -1);
368
- contactE.userName = message.fromId;
369
- contactE.smallHeadImgUrl = 'https://raw.githubusercontent.com/finalpi/wechat2tg/wx2tg-pad/qywx.jpg';
370
- contactE.bigHeadImgUrl = 'https://raw.githubusercontent.com/finalpi/wechat2tg/wx2tg-pad/qywx.jpg';
371
- contact_service_1.contactService.upsertByUsername(contactE.userName, contactE);
372
- }
373
- if (message.type() === MessageType_1.MessageType.AddFriend) { // 好友请求
374
- const obj = Message_1.Message.getXmlToJson(message.text());
375
- console.log(obj);
376
- const friendship = new Friendship_1.Friendship({
377
- fromnickname: obj.msg.fromnickname,
378
- v3: obj.msg.encryptusername,
379
- v4: obj.msg.ticket,
380
- scene: obj.msg.scene,
381
- fromusername: obj.msg.fromusername,
382
- bigHeadImgUrl: obj.msg.bigHeadImgUrl,
383
- smallHeadImgUrl: obj.msg.smallHeadImgUrl,
384
- content: obj.msg.content
385
- });
386
- this.emit('friendship', friendship);
387
- }
388
- else {
389
- this.emit('message', message);
390
- }
569
+ this.emit('friendship', friendship);
570
+ }
571
+ else {
572
+ this.emit('message', message);
391
573
  }
392
574
  }
393
- }), 1000);
394
- this.emit('login', userName);
575
+ }
395
576
  });
396
577
  }
397
578
  logOut() {
package/dist/test.js CHANGED
@@ -12,9 +12,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  const _1 = require(".");
13
13
  (() => __awaiter(void 0, void 0, void 0, function* () {
14
14
  const wxBot = new _1.WxBot({
15
- baseUrl: 'http://127.0.0.1:8059/api',
15
+ baseUrl: 'http://192.168.56.101:8088/api',
16
16
  databasePath: './database.sqlite',
17
- deviceType: 'ipadX'
17
+ deviceType: 'ipad',
18
+ mode: 'polling',
19
+ callbackPort: 8056
18
20
  });
19
21
  yield wxBot.start();
20
22
  wxBot.on('message', (message) => __awaiter(void 0, void 0, void 0, function* () {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wx2tg-puppet",
3
- "version": "2.0.9",
3
+ "version": "2.1.1",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [