wechaty-puppet-matrix 0.0.10 → 0.0.12
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/cache-manager.js +1 -1
- package/dist/cjs/src/matrix/service/request.d.ts +55 -0
- package/dist/cjs/src/matrix/service/request.d.ts.map +1 -1
- package/dist/cjs/src/matrix/service/request.js +349 -1
- package/dist/cjs/src/matrix/utils/index.d.ts +3 -0
- package/dist/cjs/src/matrix/utils/index.d.ts.map +1 -1
- package/dist/cjs/src/matrix/utils/index.js +31 -0
- package/dist/cjs/src/puppet-matrix.d.ts +6 -0
- package/dist/cjs/src/puppet-matrix.d.ts.map +1 -1
- package/dist/cjs/src/puppet-matrix.js +84 -0
- package/dist/cjs/src/utils/normalize-filebox.d.ts +6 -0
- package/dist/cjs/src/utils/normalize-filebox.d.ts.map +1 -0
- package/dist/cjs/src/utils/normalize-filebox.js +46 -0
- package/dist/cjs/src/utils/sns-xml-generator.d.ts +19 -0
- package/dist/cjs/src/utils/sns-xml-generator.d.ts.map +1 -0
- package/dist/cjs/src/utils/sns-xml-generator.js +171 -0
- package/dist/esm/src/matrix/cache-manager.js +1 -1
- package/dist/esm/src/matrix/service/request.d.ts +55 -0
- package/dist/esm/src/matrix/service/request.d.ts.map +1 -1
- package/dist/esm/src/matrix/service/request.js +349 -1
- package/dist/esm/src/matrix/utils/index.d.ts +3 -0
- package/dist/esm/src/matrix/utils/index.d.ts.map +1 -1
- package/dist/esm/src/matrix/utils/index.js +28 -0
- package/dist/esm/src/puppet-matrix.d.ts +6 -0
- package/dist/esm/src/puppet-matrix.d.ts.map +1 -1
- package/dist/esm/src/puppet-matrix.js +85 -1
- package/dist/esm/src/utils/normalize-filebox.d.ts +6 -0
- package/dist/esm/src/utils/normalize-filebox.d.ts.map +1 -0
- package/dist/esm/src/utils/normalize-filebox.js +42 -0
- package/dist/esm/src/utils/sns-xml-generator.d.ts +19 -0
- package/dist/esm/src/utils/sns-xml-generator.d.ts.map +1 -0
- package/dist/esm/src/utils/sns-xml-generator.js +165 -0
- package/package.json +6 -5
- package/src/matrix/cache-manager.ts +1 -1
- package/src/matrix/service/request.ts +455 -2
- package/src/matrix/utils/index.ts +62 -0
- package/src/puppet-matrix.ts +112 -1
- package/src/utils/normalize-filebox.ts +90 -0
- package/src/utils/sns-xml-generator.ts +184 -0
|
@@ -7,7 +7,10 @@ import * as PUPPET from '@juzi/wechaty-puppet'
|
|
|
7
7
|
import { format, getUnixTime } from 'date-fns'
|
|
8
8
|
import { xmlToJson } from '../utils/xml-to-json.js'
|
|
9
9
|
import { isRoomId } from '../utils/is-type.js'
|
|
10
|
-
|
|
10
|
+
import imageSize from 'image-size'
|
|
11
|
+
import fs from 'fs-extra'
|
|
12
|
+
import os from 'os'
|
|
13
|
+
import path from 'path'
|
|
11
14
|
const PRE = '[PuppetMatrix]'
|
|
12
15
|
|
|
13
16
|
/**
|
|
@@ -252,6 +255,25 @@ export interface ImageMessagePayload {
|
|
|
252
255
|
file_name?: string;
|
|
253
256
|
}
|
|
254
257
|
|
|
258
|
+
export interface SnsImageMessagePayload {
|
|
259
|
+
errcode: number;
|
|
260
|
+
file_key: string;
|
|
261
|
+
file_url: string;
|
|
262
|
+
thumb_url: string;
|
|
263
|
+
}
|
|
264
|
+
export interface SnsVideoMessagePayload {
|
|
265
|
+
errcode: number;
|
|
266
|
+
mp4_identify: string;
|
|
267
|
+
file_key: string;
|
|
268
|
+
file_url: string;
|
|
269
|
+
thumb_url: string;
|
|
270
|
+
thumb_width: string;
|
|
271
|
+
thumb_height: string;
|
|
272
|
+
video_duration: string;
|
|
273
|
+
video_width: string;
|
|
274
|
+
video_height: string;
|
|
275
|
+
}
|
|
276
|
+
|
|
255
277
|
export interface AudioMessagePayload {
|
|
256
278
|
aesKey: string;
|
|
257
279
|
length: string;
|
|
@@ -319,6 +341,22 @@ export interface RoomLeavPayload {
|
|
|
319
341
|
roomId: string;
|
|
320
342
|
leaveIds: string[];
|
|
321
343
|
}
|
|
344
|
+
export interface Media {
|
|
345
|
+
fileId: string
|
|
346
|
+
file_size: number
|
|
347
|
+
fileType: number
|
|
348
|
+
video_duration: number
|
|
349
|
+
url: string
|
|
350
|
+
video_width: number
|
|
351
|
+
video_height: number
|
|
352
|
+
file_url: string
|
|
353
|
+
file_key: string
|
|
354
|
+
thumb_url: string
|
|
355
|
+
thumb_width: number
|
|
356
|
+
thumb_height: number
|
|
357
|
+
image_width: number
|
|
358
|
+
image_height: number
|
|
359
|
+
}
|
|
322
360
|
|
|
323
361
|
async function getAtWxidList (source: string): Promise<string[]> {
|
|
324
362
|
if (source) {
|
|
@@ -330,9 +368,241 @@ async function getAtWxidList (source: string): Promise<string[]> {
|
|
|
330
368
|
}
|
|
331
369
|
return []
|
|
332
370
|
}
|
|
371
|
+
|
|
333
372
|
interface ConnectionStatus {
|
|
334
373
|
status: 'disconnected' | 'connected' | 'connecting'
|
|
335
374
|
}
|
|
375
|
+
|
|
376
|
+
async function getImageInfo (imageUrl: string) {
|
|
377
|
+
try {
|
|
378
|
+
// 1. 下载图片
|
|
379
|
+
const response = await axios({
|
|
380
|
+
method: 'get',
|
|
381
|
+
url: imageUrl,
|
|
382
|
+
responseType: 'arraybuffer',
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
// 2. 获取文件大小(字节数)
|
|
386
|
+
const fileSize = response.data.length
|
|
387
|
+
|
|
388
|
+
// 3. 创建临时文件保存图片
|
|
389
|
+
const tempFilePath = path.join(
|
|
390
|
+
os.homedir(),
|
|
391
|
+
path.sep,
|
|
392
|
+
'.wechaty',
|
|
393
|
+
'puppet-matrix-cache',
|
|
394
|
+
path.sep,
|
|
395
|
+
'temp_image_' + Date.now() + path.extname(imageUrl),
|
|
396
|
+
path.sep,
|
|
397
|
+
)
|
|
398
|
+
const baseDirExist = await fs.pathExists(tempFilePath)
|
|
399
|
+
if (!baseDirExist) {
|
|
400
|
+
await fs.mkdirp(tempFilePath)
|
|
401
|
+
}
|
|
402
|
+
await fs.writeFile(tempFilePath, response.data)
|
|
403
|
+
|
|
404
|
+
// 4. 获取图片宽高
|
|
405
|
+
const dimensions = imageSize(tempFilePath)
|
|
406
|
+
|
|
407
|
+
// 5. 删除临时文件
|
|
408
|
+
await fs.unlink(tempFilePath)
|
|
409
|
+
|
|
410
|
+
return {
|
|
411
|
+
file_size: fileSize, // 文件大小(字节)
|
|
412
|
+
image_width: dimensions.width,
|
|
413
|
+
image_height: dimensions.height,
|
|
414
|
+
file_type: dimensions.type,
|
|
415
|
+
}
|
|
416
|
+
} catch (error) {
|
|
417
|
+
console.error('获取图片信息错误:', error)
|
|
418
|
+
throw error
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const genTextSnsXml = (wxid: string, content: string): string => {
|
|
423
|
+
const xmlTemplate = `
|
|
424
|
+
<TimelineObject>
|
|
425
|
+
<id><![CDATA[0]]></id>
|
|
426
|
+
<username><![CDATA[${wxid}]]></username>
|
|
427
|
+
<createTime><![CDATA[${Math.floor(Date.now() / 1000)}]]></createTime>
|
|
428
|
+
<contentDescShowType>0</contentDescShowType>
|
|
429
|
+
<contentDescScene>0</contentDescScene>
|
|
430
|
+
<private><![CDATA[0]]></private>
|
|
431
|
+
<contentDesc><![CDATA[${content}]]></contentDesc>
|
|
432
|
+
<contentattr><![CDATA[0]]></contentattr>
|
|
433
|
+
<sourceUserName></sourceUserName>
|
|
434
|
+
<sourceNickName></sourceNickName>
|
|
435
|
+
<statisticsData></statisticsData>
|
|
436
|
+
<weappInfo>
|
|
437
|
+
<appUserName></appUserName>
|
|
438
|
+
<pagePath></pagePath>
|
|
439
|
+
<version><![CDATA[0]]></version>
|
|
440
|
+
<isHidden>0</isHidden>
|
|
441
|
+
<debugMode><![CDATA[0]]></debugMode>
|
|
442
|
+
<shareActionId></shareActionId>
|
|
443
|
+
<isGame><![CDATA[0]]></isGame>
|
|
444
|
+
<messageExtraData></messageExtraData>
|
|
445
|
+
<subType><![CDATA[0]]></subType>
|
|
446
|
+
<preloadResources></preloadResources>
|
|
447
|
+
</weappInfo>
|
|
448
|
+
<canvasInfoXml></canvasInfoXml>
|
|
449
|
+
<ContentObject>
|
|
450
|
+
<contentStyle><![CDATA[2]]></contentStyle>
|
|
451
|
+
<contentSubStyle><![CDATA[0]]></contentSubStyle>
|
|
452
|
+
<title></title>
|
|
453
|
+
<description></description>
|
|
454
|
+
<contentUrl></contentUrl>
|
|
455
|
+
</ContentObject>
|
|
456
|
+
<actionInfo>
|
|
457
|
+
<appMsg>
|
|
458
|
+
<mediaTagName></mediaTagName>
|
|
459
|
+
<messageExt></messageExt>
|
|
460
|
+
<messageAction></messageAction>
|
|
461
|
+
</appMsg>
|
|
462
|
+
</actionInfo>
|
|
463
|
+
<appInfo><id></id></appInfo>
|
|
464
|
+
<publicUserName></publicUserName>
|
|
465
|
+
<streamvideo>
|
|
466
|
+
<streamvideourl></streamvideourl>
|
|
467
|
+
<streamvideothumburl></streamvideothumburl>
|
|
468
|
+
<streamvideoweburl></streamvideoweburl>
|
|
469
|
+
</streamvideo>
|
|
470
|
+
</TimelineObject>`.replace(/\s+/g, '')
|
|
471
|
+
return xmlTemplate
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
const genVideoSnsXml = (wxid: string, content: string, media: Media): string => {
|
|
475
|
+
const xmlTemplate = `
|
|
476
|
+
<TimelineObject>
|
|
477
|
+
<id>0</id>
|
|
478
|
+
<username>${wxid}</username>
|
|
479
|
+
<createTime>${Math.floor(Date.now() / 1000)}</createTime>
|
|
480
|
+
<contentDesc>${content}</contentDesc>
|
|
481
|
+
<contentDescShowType>0</contentDescShowType>
|
|
482
|
+
<contentDescScene>0</contentDescScene>
|
|
483
|
+
<private>0</private>
|
|
484
|
+
<sightFolded>0</sightFolded>
|
|
485
|
+
<showFlag>0</showFlag>
|
|
486
|
+
<appInfo>
|
|
487
|
+
<id></id>
|
|
488
|
+
<version></version>
|
|
489
|
+
<appName></appName>
|
|
490
|
+
<installUrl></installUrl>
|
|
491
|
+
<fromUrl></fromUrl>
|
|
492
|
+
<isForceUpdate>0</isForceUpdate>
|
|
493
|
+
<isHidden>0</isHidden>
|
|
494
|
+
</appInfo>
|
|
495
|
+
<sourceUserName></sourceUserName>
|
|
496
|
+
<sourceNickName></sourceNickName>
|
|
497
|
+
<statisticsData></statisticsData>
|
|
498
|
+
<statExtStr></statExtStr>
|
|
499
|
+
<ContentObject>
|
|
500
|
+
<contentStyle>15</contentStyle>
|
|
501
|
+
<title></title>
|
|
502
|
+
<description>Sight</description>
|
|
503
|
+
<mediaList>
|
|
504
|
+
<media>
|
|
505
|
+
<id>0</id>
|
|
506
|
+
<type>6</type>
|
|
507
|
+
<title></title>
|
|
508
|
+
<description>测试</description>
|
|
509
|
+
<private>0</private>
|
|
510
|
+
<userData></userData>
|
|
511
|
+
<subType>0</subType>
|
|
512
|
+
<videoSize width="${media.video_width}" height="${media.video_height}"/>
|
|
513
|
+
<url type="1" md5="951a7d7864d685a92fd2624155794bf9" videomd5="577f55635faf44f595a69ded26d87bcc">${media.file_url}</url>
|
|
514
|
+
<thumb type="1">${media.thumb_url}</thumb>
|
|
515
|
+
<size width="${media.thumb_width}.000000" height="${media.thumb_height}.000000" totalSize="${media.file_size}"/>
|
|
516
|
+
<videoDuration>${media.video_duration}.000000</videoDuration>
|
|
517
|
+
</media>
|
|
518
|
+
</mediaList>
|
|
519
|
+
</ContentObject>
|
|
520
|
+
</TimelineObject>`
|
|
521
|
+
return xmlTemplate
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
const genImageSnsXml = (wxid: string, contentDesc: string, mediaList: Media[]): string => {
|
|
525
|
+
const mediaTemplate = (media: Media) => `
|
|
526
|
+
<media>
|
|
527
|
+
<id><![CDATA[0]]></id>
|
|
528
|
+
<type><![CDATA[2]]></type>
|
|
529
|
+
<title></title>
|
|
530
|
+
<description></description>
|
|
531
|
+
<private><![CDATA[0]]></private>
|
|
532
|
+
<url type="1" md5="951a7d7864d685a92fd2624155794bf9"><![CDATA[${media.file_url}]]></url>
|
|
533
|
+
<thumb type="1"><![CDATA[${media.thumb_url}]]></thumb>
|
|
534
|
+
<videoDuration><![CDATA[0.0]]></videoDuration>
|
|
535
|
+
<size totalSize="${media.file_size}" width="${media.image_width}" height="${media.image_height}"></size>
|
|
536
|
+
</media>`
|
|
537
|
+
|
|
538
|
+
const mediaString = mediaList.map(media => mediaTemplate(media)).join('')
|
|
539
|
+
|
|
540
|
+
const xmlTemplate = `
|
|
541
|
+
<TimelineObject>
|
|
542
|
+
<id><![CDATA[0]]></id>
|
|
543
|
+
<username><![CDATA[${wxid}]]></username>
|
|
544
|
+
<createTime><![CDATA[${Math.floor(Date.now() / 1000)}]]></createTime>
|
|
545
|
+
<contentDescShowType>0</contentDescShowType>
|
|
546
|
+
<contentDescScene>0</contentDescScene>
|
|
547
|
+
<private><![CDATA[0]]></private>
|
|
548
|
+
<contentDesc><![CDATA[${contentDesc}]]></contentDesc>
|
|
549
|
+
<contentattr><![CDATA[0]]></contentattr>
|
|
550
|
+
<sourceUserName></sourceUserName>
|
|
551
|
+
<sourceNickName></sourceNickName>
|
|
552
|
+
<statisticsData></statisticsData>
|
|
553
|
+
<weappInfo>
|
|
554
|
+
<appUserName></appUserName>
|
|
555
|
+
<pagePath></pagePath>
|
|
556
|
+
<version><![CDATA[0]]></version>
|
|
557
|
+
<isHidden>0</isHidden>
|
|
558
|
+
<debugMode><![CDATA[0]]></debugMode>
|
|
559
|
+
<shareActionId></shareActionId>
|
|
560
|
+
<isGame><![CDATA[0]]></isGame>
|
|
561
|
+
<messageExtraData></messageExtraData>
|
|
562
|
+
<subType><![CDATA[0]]></subType>
|
|
563
|
+
<preloadResources></preloadResources>
|
|
564
|
+
</weappInfo>
|
|
565
|
+
<canvasInfoXml></canvasInfoXml>
|
|
566
|
+
<ContentObject>
|
|
567
|
+
<contentStyle><![CDATA[1]]></contentStyle>
|
|
568
|
+
<contentSubStyle><![CDATA[0]]></contentSubStyle>
|
|
569
|
+
<title></title>
|
|
570
|
+
<description></description>
|
|
571
|
+
<contentUrl></contentUrl>
|
|
572
|
+
<mediaList>${mediaString}</mediaList>
|
|
573
|
+
</ContentObject>
|
|
574
|
+
<actionInfo>
|
|
575
|
+
<appMsg>
|
|
576
|
+
<mediaTagName></mediaTagName>
|
|
577
|
+
<messageExt></messageExt>
|
|
578
|
+
<messageAction></messageAction>
|
|
579
|
+
</appMsg>
|
|
580
|
+
</actionInfo>
|
|
581
|
+
<appInfo><id></id></appInfo>
|
|
582
|
+
<publicUserName></publicUserName>
|
|
583
|
+
<streamvideo>
|
|
584
|
+
<streamvideourl></streamvideourl>
|
|
585
|
+
<streamvideothumburl></streamvideothumburl>
|
|
586
|
+
<streamvideoweburl></streamvideoweburl>
|
|
587
|
+
</streamvideo>
|
|
588
|
+
</TimelineObject>`
|
|
589
|
+
return xmlTemplate
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
export interface MomentInfo {
|
|
593
|
+
content: string,
|
|
594
|
+
mentionIdList: string[],
|
|
595
|
+
visibledList: string[],
|
|
596
|
+
imageUrls: string[],
|
|
597
|
+
videoUrl: string,
|
|
598
|
+
urlLink: null | PUPPET.payloads.UrlLink,
|
|
599
|
+
channel: null | PUPPET.payloads.Channel,
|
|
600
|
+
miniInfo: null | PUPPET.payloads.MiniProgram,
|
|
601
|
+
location: null | PUPPET.payloads.Location,
|
|
602
|
+
rootId: string,
|
|
603
|
+
parentId: string
|
|
604
|
+
}
|
|
605
|
+
|
|
336
606
|
class Client extends EventEmitter {
|
|
337
607
|
|
|
338
608
|
private readonly options: PuppetMatrixOptions
|
|
@@ -2051,7 +2321,7 @@ class Client extends EventEmitter {
|
|
|
2051
2321
|
announcement: content,
|
|
2052
2322
|
},
|
|
2053
2323
|
})
|
|
2054
|
-
if (res
|
|
2324
|
+
if (res?.baseResponse?.ret) {
|
|
2055
2325
|
log.error('sendAnnouncement error: %s', JSON.stringify(res.baseResponse))
|
|
2056
2326
|
}
|
|
2057
2327
|
} catch (error) {
|
|
@@ -2059,6 +2329,189 @@ class Client extends EventEmitter {
|
|
|
2059
2329
|
}
|
|
2060
2330
|
}
|
|
2061
2331
|
|
|
2332
|
+
/**
|
|
2333
|
+
* 上传朋友圈图片
|
|
2334
|
+
* @param url
|
|
2335
|
+
* @returns
|
|
2336
|
+
*/
|
|
2337
|
+
public async uploadSnsImage (url: string): Promise<SnsImageMessagePayload | void> {
|
|
2338
|
+
try {
|
|
2339
|
+
const res = await this.postData({
|
|
2340
|
+
path: '/cloud/cdn_upload_sns_image',
|
|
2341
|
+
data: {
|
|
2342
|
+
url,
|
|
2343
|
+
},
|
|
2344
|
+
})
|
|
2345
|
+
if (res?.errcode !== 0) {
|
|
2346
|
+
log.error('uploadSnsImage error: %s', JSON.stringify(res))
|
|
2347
|
+
return
|
|
2348
|
+
}
|
|
2349
|
+
return res as SnsImageMessagePayload
|
|
2350
|
+
} catch (error) {
|
|
2351
|
+
log.error(PRE, 'uploadSnsImage(%s): %s', url, error)
|
|
2352
|
+
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
|
|
2356
|
+
/**
|
|
2357
|
+
* 上传朋友视频
|
|
2358
|
+
* @param url
|
|
2359
|
+
* @returns
|
|
2360
|
+
*/
|
|
2361
|
+
public async uploadSnsVideo (url: string): Promise<SnsVideoMessagePayload | void> {
|
|
2362
|
+
try {
|
|
2363
|
+
const res = await this.postData({
|
|
2364
|
+
path: '/cloud/cdn_upload_sns_video',
|
|
2365
|
+
data: {
|
|
2366
|
+
url,
|
|
2367
|
+
},
|
|
2368
|
+
})
|
|
2369
|
+
log.info('uploadSnsVideo result:%s', res.data)
|
|
2370
|
+
if (res?.errcode !== 0) {
|
|
2371
|
+
log.error('uploadSnsVideo error: %s', JSON.stringify(res))
|
|
2372
|
+
}
|
|
2373
|
+
return res as SnsVideoMessagePayload
|
|
2374
|
+
} catch (error) {
|
|
2375
|
+
log.error(PRE, 'updateSnsImage(%s): %s', url, error)
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
|
|
2379
|
+
/**
|
|
2380
|
+
* 发送朋友圈
|
|
2381
|
+
* @param wxid
|
|
2382
|
+
* @param momentInfo
|
|
2383
|
+
*/
|
|
2384
|
+
public async sendSnsMoment (wxid: string, momentInfo: MomentInfo) {
|
|
2385
|
+
try {
|
|
2386
|
+
log.info('momentInfo: %s', JSON.stringify(momentInfo))
|
|
2387
|
+
let xmlContent = ''
|
|
2388
|
+
// 朋友圈评论
|
|
2389
|
+
if (momentInfo.parentId && momentInfo.rootId) {
|
|
2390
|
+
return await this.sendMomentReply(momentInfo.rootId, momentInfo.content, momentInfo.parentId === momentInfo.rootId ? '' : momentInfo.parentId)
|
|
2391
|
+
}
|
|
2392
|
+
if (momentInfo.imageUrls.length) {
|
|
2393
|
+
const imageInfo: any[] = []
|
|
2394
|
+
for (const image of momentInfo.imageUrls) {
|
|
2395
|
+
const res = await this.uploadSnsImage(image)
|
|
2396
|
+
if (res) {
|
|
2397
|
+
const imageBaseInfo = await getImageInfo(image)
|
|
2398
|
+
imageInfo.push({ ...res, ...imageBaseInfo })
|
|
2399
|
+
}
|
|
2400
|
+
xmlContent = genImageSnsXml(wxid, momentInfo.content, imageInfo)
|
|
2401
|
+
|
|
2402
|
+
}
|
|
2403
|
+
} else if (momentInfo.videoUrl) {
|
|
2404
|
+
const mediaInfo: any = await this.uploadSnsVideo(momentInfo.videoUrl)
|
|
2405
|
+
if (mediaInfo) {
|
|
2406
|
+
const thumbInfo = await getImageInfo(mediaInfo.thumb_url)
|
|
2407
|
+
xmlContent = genVideoSnsXml(wxid, momentInfo.content, { ...mediaInfo, ...thumbInfo })
|
|
2408
|
+
}
|
|
2409
|
+
} else {
|
|
2410
|
+
xmlContent = genTextSnsXml(wxid, momentInfo.content)
|
|
2411
|
+
}
|
|
2412
|
+
return await this.sendMoment(xmlContent)
|
|
2413
|
+
} catch (error) {
|
|
2414
|
+
log.error(PRE, 'sendSnsMoment(%s): %s', JSON.stringify(momentInfo), error)
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2418
|
+
public async sendMoment (content: string):Promise<string | void> {
|
|
2419
|
+
try {
|
|
2420
|
+
const res = await this.postData({
|
|
2421
|
+
path: '/sns/sns_post',
|
|
2422
|
+
data: {
|
|
2423
|
+
object_desc: content,
|
|
2424
|
+
with_user_list: [],
|
|
2425
|
+
block_user_list: [],
|
|
2426
|
+
group_user_list: [],
|
|
2427
|
+
group_contact_tag_id_list: [],
|
|
2428
|
+
black_contact_tag_id_list: [],
|
|
2429
|
+
},
|
|
2430
|
+
})
|
|
2431
|
+
if (res?.baseResponse?.ret !== 0) {
|
|
2432
|
+
log.error('sendMoment error: %s', JSON.stringify(res))
|
|
2433
|
+
return
|
|
2434
|
+
}
|
|
2435
|
+
log.info(PRE, 'sendMomen success: %s', res?.snsObject?.id)
|
|
2436
|
+
return res?.snsObject?.id
|
|
2437
|
+
} catch (error) {
|
|
2438
|
+
log.error(PRE, 'sendMoment(%s): %s', content, error)
|
|
2439
|
+
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
|
|
2443
|
+
public async unSendMoment (objectId: string):Promise<string | void> {
|
|
2444
|
+
try {
|
|
2445
|
+
const res = await this.postData({
|
|
2446
|
+
path: '/sns/sns_delete',
|
|
2447
|
+
data: {
|
|
2448
|
+
object_id: objectId,
|
|
2449
|
+
},
|
|
2450
|
+
})
|
|
2451
|
+
if (res?.baseResponse?.ret !== 0) {
|
|
2452
|
+
log.error('unSendMoment error: %s', JSON.stringify(res))
|
|
2453
|
+
return
|
|
2454
|
+
}
|
|
2455
|
+
return objectId
|
|
2456
|
+
} catch (error) {
|
|
2457
|
+
log.error(PRE, 'unSendMoment(%s): %s', objectId, error)
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
|
|
2461
|
+
/**
|
|
2462
|
+
* 朋友圈点赞
|
|
2463
|
+
* @param objectId
|
|
2464
|
+
* @param status 1 赞 0 取消点赞
|
|
2465
|
+
* @returns
|
|
2466
|
+
*/
|
|
2467
|
+
public async sendMomentLike (objectId: string, status: PUPPET.types.Tap):Promise<string | void> {
|
|
2468
|
+
try {
|
|
2469
|
+
const res = await this.postData({
|
|
2470
|
+
path: '/sns/sns_like',
|
|
2471
|
+
data: {
|
|
2472
|
+
object_id: objectId,
|
|
2473
|
+
status,
|
|
2474
|
+
},
|
|
2475
|
+
})
|
|
2476
|
+
if (res?.baseResponse?.ret !== 0) {
|
|
2477
|
+
log.error('sendMomentLike error: %s', JSON.stringify(res))
|
|
2478
|
+
return
|
|
2479
|
+
}
|
|
2480
|
+
return res?.snsObject?.id
|
|
2481
|
+
} catch (error) {
|
|
2482
|
+
log.error(PRE, 'sendMomentLike(%s): %s', objectId, error)
|
|
2483
|
+
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2486
|
+
|
|
2487
|
+
/**
|
|
2488
|
+
* 朋友圈回复
|
|
2489
|
+
* @param objectId 朋友圈id
|
|
2490
|
+
* @param content
|
|
2491
|
+
* @param commentId 评论id
|
|
2492
|
+
* @returns
|
|
2493
|
+
*/
|
|
2494
|
+
public async sendMomentReply (objectId: string, content: string, commentId?: string):Promise<string | void> {
|
|
2495
|
+
try {
|
|
2496
|
+
const res = await this.postData({
|
|
2497
|
+
path: '/sns/sns_comment',
|
|
2498
|
+
data: {
|
|
2499
|
+
object_id: objectId,
|
|
2500
|
+
content,
|
|
2501
|
+
reply_comment_id: commentId || '0',
|
|
2502
|
+
},
|
|
2503
|
+
})
|
|
2504
|
+
if (res?.baseResponse?.ret !== 0) {
|
|
2505
|
+
log.error('sendMomentLike error: %s', JSON.stringify(res))
|
|
2506
|
+
return
|
|
2507
|
+
}
|
|
2508
|
+
return res?.snsObject?.id
|
|
2509
|
+
} catch (error) {
|
|
2510
|
+
log.error(PRE, 'sendMomentLike(%s): %s', objectId, error)
|
|
2511
|
+
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
|
|
2062
2515
|
}
|
|
2063
2516
|
|
|
2064
2517
|
export default Client
|
|
@@ -32,3 +32,65 @@ export function getFileName (path:string): string {
|
|
|
32
32
|
const pos = Math.max(pos1, pos2)
|
|
33
33
|
if (pos < 0) { return path } else { return path.substring(pos + 1) }
|
|
34
34
|
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 生成指定位数的唯一数字标识
|
|
38
|
+
* @param {number} digits - 期望生成的总位数,默认为20
|
|
39
|
+
* @returns {number} 唯一数字标识
|
|
40
|
+
*/
|
|
41
|
+
export function generateUniqueNumeric (digits = 20) {
|
|
42
|
+
digits = digits - 13
|
|
43
|
+
if (digits <= 0) {
|
|
44
|
+
throw new Error('位数长度必须大于0')
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 获取当前时间戳
|
|
48
|
+
const currentTimestamp = Date.now()
|
|
49
|
+
|
|
50
|
+
// 生成随机数,并确保在指定位数内
|
|
51
|
+
const maxRandom = Math.pow(10, digits) - 1
|
|
52
|
+
const randomPart = Math.floor(Math.random() * maxRandom)
|
|
53
|
+
|
|
54
|
+
// 将随机数补足指定位数
|
|
55
|
+
const paddedRandomPart = randomPart.toString().padStart(digits, '0')
|
|
56
|
+
|
|
57
|
+
// 将时间戳和随机数组合成唯一数字
|
|
58
|
+
const uniqueNumeric = parseInt(`${currentTimestamp}${paddedRandomPart}`)
|
|
59
|
+
|
|
60
|
+
return uniqueNumeric
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 生成指定长度的随机字符串
|
|
65
|
+
* @param {number} length - 字符串长度
|
|
66
|
+
* @returns {string} 随机字符串
|
|
67
|
+
*/
|
|
68
|
+
export function generateRandomString (length: number) {
|
|
69
|
+
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
|
70
|
+
let result = ''
|
|
71
|
+
|
|
72
|
+
for (let i = 0; i < length; i++) {
|
|
73
|
+
const randomIndex = Math.floor(Math.random() * characters.length)
|
|
74
|
+
result += characters.charAt(randomIndex)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return result
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* 生成带有指定前缀的随机字符串
|
|
82
|
+
* @param {string} prefix - 字符串前缀
|
|
83
|
+
* @param {number} length - 总字符串长度
|
|
84
|
+
* @returns {string} 带前缀的随机字符串
|
|
85
|
+
*/
|
|
86
|
+
export function generateCustomRandomString (prefix: string, length: number) {
|
|
87
|
+
// 确保前缀部分不超过指定长度
|
|
88
|
+
if (prefix.length >= length) {
|
|
89
|
+
throw new Error('前缀长度不能大于或等于总长度')
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// 生成剩余部分的随机字符串
|
|
93
|
+
const remainingPart = generateRandomString(length - prefix.length)
|
|
94
|
+
|
|
95
|
+
return prefix + remainingPart
|
|
96
|
+
}
|
package/src/puppet-matrix.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { log } from '@juzi/wechaty-puppet'
|
|
2
2
|
import * as PUPPET from '@juzi/wechaty-puppet'
|
|
3
3
|
import type { FileBoxInterface } from 'file-box'
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
FileBox,
|
|
6
|
+
FileBoxType,
|
|
7
|
+
} from 'file-box'
|
|
5
8
|
import type { ContactPayload, MessagePayload } from './engine-schema.js'
|
|
6
9
|
import type { FileBoxMetadataMessage } from './matrix/types.js'
|
|
7
10
|
import Client from './matrix/service/request.js'
|
|
@@ -54,6 +57,11 @@ class PuppetMatrix extends PUPPET.Puppet {
|
|
|
54
57
|
private _verifyInterval?: ReturnType<typeof setInterval> | null
|
|
55
58
|
private _qrcodeStatuasInterval?: ReturnType<typeof setInterval> | null
|
|
56
59
|
public static override readonly VERSION = VERSION
|
|
60
|
+
/**
|
|
61
|
+
* UUIDify:
|
|
62
|
+
* We need to clone a FileBox
|
|
63
|
+
* to set uuid loader/saver with this grpc client
|
|
64
|
+
*/
|
|
57
65
|
|
|
58
66
|
constructor (public override options: PuppetEngineOptions = {} as PuppetEngineOptions) {
|
|
59
67
|
super(options)
|
|
@@ -1160,6 +1168,109 @@ class PuppetMatrix extends PUPPET.Puppet {
|
|
|
1160
1168
|
|
|
1161
1169
|
return ret
|
|
1162
1170
|
}
|
|
1171
|
+
|
|
1172
|
+
/****************************************************************************
|
|
1173
|
+
* moment section
|
|
1174
|
+
***************************************************************************/
|
|
1175
|
+
/**
|
|
1176
|
+
* 发布朋友圈
|
|
1177
|
+
* @param payload
|
|
1178
|
+
*/
|
|
1179
|
+
override async postPublish (payload: PUPPET.payloads.Post): Promise<void | string> {
|
|
1180
|
+
log.verbose(PRE, 'postPublish(%s)', payload)
|
|
1181
|
+
if (!PUPPET.payloads.isPostClient(payload)) {
|
|
1182
|
+
throw new Error('can only publish client post now')
|
|
1183
|
+
}
|
|
1184
|
+
const momentInfo:any = {
|
|
1185
|
+
content: '',
|
|
1186
|
+
mentionIdList: [],
|
|
1187
|
+
visibledList: [],
|
|
1188
|
+
imageUrls: [],
|
|
1189
|
+
videoUrl: '',
|
|
1190
|
+
urlLink: null,
|
|
1191
|
+
channel: null,
|
|
1192
|
+
miniInfo: null,
|
|
1193
|
+
location: null,
|
|
1194
|
+
rootId: '',
|
|
1195
|
+
parentId: '',
|
|
1196
|
+
}
|
|
1197
|
+
for (const item of payload.sayableList) {
|
|
1198
|
+
switch (item.type) {
|
|
1199
|
+
case PUPPET.types.Sayable.Text:
|
|
1200
|
+
momentInfo.content = `${momentInfo.content ? momentInfo.content + '\n' : ''}${item.payload.text}`
|
|
1201
|
+
momentInfo.mentionIdList = item.payload.mentions
|
|
1202
|
+
break
|
|
1203
|
+
case PUPPET.types.Sayable.Attachment: {
|
|
1204
|
+
const fileBox = item.payload.filebox as FileBoxInterface
|
|
1205
|
+
if (typeof item.payload.filebox !== 'string' && (fileBox as FileBoxInterface).type === FileBoxType.Url) {
|
|
1206
|
+
const fileType = fileBox.mediaType && fileBox.mediaType !== 'application/octet-stream' ? fileBox.mediaType : path.extname(fileBox.name)
|
|
1207
|
+
// @ts-ignore
|
|
1208
|
+
const fileUrl = fileBox.remoteUrl || ''
|
|
1209
|
+
if (fileBox.mediaType.startsWith('image/')) {
|
|
1210
|
+
momentInfo.imageUrls.push(fileUrl)
|
|
1211
|
+
} else if (fileType.includes('video/mp4') || fileType.includes('.mp4')) {
|
|
1212
|
+
momentInfo.videoInfo = fileUrl
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
break
|
|
1216
|
+
}
|
|
1217
|
+
case PUPPET.types.Sayable.Url: {
|
|
1218
|
+
momentInfo.urlLink = item.payload
|
|
1219
|
+
break
|
|
1220
|
+
}
|
|
1221
|
+
case PUPPET.types.Sayable.Channel: {
|
|
1222
|
+
momentInfo.channel = item.payload
|
|
1223
|
+
break
|
|
1224
|
+
}
|
|
1225
|
+
case PUPPET.types.Sayable.MiniProgram: {
|
|
1226
|
+
momentInfo.miniInfo = item.payload
|
|
1227
|
+
break
|
|
1228
|
+
}
|
|
1229
|
+
default:
|
|
1230
|
+
throw new Error(`postPublish unsupported type ${item.type}`)
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
if (payload.rootId) momentInfo.rootId = payload.rootId
|
|
1234
|
+
if (payload.parentId) momentInfo.parentId = payload.parentId
|
|
1235
|
+
if (payload.location) momentInfo.location = payload.location
|
|
1236
|
+
|
|
1237
|
+
momentInfo.visibleList = payload.visibleList
|
|
1238
|
+
|
|
1239
|
+
const res = await this._client?.sendSnsMoment(this._self?.wxid || '', momentInfo)
|
|
1240
|
+
|
|
1241
|
+
return res
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
override async postUnpublish (id: string): Promise<void> {
|
|
1245
|
+
log.verbose(PRE, 'postUnpublish(%s)', id)
|
|
1246
|
+
|
|
1247
|
+
await this._client?.unSendMoment(id)
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
override async postRawPayload (id: string): Promise<PUPPET.payloads.Post | string> {
|
|
1251
|
+
log.verbose(PRE, 'postRawPayload(%s)', id)
|
|
1252
|
+
return id
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
override async postPayloadSayable (postId: string, sayableId: string): Promise<PUPPET.payloads.Sayable> {
|
|
1256
|
+
log.verbose(PRE, 'postPayloadSayable(%s, %s)', postId, sayableId)
|
|
1257
|
+
return postId as any
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
override async postRawPayloadParser (payload: PUPPET.payloads.Post): Promise<PUPPET.payloads.Post> {
|
|
1261
|
+
// log.silly('PuppetService', 'postRawPayloadParser({id:%s})', payload.id)
|
|
1262
|
+
// passthrough
|
|
1263
|
+
return payload
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
override async tap (postId: string, type: PUPPET.types.Tap, tap = true): Promise<boolean | void> {
|
|
1267
|
+
log.verbose(PRE, 'tap(%s, %s, %s)', postId, type, tap)
|
|
1268
|
+
|
|
1269
|
+
const res = await this._client?.sendMomentLike(postId, type)
|
|
1270
|
+
|
|
1271
|
+
return !!res
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1163
1274
|
/****************************************************************************
|
|
1164
1275
|
* extra methods section
|
|
1165
1276
|
***************************************************************************/
|