wechaty-puppet-matrix 0.0.10 → 0.0.13
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/messages/message-location.d.ts +2 -0
- package/dist/cjs/src/matrix/messages/message-location.d.ts.map +1 -0
- package/dist/cjs/src/matrix/messages/message-location.js +18 -0
- 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 +365 -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 +7 -0
- package/dist/cjs/src/puppet-matrix.d.ts.map +1 -1
- package/dist/cjs/src/puppet-matrix.js +94 -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/messages/message-location.d.ts +2 -0
- package/dist/esm/src/matrix/messages/message-location.d.ts.map +1 -0
- package/dist/esm/src/matrix/messages/message-location.js +15 -0
- 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 +365 -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 +7 -0
- package/dist/esm/src/puppet-matrix.d.ts.map +1 -1
- package/dist/esm/src/puppet-matrix.js +95 -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/messages/message-location.ts +46 -0
- package/src/matrix/service/request.ts +483 -2
- package/src/matrix/utils/index.ts +62 -0
- package/src/puppet-matrix.ts +130 -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,268 @@ 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
|
+
)
|
|
396
|
+
const baseDirExist = await fs.pathExists(tempFilePath)
|
|
397
|
+
if (!baseDirExist) {
|
|
398
|
+
await fs.mkdirp(tempFilePath)
|
|
399
|
+
}
|
|
400
|
+
const finalFilePath = path.join(
|
|
401
|
+
tempFilePath,
|
|
402
|
+
'temp_image_' + Date.now() + path.extname(imageUrl),
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
await fs.writeFile(finalFilePath, response.data)
|
|
406
|
+
|
|
407
|
+
// 4. 获取图片宽高
|
|
408
|
+
const dimensions = imageSize(finalFilePath)
|
|
409
|
+
|
|
410
|
+
// 5. 删除临时文件
|
|
411
|
+
await fs.unlink(finalFilePath)
|
|
412
|
+
|
|
413
|
+
return {
|
|
414
|
+
file_size: fileSize, // 文件大小(字节)
|
|
415
|
+
image_width: dimensions.width,
|
|
416
|
+
image_height: dimensions.height,
|
|
417
|
+
file_type: dimensions.type,
|
|
418
|
+
}
|
|
419
|
+
} catch (error) {
|
|
420
|
+
console.error('获取图片信息错误:', error)
|
|
421
|
+
throw error
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function splitAddress (address = '') {
|
|
426
|
+
if (!address) {
|
|
427
|
+
return { province: '', city: '', area: '' }
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
let province: any = ''
|
|
431
|
+
let city: any = ''
|
|
432
|
+
let area: any = ''
|
|
433
|
+
|
|
434
|
+
// 省级行政区划
|
|
435
|
+
const provinceRegex = /^(.*?(省|自治区|市))|^(.*?)(北京|天津|上海|重庆)市?$/
|
|
436
|
+
const provinceMatch = address.match(provinceRegex)
|
|
437
|
+
if (provinceMatch) {
|
|
438
|
+
province = provinceMatch[1] || provinceMatch[3]
|
|
439
|
+
address = address.replace(province, '')
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// 市级行政区划
|
|
443
|
+
const cityRegex = /^(.*?(市|自治州|地区|盟))|^([^\u4e00-\u9fa5]*)(北京|天津|上海|重庆)$/
|
|
444
|
+
const cityMatch = address.match(cityRegex)
|
|
445
|
+
if (cityMatch) {
|
|
446
|
+
city = cityMatch[1] || cityMatch[3]
|
|
447
|
+
address = address.replace(city, '')
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// 区/县级行政区划
|
|
451
|
+
const areaRegex = /^(.*?(区|县|市|旗|自治县|林区|特区))|^(.*?(街道|镇|乡))|^([^\u4e00-\u9fa5]*)/
|
|
452
|
+
const areaMatch = address.match(areaRegex)
|
|
453
|
+
if (areaMatch) {
|
|
454
|
+
area = areaMatch[1] || areaMatch[3] || areaMatch[4]
|
|
455
|
+
address = address.replace(area, '')
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
return { province, city, area }
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const genTextPosSnsXml = (wxid: string, content: string, location: any): string => {
|
|
462
|
+
const addressInfo: any = location ? splitAddress(location.address) : {}
|
|
463
|
+
const xmlTemplate = `
|
|
464
|
+
<TimelineObject>
|
|
465
|
+
<id>0</id>
|
|
466
|
+
<username><![CDATA[${wxid}]]></username>
|
|
467
|
+
<createTime><![CDATA[${Math.floor(Date.now() / 1000)}]]></createTime>
|
|
468
|
+
<contentDesc><![CDATA[${content}]]></contentDesc>
|
|
469
|
+
<contentDescShowType>0</contentDescShowType>
|
|
470
|
+
<contentDescScene>3</contentDescScene>
|
|
471
|
+
<private>0</private>
|
|
472
|
+
<sightFolded>0</sightFolded>
|
|
473
|
+
<showFlag>0</showFlag>${location ? `<location city="${addressInfo.city}" longitude="${location.longitude}" latitude="${location.latitude}" poiName="${location.name}" poiAddress="${location.address}" poiScale="11.000000" poiInfoUrl="" poiClassifyId="${location.poiId}" poiClassifyType="1" poiClickableStatus="0" buildingId="0" floorName=""/>` : ''}
|
|
474
|
+
<appInfo>
|
|
475
|
+
<id/>
|
|
476
|
+
<version/>
|
|
477
|
+
<appName/>
|
|
478
|
+
<installUrl/>
|
|
479
|
+
<fromUrl/>
|
|
480
|
+
<isForceUpdate>0</isForceUpdate>
|
|
481
|
+
<isHidden>0</isHidden>
|
|
482
|
+
</appInfo>
|
|
483
|
+
<sourceUserName/>
|
|
484
|
+
<sourceNickName/>
|
|
485
|
+
<statisticsData/>
|
|
486
|
+
<statExtStr/>
|
|
487
|
+
<ContentObject>
|
|
488
|
+
<contentStyle>2</contentStyle>
|
|
489
|
+
<title/>
|
|
490
|
+
<description/>
|
|
491
|
+
<mediaList/>
|
|
492
|
+
</ContentObject>
|
|
493
|
+
</TimelineObject>`
|
|
494
|
+
return xmlTemplate
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
const genVideoSnsXml = (wxid: string, content: string, media: Media, location: any): string => {
|
|
498
|
+
const addressInfo: any = location ? splitAddress(location.address) : {}
|
|
499
|
+
|
|
500
|
+
const xmlTemplate = `
|
|
501
|
+
<TimelineObject>
|
|
502
|
+
<id>0</id>
|
|
503
|
+
<username>${wxid}</username>
|
|
504
|
+
<createTime>${Math.floor(Date.now() / 1000)}</createTime>
|
|
505
|
+
<contentDesc>${content}</contentDesc>
|
|
506
|
+
<contentDescShowType>0</contentDescShowType>
|
|
507
|
+
<contentDescScene>0</contentDescScene>
|
|
508
|
+
<private>0</private>
|
|
509
|
+
<sightFolded>0</sightFolded>
|
|
510
|
+
<showFlag>0</showFlag>${location ? `<location city="${addressInfo.city}" longitude="${location.longitude}" latitude="${location.latitude}" poiName="${location.name}" poiAddress="${location.address}" poiScale="11.000000" poiInfoUrl="" poiClassifyId="${location.poiId}" poiClassifyType="1" poiClickableStatus="0" buildingId="0" floorName=""/>` : ''}
|
|
511
|
+
<appInfo>
|
|
512
|
+
<id></id>
|
|
513
|
+
<version></version>
|
|
514
|
+
<appName></appName>
|
|
515
|
+
<installUrl></installUrl>
|
|
516
|
+
<fromUrl></fromUrl>
|
|
517
|
+
<isForceUpdate>0</isForceUpdate>
|
|
518
|
+
<isHidden>0</isHidden>
|
|
519
|
+
</appInfo>
|
|
520
|
+
<sourceUserName></sourceUserName>
|
|
521
|
+
<sourceNickName></sourceNickName>
|
|
522
|
+
<statisticsData></statisticsData>
|
|
523
|
+
<statExtStr></statExtStr>
|
|
524
|
+
<ContentObject>
|
|
525
|
+
<contentStyle>15</contentStyle>
|
|
526
|
+
<title>微信小视频</title>
|
|
527
|
+
<description>Sight</description>
|
|
528
|
+
<mediaList>
|
|
529
|
+
<media>
|
|
530
|
+
<id>0</id>
|
|
531
|
+
<type>6</type>
|
|
532
|
+
<title></title>
|
|
533
|
+
<description>${content}</description>
|
|
534
|
+
<private>0</private>
|
|
535
|
+
<userData></userData>
|
|
536
|
+
<subType>0</subType>
|
|
537
|
+
<videoSize width="${media.video_width}" height="${media.video_height}"/>
|
|
538
|
+
<url type="1" md5="951a7d7864d685a92fd2624155794bf9" videomd5="577f55635faf44f595a69ded26d87bcc">${media.file_url}</url>
|
|
539
|
+
<thumb type="1">${media.thumb_url}</thumb>
|
|
540
|
+
<size width="${media.thumb_width}.000000" height="${media.thumb_height}.000000" totalSize="${media.file_size}"/>
|
|
541
|
+
<videoDuration>${media.video_duration}.000000</videoDuration>
|
|
542
|
+
</media>
|
|
543
|
+
</mediaList>
|
|
544
|
+
<contentUrl>https://support.weixin.qq.com/cgi-bin/mmsupport-bin/readtemplate?t=page/common_page__upgrade&v=1</contentUrl>
|
|
545
|
+
</ContentObject>
|
|
546
|
+
</TimelineObject>`
|
|
547
|
+
return xmlTemplate
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
const genImageSnsXml = (wxid: string, contentDesc: string, mediaList: Media[], location: any): string => {
|
|
551
|
+
const mediaTemplate = (media: Media) => `
|
|
552
|
+
<media>
|
|
553
|
+
<id><![CDATA[0]]></id>
|
|
554
|
+
<type><![CDATA[2]]></type>
|
|
555
|
+
<title></title>
|
|
556
|
+
<description></description>
|
|
557
|
+
<private><![CDATA[0]]></private>
|
|
558
|
+
<url type="1" md5="951a7d7864d685a92fd2624155794bf9"><![CDATA[${media.file_url}]]></url>
|
|
559
|
+
<thumb type="1"><![CDATA[${media.thumb_url}]]></thumb>
|
|
560
|
+
<videoDuration><![CDATA[0.0]]></videoDuration>
|
|
561
|
+
<size totalSize="${media.file_size}" width="${media.image_width}" height="${media.image_height}"></size>
|
|
562
|
+
</media>`
|
|
563
|
+
|
|
564
|
+
const mediaString = mediaList.map(media => mediaTemplate(media)).join('')
|
|
565
|
+
const addressInfo: any = location ? splitAddress(location.address) : {}
|
|
566
|
+
const xmlTemplate = `
|
|
567
|
+
<TimelineObject>
|
|
568
|
+
<id><![CDATA[0]]></id>
|
|
569
|
+
<username><![CDATA[${wxid}]]></username>
|
|
570
|
+
<createTime><![CDATA[${Math.floor(Date.now() / 1000)}]]></createTime>
|
|
571
|
+
<contentDescShowType>0</contentDescShowType>
|
|
572
|
+
<contentDescScene>0</contentDescScene>
|
|
573
|
+
<private><![CDATA[0]]></private>
|
|
574
|
+
<contentDesc><![CDATA[${contentDesc}]]></contentDesc>
|
|
575
|
+
<contentattr><![CDATA[0]]></contentattr>
|
|
576
|
+
<sourceUserName></sourceUserName>
|
|
577
|
+
<sourceNickName></sourceNickName>
|
|
578
|
+
<statisticsData></statisticsData>
|
|
579
|
+
<weappInfo>
|
|
580
|
+
<appUserName></appUserName>
|
|
581
|
+
<pagePath></pagePath>
|
|
582
|
+
<version><![CDATA[0]]></version>
|
|
583
|
+
<isHidden>0</isHidden>
|
|
584
|
+
<debugMode><![CDATA[0]]></debugMode>
|
|
585
|
+
<shareActionId></shareActionId>
|
|
586
|
+
<isGame><![CDATA[0]]></isGame>
|
|
587
|
+
<messageExtraData></messageExtraData>
|
|
588
|
+
<subType><![CDATA[0]]></subType>
|
|
589
|
+
<preloadResources></preloadResources>
|
|
590
|
+
</weappInfo>
|
|
591
|
+
<canvasInfoXml></canvasInfoXml>
|
|
592
|
+
${location ? `<location city="${addressInfo.city}" longitude="${location.longitude}" latitude="${location.latitude}" poiName="${location.name}" poiAddress="${location.address}" poiScale="11.000000" poiInfoUrl="" poiClassifyId="${location.poiId}" poiClassifyType="1" poiClickableStatus="0" buildingId="0" floorName=""/>` : ''}
|
|
593
|
+
<ContentObject>
|
|
594
|
+
<contentStyle><![CDATA[1]]></contentStyle>
|
|
595
|
+
<contentSubStyle><![CDATA[0]]></contentSubStyle>
|
|
596
|
+
<title></title>
|
|
597
|
+
<description></description>
|
|
598
|
+
<contentUrl></contentUrl>
|
|
599
|
+
<mediaList>${mediaString}</mediaList>
|
|
600
|
+
</ContentObject>
|
|
601
|
+
<actionInfo>
|
|
602
|
+
<appMsg>
|
|
603
|
+
<mediaTagName></mediaTagName>
|
|
604
|
+
<messageExt></messageExt>
|
|
605
|
+
<messageAction></messageAction>
|
|
606
|
+
</appMsg>
|
|
607
|
+
</actionInfo>
|
|
608
|
+
<appInfo><id></id></appInfo>
|
|
609
|
+
<publicUserName></publicUserName>
|
|
610
|
+
<streamvideo>
|
|
611
|
+
<streamvideourl></streamvideourl>
|
|
612
|
+
<streamvideothumburl></streamvideothumburl>
|
|
613
|
+
<streamvideoweburl></streamvideoweburl>
|
|
614
|
+
</streamvideo>
|
|
615
|
+
</TimelineObject>`
|
|
616
|
+
return xmlTemplate
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
export interface MomentInfo {
|
|
620
|
+
content: string,
|
|
621
|
+
mentionIdList: string[],
|
|
622
|
+
visibledList: string[],
|
|
623
|
+
imageUrls: string[],
|
|
624
|
+
videoUrl: string,
|
|
625
|
+
urlLink: null | PUPPET.payloads.UrlLink,
|
|
626
|
+
channel: null | PUPPET.payloads.Channel,
|
|
627
|
+
miniInfo: null | PUPPET.payloads.MiniProgram,
|
|
628
|
+
location: null | PUPPET.payloads.Location,
|
|
629
|
+
rootId: string,
|
|
630
|
+
parentId: string
|
|
631
|
+
}
|
|
632
|
+
|
|
336
633
|
class Client extends EventEmitter {
|
|
337
634
|
|
|
338
635
|
private readonly options: PuppetMatrixOptions
|
|
@@ -2051,7 +2348,7 @@ class Client extends EventEmitter {
|
|
|
2051
2348
|
announcement: content,
|
|
2052
2349
|
},
|
|
2053
2350
|
})
|
|
2054
|
-
if (res
|
|
2351
|
+
if (res?.baseResponse?.ret) {
|
|
2055
2352
|
log.error('sendAnnouncement error: %s', JSON.stringify(res.baseResponse))
|
|
2056
2353
|
}
|
|
2057
2354
|
} catch (error) {
|
|
@@ -2059,6 +2356,190 @@ class Client extends EventEmitter {
|
|
|
2059
2356
|
}
|
|
2060
2357
|
}
|
|
2061
2358
|
|
|
2359
|
+
/**
|
|
2360
|
+
* 上传朋友圈图片
|
|
2361
|
+
* @param url
|
|
2362
|
+
* @returns
|
|
2363
|
+
*/
|
|
2364
|
+
public async uploadSnsImage (url: string): Promise<SnsImageMessagePayload | void> {
|
|
2365
|
+
try {
|
|
2366
|
+
const res = await this.postData({
|
|
2367
|
+
path: '/cloud/cdn_upload_sns_image',
|
|
2368
|
+
data: {
|
|
2369
|
+
url,
|
|
2370
|
+
},
|
|
2371
|
+
})
|
|
2372
|
+
if (res?.errcode !== 0) {
|
|
2373
|
+
log.error('uploadSnsImage error: %s', JSON.stringify(res))
|
|
2374
|
+
return
|
|
2375
|
+
}
|
|
2376
|
+
return res as SnsImageMessagePayload
|
|
2377
|
+
} catch (error) {
|
|
2378
|
+
log.error(PRE, 'uploadSnsImage(%s): %s', url, error)
|
|
2379
|
+
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2382
|
+
|
|
2383
|
+
/**
|
|
2384
|
+
* 上传朋友视频
|
|
2385
|
+
* @param url
|
|
2386
|
+
* @returns
|
|
2387
|
+
*/
|
|
2388
|
+
public async uploadSnsVideo (url: string): Promise<SnsVideoMessagePayload | void> {
|
|
2389
|
+
try {
|
|
2390
|
+
const res = await this.postData({
|
|
2391
|
+
path: '/cloud/cdn_upload_sns_video',
|
|
2392
|
+
data: {
|
|
2393
|
+
url,
|
|
2394
|
+
},
|
|
2395
|
+
})
|
|
2396
|
+
log.info('uploadSnsVideo result: %s', res)
|
|
2397
|
+
if (res?.errcode !== 0) {
|
|
2398
|
+
log.error('uploadSnsVideo error: %s', JSON.stringify(res))
|
|
2399
|
+
}
|
|
2400
|
+
return res as SnsVideoMessagePayload
|
|
2401
|
+
} catch (error) {
|
|
2402
|
+
log.error(PRE, 'updateSnsImage(%s): %s', url, error)
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
|
|
2406
|
+
/**
|
|
2407
|
+
* 发送朋友圈
|
|
2408
|
+
* @param wxid
|
|
2409
|
+
* @param momentInfo
|
|
2410
|
+
*/
|
|
2411
|
+
public async sendSnsMoment (wxid: string, momentInfo: MomentInfo) {
|
|
2412
|
+
try {
|
|
2413
|
+
log.info('momentInfo: %s', JSON.stringify(momentInfo))
|
|
2414
|
+
let xmlContent = ''
|
|
2415
|
+
// 朋友圈评论
|
|
2416
|
+
if (momentInfo.parentId && momentInfo.rootId) {
|
|
2417
|
+
return await this.sendMomentReply(momentInfo.rootId, momentInfo.content, momentInfo.parentId === momentInfo.rootId ? '' : momentInfo.parentId)
|
|
2418
|
+
}
|
|
2419
|
+
if (momentInfo.imageUrls.length) {
|
|
2420
|
+
const imageInfo: any[] = []
|
|
2421
|
+
for (const image of momentInfo.imageUrls) {
|
|
2422
|
+
const res = await this.uploadSnsImage(image)
|
|
2423
|
+
if (res) {
|
|
2424
|
+
const imageBaseInfo = await getImageInfo(image)
|
|
2425
|
+
imageInfo.push({ ...res, ...imageBaseInfo })
|
|
2426
|
+
}
|
|
2427
|
+
xmlContent = genImageSnsXml(wxid, momentInfo.content, imageInfo, momentInfo.location)
|
|
2428
|
+
|
|
2429
|
+
}
|
|
2430
|
+
} else if (momentInfo.videoUrl) {
|
|
2431
|
+
const mediaInfo: any = await this.uploadSnsVideo(momentInfo.videoUrl)
|
|
2432
|
+
if (mediaInfo) {
|
|
2433
|
+
const thumbInfo = await getImageInfo(mediaInfo.thumb_url)
|
|
2434
|
+
xmlContent = genVideoSnsXml(wxid, momentInfo.content, { ...mediaInfo, ...thumbInfo }, momentInfo.location)
|
|
2435
|
+
}
|
|
2436
|
+
} else {
|
|
2437
|
+
xmlContent = genTextPosSnsXml(wxid, momentInfo.content, momentInfo.location)
|
|
2438
|
+
|
|
2439
|
+
}
|
|
2440
|
+
return await this.sendMoment(xmlContent)
|
|
2441
|
+
} catch (error) {
|
|
2442
|
+
log.error(PRE, 'sendSnsMoment(%s): %s', JSON.stringify(momentInfo), error)
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
|
|
2446
|
+
public async sendMoment (content: string):Promise<string | void> {
|
|
2447
|
+
try {
|
|
2448
|
+
const res = await this.postData({
|
|
2449
|
+
path: '/sns/sns_post',
|
|
2450
|
+
data: {
|
|
2451
|
+
object_desc: content,
|
|
2452
|
+
with_user_list: [],
|
|
2453
|
+
block_user_list: [],
|
|
2454
|
+
group_user_list: [],
|
|
2455
|
+
group_contact_tag_id_list: [],
|
|
2456
|
+
black_contact_tag_id_list: [],
|
|
2457
|
+
},
|
|
2458
|
+
})
|
|
2459
|
+
if (res?.baseResponse?.ret !== 0) {
|
|
2460
|
+
log.error('sendMoment error: %s', JSON.stringify(res))
|
|
2461
|
+
return
|
|
2462
|
+
}
|
|
2463
|
+
log.info(PRE, 'sendMomen success: %s', res?.snsObject?.id)
|
|
2464
|
+
return res?.snsObject?.id
|
|
2465
|
+
} catch (error) {
|
|
2466
|
+
log.error(PRE, 'sendMoment(%s): %s', content, error)
|
|
2467
|
+
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
public async unSendMoment (objectId: string):Promise<string | void> {
|
|
2472
|
+
try {
|
|
2473
|
+
const res = await this.postData({
|
|
2474
|
+
path: '/sns/sns_delete',
|
|
2475
|
+
data: {
|
|
2476
|
+
object_id: objectId,
|
|
2477
|
+
},
|
|
2478
|
+
})
|
|
2479
|
+
if (res?.baseResponse?.ret !== 0) {
|
|
2480
|
+
log.error('unSendMoment error: %s', JSON.stringify(res))
|
|
2481
|
+
return
|
|
2482
|
+
}
|
|
2483
|
+
return objectId
|
|
2484
|
+
} catch (error) {
|
|
2485
|
+
log.error(PRE, 'unSendMoment(%s): %s', objectId, error)
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
|
|
2489
|
+
/**
|
|
2490
|
+
* 朋友圈点赞
|
|
2491
|
+
* @param objectId
|
|
2492
|
+
* @param status 1 赞 0 取消点赞
|
|
2493
|
+
* @returns
|
|
2494
|
+
*/
|
|
2495
|
+
public async sendMomentLike (objectId: string, status: PUPPET.types.Tap):Promise<string | void> {
|
|
2496
|
+
try {
|
|
2497
|
+
const res = await this.postData({
|
|
2498
|
+
path: '/sns/sns_like',
|
|
2499
|
+
data: {
|
|
2500
|
+
object_id: objectId,
|
|
2501
|
+
status,
|
|
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
|
+
|
|
2515
|
+
/**
|
|
2516
|
+
* 朋友圈回复
|
|
2517
|
+
* @param objectId 朋友圈id
|
|
2518
|
+
* @param content
|
|
2519
|
+
* @param commentId 评论id
|
|
2520
|
+
* @returns
|
|
2521
|
+
*/
|
|
2522
|
+
public async sendMomentReply (objectId: string, content: string, commentId?: string):Promise<string | void> {
|
|
2523
|
+
try {
|
|
2524
|
+
const res = await this.postData({
|
|
2525
|
+
path: '/sns/sns_comment',
|
|
2526
|
+
data: {
|
|
2527
|
+
object_id: objectId,
|
|
2528
|
+
content,
|
|
2529
|
+
reply_comment_id: commentId || '0',
|
|
2530
|
+
},
|
|
2531
|
+
})
|
|
2532
|
+
if (res?.baseResponse?.ret !== 0) {
|
|
2533
|
+
log.error('sendMomentLike error: %s', JSON.stringify(res))
|
|
2534
|
+
return
|
|
2535
|
+
}
|
|
2536
|
+
return res?.snsObject?.id
|
|
2537
|
+
} catch (error) {
|
|
2538
|
+
log.error(PRE, 'sendMomentLike(%s): %s', objectId, error)
|
|
2539
|
+
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
|
|
2062
2543
|
}
|
|
2063
2544
|
|
|
2064
2545
|
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
|
+
}
|