wechaty-puppet-matrix 0.0.7 → 0.0.9
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/cjs/src/matrix/events/event-friendship.js +17 -7
- package/dist/cjs/src/matrix/schema-mapper/contact.js +17 -7
- package/dist/cjs/src/matrix/schema-mapper/message/message-parser-appmsg.js +17 -7
- package/dist/cjs/src/matrix/schema-mapper/message/message-parser-refermsg.js +17 -7
- package/dist/cjs/src/matrix/schema-mapper/message/message-parser-type.js +17 -7
- package/dist/cjs/src/matrix/schema-mapper/message.js +17 -7
- package/dist/cjs/src/matrix/service/request.d.ts +11 -0
- package/dist/cjs/src/matrix/service/request.d.ts.map +1 -1
- package/dist/cjs/src/matrix/service/request.js +129 -51
- package/dist/cjs/src/puppet-matrix.d.ts +1 -0
- package/dist/cjs/src/puppet-matrix.d.ts.map +1 -1
- package/dist/cjs/src/puppet-matrix.js +35 -7
- package/dist/cjs/tests/tap.spec.js +17 -7
- package/dist/esm/src/matrix/service/request.d.ts +11 -0
- package/dist/esm/src/matrix/service/request.d.ts.map +1 -1
- package/dist/esm/src/matrix/service/request.js +112 -44
- package/dist/esm/src/puppet-matrix.d.ts +1 -0
- package/dist/esm/src/puppet-matrix.d.ts.map +1 -1
- package/dist/esm/src/puppet-matrix.js +18 -0
- package/package.json +1 -1
- package/src/matrix/service/request.ts +144 -87
- package/src/puppet-matrix.ts +19 -0
|
@@ -330,9 +330,17 @@ async function getAtWxidList (source: string): Promise<string[]> {
|
|
|
330
330
|
}
|
|
331
331
|
return []
|
|
332
332
|
}
|
|
333
|
+
interface ConnectionStatus {
|
|
334
|
+
status: 'disconnected' | 'connected' | 'connecting'
|
|
335
|
+
}
|
|
333
336
|
class Client extends EventEmitter {
|
|
334
337
|
|
|
335
338
|
private readonly options: PuppetMatrixOptions
|
|
339
|
+
private connectionStatus: ConnectionStatus = { status: 'disconnected' }
|
|
340
|
+
private readonly MAX_RECONNECT_ATTEMPTS = 10
|
|
341
|
+
private readonly INITIAL_RECONNECT_DELAY = 1000 // 1秒
|
|
342
|
+
private readonly MAX_RECONNECT_DELAY = 30000 // 30秒
|
|
343
|
+
private reconnectAttempts = 0
|
|
336
344
|
socket: any
|
|
337
345
|
server: any
|
|
338
346
|
tokenInfo: any
|
|
@@ -386,102 +394,119 @@ class Client extends EventEmitter {
|
|
|
386
394
|
|
|
387
395
|
async initServer () {
|
|
388
396
|
await this.getTokenInfo()
|
|
389
|
-
if (
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
* 1. Message
|
|
397
|
-
* 1.1. Deal with payload
|
|
398
|
-
*/
|
|
399
|
-
ws.on('message', (data: string) => {
|
|
400
|
-
log.silly(PRE, 'initWebSocket() ws.on(message): %s', data)
|
|
401
|
-
try {
|
|
402
|
-
const payload: any = JSON.parse(data)
|
|
403
|
-
log.info(JSON.stringify(payload))
|
|
404
|
-
void this.eventParse(payload)
|
|
405
|
-
} catch (e: any) {
|
|
406
|
-
log.warn(PRE, 'initWebSocket() ws.on(message) exception: %s', e)
|
|
407
|
-
this.emit('error', e.message)
|
|
408
|
-
}
|
|
409
|
-
})
|
|
397
|
+
if (this.socket) {
|
|
398
|
+
log.error('socket had already been opened!')
|
|
399
|
+
return
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
await this.createWebSocket()
|
|
403
|
+
}
|
|
410
404
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
405
|
+
async createWebSocket (): Promise<void> {
|
|
406
|
+
if (!this.tokenInfo) {
|
|
407
|
+
log.error('Token info not available')
|
|
408
|
+
return
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const ws = new WebSocket(
|
|
412
|
+
`${this.tokenInfo.endpoint}?guid=${this.tokenInfo.guid}`,
|
|
413
|
+
{
|
|
414
|
+
perMessageDeflate: true,
|
|
415
|
+
maxPayload: 100 * 1024 * 1024,
|
|
416
|
+
},
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
// 连接建立前的 Promise
|
|
420
|
+
await new Promise<void>((resolve, reject) => {
|
|
421
|
+
ws.once('open', () => {
|
|
422
|
+
this.connectionStatus.status = 'connected'
|
|
423
|
+
this.reconnectAttempts = 0
|
|
424
|
+
log.silly('WebSocket connection opened')
|
|
425
|
+
resolve()
|
|
424
426
|
})
|
|
425
427
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
ws.on('error', (e: any) => {
|
|
430
|
-
if (e.message.indexOf('ECONNREFUSED') !== -1) {
|
|
431
|
-
// Can not connect to remote server, if this is triggered when puppet-padchat is connected,
|
|
432
|
-
// an close event must be emitted, so ignore this error
|
|
433
|
-
// If this is triggered when puppet-padchat is trying to reconnect, also ignore this error
|
|
434
|
-
} else {
|
|
435
|
-
log.verbose(PRE, 'initWebSocket() ws.on(error) %s', e)
|
|
436
|
-
this.emit('error', e)
|
|
437
|
-
}
|
|
428
|
+
ws.once('error', (error: any) => {
|
|
429
|
+
log.warn('WebSocket connection error', error)
|
|
430
|
+
reject(error)
|
|
438
431
|
})
|
|
439
432
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
ws.on('close', (code: any, reason: any) => {
|
|
444
|
-
log.warn(
|
|
445
|
-
PRE,
|
|
446
|
-
'initWebSocket() ws.on(close) code: %s, reason: %s',
|
|
447
|
-
code,
|
|
448
|
-
reason,
|
|
449
|
-
)
|
|
450
|
-
|
|
451
|
-
// if (!this.reconnectThrottleQueue) {
|
|
452
|
-
// log.warn(PRE, 'initWebSocket() ws.on(close) reconnectThrottleQueue not exist')
|
|
453
|
-
// return
|
|
454
|
-
// }
|
|
455
|
-
// if (this.connectionStatus.status === CONNECTED) {
|
|
456
|
-
// this.reconnectThrottleQueue.next('ws.on(close, ' + code + ')')
|
|
457
|
-
// }
|
|
433
|
+
ws.once('close', (code: any, reason: any) => {
|
|
434
|
+
void this.handleWebSocketClose(code, reason)
|
|
435
|
+
reject(new Error(`WebSocket closed: ${code} - ${reason}`))
|
|
458
436
|
})
|
|
437
|
+
})
|
|
459
438
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
*/
|
|
463
|
-
await new Promise((resolve, reject) => {
|
|
464
|
-
ws.once('open', () => {
|
|
465
|
-
log.silly(PRE, 'initWebSocket() Promise() ws.on(open)')
|
|
466
|
-
return resolve({})
|
|
467
|
-
})
|
|
439
|
+
// 设置事件处理器
|
|
440
|
+
this.setupWebSocketListeners(ws)
|
|
468
441
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
442
|
+
this.socket = ws
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
private setupWebSocketListeners (ws: WebSocket): void {
|
|
446
|
+
// 消息处理
|
|
447
|
+
ws.on('message', (data: string) => {
|
|
448
|
+
log.silly(PRE, 'initWebSocket() ws.on(message): %s', data)
|
|
449
|
+
try {
|
|
450
|
+
const payload = JSON.parse(data)
|
|
451
|
+
log.info('Received payload', JSON.stringify(payload))
|
|
452
|
+
void this.eventParse(payload)
|
|
453
|
+
} catch (error) {
|
|
454
|
+
log.warn(PRE, 'initWebSocket() ws.on(message) exception: %s', error)
|
|
455
|
+
// @ts-ignore
|
|
456
|
+
this.emit('error', (error as Error).message)
|
|
457
|
+
}
|
|
458
|
+
})
|
|
459
|
+
|
|
460
|
+
// 错误处理
|
|
461
|
+
ws.on('error', (error: Error) => {
|
|
462
|
+
if ((error as any).code === 'ECONNREFUSED') {
|
|
463
|
+
log.verbose('Connection refused, potential reconnect scenario')
|
|
464
|
+
return
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
log.verbose(PRE, 'initWebSocket() ws.on(error) %s', error)
|
|
468
|
+
// @ts-ignore
|
|
469
|
+
this.emit('error', error)
|
|
470
|
+
})
|
|
478
471
|
|
|
479
|
-
|
|
472
|
+
// 关闭处理
|
|
473
|
+
ws.on('close', (code:any, reason: any) => {
|
|
474
|
+
void this.handleWebSocketClose(code, reason)
|
|
475
|
+
})
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
private async handleWebSocketClose (code?: number, reason?: Buffer) {
|
|
479
|
+
this.connectionStatus.status = 'disconnected'
|
|
480
|
+
log.warn(`WebSocket closed: Code ${code}, Reason ${reason}`)
|
|
481
|
+
this.socket = null
|
|
482
|
+
// 触发重连
|
|
483
|
+
await this.reconnect()
|
|
484
|
+
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
private async reconnect (): Promise<void> {
|
|
488
|
+
if (this.reconnectAttempts >= this.MAX_RECONNECT_ATTEMPTS) {
|
|
489
|
+
log.error('Max reconnect attempts reached. Stopping reconnection.')
|
|
480
490
|
return
|
|
481
491
|
}
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
492
|
+
|
|
493
|
+
// 指数退避重连策略
|
|
494
|
+
const delay = Math.min(
|
|
495
|
+
this.INITIAL_RECONNECT_DELAY * Math.pow(2, this.reconnectAttempts),
|
|
496
|
+
this.MAX_RECONNECT_DELAY,
|
|
497
|
+
)
|
|
498
|
+
|
|
499
|
+
this.reconnectAttempts++
|
|
500
|
+
log.info(`Reconnecting in ${delay}ms (Attempt ${this.reconnectAttempts})`)
|
|
501
|
+
|
|
502
|
+
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
503
|
+
setTimeout(async () => {
|
|
504
|
+
try {
|
|
505
|
+
await this.initServer()
|
|
506
|
+
} catch (error) {
|
|
507
|
+
log.warn('Reconnection failed', error)
|
|
508
|
+
}
|
|
509
|
+
}, delay)
|
|
485
510
|
}
|
|
486
511
|
|
|
487
512
|
async eventParse (eventData: any) {
|
|
@@ -691,6 +716,10 @@ class Client extends EventEmitter {
|
|
|
691
716
|
|
|
692
717
|
if (res.data?.errcode === -102) {
|
|
693
718
|
log.warn('request error: %s', res.data?.errmsg)
|
|
719
|
+
if (res.data?.errmsg === 'bridge guid not exist') {
|
|
720
|
+
await this.setBridgeId('')
|
|
721
|
+
return
|
|
722
|
+
}
|
|
694
723
|
if (!this.hasEmitLogout) {
|
|
695
724
|
this.setEmitLogout(true)
|
|
696
725
|
this.emit('logout', res.data?.errmsg)
|
|
@@ -730,6 +759,7 @@ class Client extends EventEmitter {
|
|
|
730
759
|
* 获取登录二维码
|
|
731
760
|
*/
|
|
732
761
|
public async getQrcode (): Promise<{
|
|
762
|
+
isLogin: boolean;
|
|
733
763
|
base64: string;
|
|
734
764
|
qrcodeUrl: string;
|
|
735
765
|
expiredTime: number;
|
|
@@ -743,13 +773,24 @@ class Client extends EventEmitter {
|
|
|
743
773
|
|
|
744
774
|
if (res?.qrcode) {
|
|
745
775
|
return {
|
|
776
|
+
isLogin: false,
|
|
746
777
|
base64: `data:image/jpeg;base64,${res?.qrcode?.buffer}`,
|
|
747
778
|
qrcodeUrl: `http://weixin.qq.com/x/${res?.uuid}`,
|
|
748
779
|
uuid: res?.uuid,
|
|
749
780
|
expiredTime: res?.expiredTime,
|
|
750
781
|
}
|
|
751
782
|
}
|
|
752
|
-
|
|
783
|
+
if (res.errcode === -1 && res.errmsg === 'user is login status, please logout') {
|
|
784
|
+
log.info(PRE, 'getQrcode tips: %s', JSON.stringify(res))
|
|
785
|
+
return {
|
|
786
|
+
isLogin: true,
|
|
787
|
+
base64: '',
|
|
788
|
+
qrcodeUrl: '',
|
|
789
|
+
uuid: '',
|
|
790
|
+
expiredTime: 0,
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
log.info(PRE, 'getQrcode error: %s', JSON.stringify(res))
|
|
753
794
|
return null
|
|
754
795
|
} catch (e) {
|
|
755
796
|
log.error(PRE, 'getQrcode(): %s', e)
|
|
@@ -818,6 +859,22 @@ class Client extends EventEmitter {
|
|
|
818
859
|
})
|
|
819
860
|
}
|
|
820
861
|
|
|
862
|
+
public async setBridgeId (bridgeId?: string) {
|
|
863
|
+
try {
|
|
864
|
+
const res = await this.postData({
|
|
865
|
+
path: '/client/set_bridge',
|
|
866
|
+
data: {
|
|
867
|
+
bridge: bridgeId,
|
|
868
|
+
},
|
|
869
|
+
})
|
|
870
|
+
|
|
871
|
+
log.info(PRE, 'setBridgeId(%s): %s', bridgeId, JSON.stringify(res))
|
|
872
|
+
} catch (e) {
|
|
873
|
+
log.error(PRE, 'setBridgeId(%s): %s', bridgeId, e)
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
}
|
|
877
|
+
|
|
821
878
|
/**
|
|
822
879
|
* 获取当前bot信息
|
|
823
880
|
*/
|
|
@@ -845,7 +902,7 @@ class Client extends EventEmitter {
|
|
|
845
902
|
labelIdlist: contact?.labelIdlist,
|
|
846
903
|
}
|
|
847
904
|
}
|
|
848
|
-
log.info(PRE, 'get self info error:(%s)', res
|
|
905
|
+
log.info(PRE, 'get self info error:(%s)', JSON.stringify(res))
|
|
849
906
|
return false
|
|
850
907
|
} catch (e) {
|
|
851
908
|
log.error(PRE, 'getSelfInfo(): %s', e)
|
package/src/puppet-matrix.ts
CHANGED
|
@@ -37,6 +37,7 @@ const SEARCH_CONTACT_PREFIX = '$search$-'
|
|
|
37
37
|
const STRANGER_SUFFIX = '@stranger'
|
|
38
38
|
|
|
39
39
|
export type PuppetEngineOptions = PUPPET.PuppetOptions & {
|
|
40
|
+
proxyId?: string,
|
|
40
41
|
token?: string,
|
|
41
42
|
engine?: any
|
|
42
43
|
}
|
|
@@ -65,6 +66,12 @@ class PuppetMatrix extends PUPPET.Puppet {
|
|
|
65
66
|
this.options.token = token
|
|
66
67
|
}
|
|
67
68
|
}
|
|
69
|
+
if (!this.options.proxyId) {
|
|
70
|
+
const proxyId = process.env['WECHATY_PUPPET_MATRIX_PROXYID'] || ''
|
|
71
|
+
if (proxyId) {
|
|
72
|
+
this.options.proxyId = proxyId
|
|
73
|
+
}
|
|
74
|
+
}
|
|
68
75
|
}
|
|
69
76
|
|
|
70
77
|
public get client () {
|
|
@@ -193,6 +200,8 @@ class PuppetMatrix extends PUPPET.Puppet {
|
|
|
193
200
|
log.info(PRE, `login success: ${info.name}`)
|
|
194
201
|
await this.onLogin(info)
|
|
195
202
|
} else {
|
|
203
|
+
await this._client?.setBridgeId(this.options.proxyId)
|
|
204
|
+
|
|
196
205
|
if (this._qrcodeInterval) {
|
|
197
206
|
clearInterval(this._qrcodeInterval)
|
|
198
207
|
this._qrcodeInterval = null
|
|
@@ -215,6 +224,10 @@ class PuppetMatrix extends PUPPET.Puppet {
|
|
|
215
224
|
private async _getQrcode () {
|
|
216
225
|
const qrcode = await this._client?.getQrcode()
|
|
217
226
|
if (qrcode) {
|
|
227
|
+
if (qrcode.isLogin) {
|
|
228
|
+
await this.checkIsLogin()
|
|
229
|
+
return
|
|
230
|
+
}
|
|
218
231
|
if (this._qrcodeStatuasInterval) {
|
|
219
232
|
clearInterval(this._qrcodeStatuasInterval)
|
|
220
233
|
this._qrcodeStatuasInterval = null
|
|
@@ -244,6 +257,12 @@ class PuppetMatrix extends PUPPET.Puppet {
|
|
|
244
257
|
}
|
|
245
258
|
|
|
246
259
|
}, 8000)
|
|
260
|
+
} else {
|
|
261
|
+
let tempTimer: any = setTimeout(() => {
|
|
262
|
+
void this._getQrcode()
|
|
263
|
+
tempTimer && clearTimeout(tempTimer)
|
|
264
|
+
tempTimer = null
|
|
265
|
+
}, 2000)
|
|
247
266
|
}
|
|
248
267
|
}
|
|
249
268
|
|