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.
Files changed (46) hide show
  1. package/dist/cjs/src/matrix/cache-manager.js +1 -1
  2. package/dist/cjs/src/matrix/messages/message-location.d.ts +2 -0
  3. package/dist/cjs/src/matrix/messages/message-location.d.ts.map +1 -0
  4. package/dist/cjs/src/matrix/messages/message-location.js +18 -0
  5. package/dist/cjs/src/matrix/service/request.d.ts +55 -0
  6. package/dist/cjs/src/matrix/service/request.d.ts.map +1 -1
  7. package/dist/cjs/src/matrix/service/request.js +365 -1
  8. package/dist/cjs/src/matrix/utils/index.d.ts +3 -0
  9. package/dist/cjs/src/matrix/utils/index.d.ts.map +1 -1
  10. package/dist/cjs/src/matrix/utils/index.js +31 -0
  11. package/dist/cjs/src/puppet-matrix.d.ts +7 -0
  12. package/dist/cjs/src/puppet-matrix.d.ts.map +1 -1
  13. package/dist/cjs/src/puppet-matrix.js +94 -0
  14. package/dist/cjs/src/utils/normalize-filebox.d.ts +6 -0
  15. package/dist/cjs/src/utils/normalize-filebox.d.ts.map +1 -0
  16. package/dist/cjs/src/utils/normalize-filebox.js +46 -0
  17. package/dist/cjs/src/utils/sns-xml-generator.d.ts +19 -0
  18. package/dist/cjs/src/utils/sns-xml-generator.d.ts.map +1 -0
  19. package/dist/cjs/src/utils/sns-xml-generator.js +171 -0
  20. package/dist/esm/src/matrix/cache-manager.js +1 -1
  21. package/dist/esm/src/matrix/messages/message-location.d.ts +2 -0
  22. package/dist/esm/src/matrix/messages/message-location.d.ts.map +1 -0
  23. package/dist/esm/src/matrix/messages/message-location.js +15 -0
  24. package/dist/esm/src/matrix/service/request.d.ts +55 -0
  25. package/dist/esm/src/matrix/service/request.d.ts.map +1 -1
  26. package/dist/esm/src/matrix/service/request.js +365 -1
  27. package/dist/esm/src/matrix/utils/index.d.ts +3 -0
  28. package/dist/esm/src/matrix/utils/index.d.ts.map +1 -1
  29. package/dist/esm/src/matrix/utils/index.js +28 -0
  30. package/dist/esm/src/puppet-matrix.d.ts +7 -0
  31. package/dist/esm/src/puppet-matrix.d.ts.map +1 -1
  32. package/dist/esm/src/puppet-matrix.js +95 -1
  33. package/dist/esm/src/utils/normalize-filebox.d.ts +6 -0
  34. package/dist/esm/src/utils/normalize-filebox.d.ts.map +1 -0
  35. package/dist/esm/src/utils/normalize-filebox.js +42 -0
  36. package/dist/esm/src/utils/sns-xml-generator.d.ts +19 -0
  37. package/dist/esm/src/utils/sns-xml-generator.d.ts.map +1 -0
  38. package/dist/esm/src/utils/sns-xml-generator.js +165 -0
  39. package/package.json +6 -5
  40. package/src/matrix/cache-manager.ts +1 -1
  41. package/src/matrix/messages/message-location.ts +46 -0
  42. package/src/matrix/service/request.ts +483 -2
  43. package/src/matrix/utils/index.ts +62 -0
  44. package/src/puppet-matrix.ts +130 -1
  45. package/src/utils/normalize-filebox.ts +90 -0
  46. package/src/utils/sns-xml-generator.ts +184 -0
@@ -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 { FileBox } from 'file-box'
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'
@@ -18,6 +21,7 @@ import { parseEmotionMessagePayload } from './matrix/messages/message-emotion.js
18
21
  import { ImageMessagePayload, parseImageMessagePayload } from './matrix/messages/message-image.js'
19
22
  import { parseAudioMessagePayload, AudioMessagePayload } from './matrix/messages/message-audio.js'
20
23
  import { parseVideoMessagePayload, VideoMessagePayload } from './matrix/messages/message-video.js'
24
+ import { parseLocationMessagePayload } from './matrix/messages/message-location.js'
21
25
  import { CachedPromiseFunc } from './matrix/utils/cached-promise.js'
22
26
  import { engineMessageToWechaty } from './matrix/schema-mapper/message.js'
23
27
  import { engineContactToWechaty } from './matrix/schema-mapper/contact.js'
@@ -54,6 +58,11 @@ class PuppetMatrix extends PUPPET.Puppet {
54
58
  private _verifyInterval?: ReturnType<typeof setInterval> | null
55
59
  private _qrcodeStatuasInterval?: ReturnType<typeof setInterval> | null
56
60
  public static override readonly VERSION = VERSION
61
+ /**
62
+ * UUIDify:
63
+ * We need to clone a FileBox
64
+ * to set uuid loader/saver with this grpc client
65
+ */
57
66
 
58
67
  constructor (public override options: PuppetEngineOptions = {} as PuppetEngineOptions) {
59
68
  super(options)
@@ -750,6 +759,23 @@ class PuppetMatrix extends PUPPET.Puppet {
750
759
  }
751
760
  }
752
761
 
762
+ /**
763
+ * 解析h5链接
764
+ * @param messageId
765
+ */
766
+ override async messageLocation (messageId: string) : Promise<PUPPET.payloads.Location> {
767
+ const rawPayload = await this.messageRawPayload(messageId)
768
+ const payload = await this.messageRawPayloadParser(rawPayload)
769
+
770
+ if (payload.type !== PUPPET.types.Message.Location) {
771
+ throw new Error('Can not get location from non location payload')
772
+ }
773
+
774
+ // FIXME: thumb may not in appPayload.thumburl, but in appPayload.appAttachPayload
775
+ const locationPayload = await parseLocationMessagePayload(rawPayload.msg) as PUPPET.payloads.Location
776
+ return locationPayload
777
+ }
778
+
753
779
  /****************************************************************************
754
780
  * send message
755
781
  ***************************************************************************/
@@ -1160,6 +1186,109 @@ class PuppetMatrix extends PUPPET.Puppet {
1160
1186
 
1161
1187
  return ret
1162
1188
  }
1189
+
1190
+ /****************************************************************************
1191
+ * moment section
1192
+ ***************************************************************************/
1193
+ /**
1194
+ * 发布朋友圈
1195
+ * @param payload
1196
+ */
1197
+ override async postPublish (payload: PUPPET.payloads.Post): Promise<void | string> {
1198
+ log.verbose(PRE, 'postPublish(%s)', payload)
1199
+ if (!PUPPET.payloads.isPostClient(payload)) {
1200
+ throw new Error('can only publish client post now')
1201
+ }
1202
+ const momentInfo:any = {
1203
+ content: '',
1204
+ mentionIdList: [],
1205
+ visibledList: [],
1206
+ imageUrls: [],
1207
+ videoUrl: '',
1208
+ urlLink: null,
1209
+ channel: null,
1210
+ miniInfo: null,
1211
+ location: null,
1212
+ rootId: '',
1213
+ parentId: '',
1214
+ }
1215
+ for (const item of payload.sayableList) {
1216
+ switch (item.type) {
1217
+ case PUPPET.types.Sayable.Text:
1218
+ momentInfo.content = `${momentInfo.content ? momentInfo.content + '\n' : ''}${item.payload.text}`
1219
+ momentInfo.mentionIdList = item.payload.mentions
1220
+ break
1221
+ case PUPPET.types.Sayable.Attachment: {
1222
+ const fileBox = item.payload.filebox as FileBoxInterface
1223
+ if (typeof item.payload.filebox !== 'string' && (fileBox as FileBoxInterface).type === FileBoxType.Url) {
1224
+ const fileType = fileBox.mediaType && fileBox.mediaType !== 'application/octet-stream' ? fileBox.mediaType : path.extname(fileBox.name)
1225
+ // @ts-ignore
1226
+ const fileUrl = fileBox.remoteUrl || ''
1227
+ if (fileBox.mediaType.startsWith('image/')) {
1228
+ momentInfo.imageUrls.push(fileUrl)
1229
+ } else if (fileType.includes('video/mp4') || fileType.includes('.mp4')) {
1230
+ momentInfo.videoUrl = fileUrl
1231
+ }
1232
+ }
1233
+ break
1234
+ }
1235
+ case PUPPET.types.Sayable.Url: {
1236
+ momentInfo.urlLink = item.payload
1237
+ break
1238
+ }
1239
+ case PUPPET.types.Sayable.Channel: {
1240
+ momentInfo.channel = item.payload
1241
+ break
1242
+ }
1243
+ case PUPPET.types.Sayable.MiniProgram: {
1244
+ momentInfo.miniInfo = item.payload
1245
+ break
1246
+ }
1247
+ default:
1248
+ throw new Error(`postPublish unsupported type ${item.type}`)
1249
+ }
1250
+ }
1251
+ if (payload.rootId) momentInfo.rootId = payload.rootId
1252
+ if (payload.parentId) momentInfo.parentId = payload.parentId
1253
+ if (payload.location) momentInfo.location = payload.location
1254
+
1255
+ momentInfo.visibleList = payload.visibleList
1256
+
1257
+ const res = await this._client?.sendSnsMoment(this._self?.wxid || '', momentInfo)
1258
+
1259
+ return res
1260
+ }
1261
+
1262
+ override async postUnpublish (id: string): Promise<void> {
1263
+ log.verbose(PRE, 'postUnpublish(%s)', id)
1264
+
1265
+ await this._client?.unSendMoment(id)
1266
+ }
1267
+
1268
+ override async postRawPayload (id: string): Promise<PUPPET.payloads.Post | string> {
1269
+ log.verbose(PRE, 'postRawPayload(%s)', id)
1270
+ return id
1271
+ }
1272
+
1273
+ override async postPayloadSayable (postId: string, sayableId: string): Promise<PUPPET.payloads.Sayable> {
1274
+ log.verbose(PRE, 'postPayloadSayable(%s, %s)', postId, sayableId)
1275
+ return postId as any
1276
+ }
1277
+
1278
+ override async postRawPayloadParser (payload: PUPPET.payloads.Post): Promise<PUPPET.payloads.Post> {
1279
+ // log.silly('PuppetService', 'postRawPayloadParser({id:%s})', payload.id)
1280
+ // passthrough
1281
+ return payload
1282
+ }
1283
+
1284
+ override async tap (postId: string, type: PUPPET.types.Tap, tap = true): Promise<boolean | void> {
1285
+ log.verbose(PRE, 'tap(%s, %s, %s)', postId, type, tap)
1286
+
1287
+ const res = await this._client?.sendMomentLike(postId, type)
1288
+
1289
+ return !!res
1290
+ }
1291
+
1163
1292
  /****************************************************************************
1164
1293
  * extra methods section
1165
1294
  ***************************************************************************/
@@ -0,0 +1,90 @@
1
+ import type {
2
+ FileBox,
3
+ } from 'file-box'
4
+ import {
5
+ FileBoxType,
6
+ FileBoxInterface,
7
+ } from 'file-box'
8
+
9
+ /**
10
+ * Huan(202110): for testing propose, use 20KB as the threshold
11
+ * after stable we should use a value between 64KB to 256KB as the threshold
12
+ */
13
+ const PASS_THROUGH_THRESHOLD_BYTES = 20 * 1024 // 20KB
14
+
15
+ /**
16
+ * 1. Green:
17
+ * Can be serialized directly
18
+ */
19
+ const greenFileBoxTypes = [
20
+ FileBoxType.Url,
21
+ FileBoxType.Uuid,
22
+ FileBoxType.QRCode,
23
+ ]
24
+ /**
25
+ * 2. Yellow:
26
+ * Can be serialized directly, if the size is less than a threshold
27
+ * if it's bigger than the threshold,
28
+ * then it should be convert to a UUID file box before send out
29
+ */
30
+ const yellowFileBoxTypes: FileBoxType [] = [
31
+ // FileBoxType.Buffer,
32
+ // FileBoxType.Base64,
33
+ ]
34
+
35
+ const canPassthrough = (fileBox: FileBoxInterface) => {
36
+ /**
37
+ * 1. Green types: YES
38
+ */
39
+ if (greenFileBoxTypes.includes(fileBox.type)) {
40
+ return true
41
+ }
42
+
43
+ /**
44
+ * 2. Red types: NO
45
+ */
46
+ if (!yellowFileBoxTypes.includes(fileBox.type)) {
47
+ return false
48
+ }
49
+
50
+ /**
51
+ * 3. Yellow types: CHECK size
52
+ */
53
+ const size = fileBox.size
54
+ if (size < 0) {
55
+ // 1. Size unknown: NO
56
+ return false
57
+ } else if (size > PASS_THROUGH_THRESHOLD_BYTES) {
58
+ // 2. Size: bigger than threshold: NO
59
+ return false
60
+ } else {
61
+ // 3. Size: smaller than threshold: YES
62
+ return true
63
+ }
64
+
65
+ }
66
+
67
+ const normalizeFileBoxUuid = (FileBoxUuid: typeof FileBox) => async (fileBox: FileBoxInterface) => {
68
+ if (canPassthrough(fileBox)) {
69
+ return fileBox
70
+ }
71
+
72
+ const stream = await fileBox.toStream()
73
+
74
+ const uuid = await FileBoxUuid
75
+ .fromStream(stream, fileBox.name)
76
+ .toUuid()
77
+
78
+ const uuidFileBox = FileBoxUuid.fromUuid(uuid, {
79
+ md5 : fileBox.md5,
80
+ name : fileBox.name,
81
+ size : fileBox.size,
82
+ })
83
+
84
+ return uuidFileBox
85
+ }
86
+
87
+ export {
88
+ canPassthrough,
89
+ normalizeFileBoxUuid,
90
+ }
@@ -0,0 +1,184 @@
1
+ type Media = {
2
+ fileId: string
3
+ fileSize: number
4
+ fileType: number
5
+ videoDuration: number
6
+ url: string
7
+ width: number
8
+ height: number
9
+ fileUrl: string
10
+ fileKey: string
11
+ thumbUrl: string
12
+ thumbWidth: number
13
+ thumbHeight: number
14
+ }
15
+
16
+ export const genTextSnsXml = (wxid: string, content: string): string => {
17
+ const xmlTemplate = `
18
+ <TimelineObject>
19
+ <id><![CDATA[0]]></id>
20
+ <username><![CDATA[${wxid}]]></username>
21
+ <createTime><![CDATA[${Math.floor(Date.now() / 1000)}]]></createTime>
22
+ <contentDescShowType>0</contentDescShowType>
23
+ <contentDescScene>0</contentDescScene>
24
+ <private><![CDATA[0]]></private>
25
+ <contentDesc><![CDATA[${content}]]></contentDesc>
26
+ <contentattr><![CDATA[0]]></contentattr>
27
+ <sourceUserName></sourceUserName>
28
+ <sourceNickName></sourceNickName>
29
+ <statisticsData></statisticsData>
30
+ <weappInfo>
31
+ <appUserName></appUserName>
32
+ <pagePath></pagePath>
33
+ <version><![CDATA[0]]></version>
34
+ <isHidden>0</isHidden>
35
+ <debugMode><![CDATA[0]]></debugMode>
36
+ <shareActionId></shareActionId>
37
+ <isGame><![CDATA[0]]></isGame>
38
+ <messageExtraData></messageExtraData>
39
+ <subType><![CDATA[0]]></subType>
40
+ <preloadResources></preloadResources>
41
+ </weappInfo>
42
+ <canvasInfoXml></canvasInfoXml>
43
+ <ContentObject>
44
+ <contentStyle><![CDATA[2]]></contentStyle>
45
+ <contentSubStyle><![CDATA[0]]></contentSubStyle>
46
+ <title></title>
47
+ <description></description>
48
+ <contentUrl></contentUrl>
49
+ </ContentObject>
50
+ <actionInfo>
51
+ <appMsg>
52
+ <mediaTagName></mediaTagName>
53
+ <messageExt></messageExt>
54
+ <messageAction></messageAction>
55
+ </appMsg>
56
+ </actionInfo>
57
+ <appInfo><id></id></appInfo>
58
+ <publicUserName></publicUserName>
59
+ <streamvideo>
60
+ <streamvideourl></streamvideourl>
61
+ <streamvideothumburl></streamvideothumburl>
62
+ <streamvideoweburl></streamvideoweburl>
63
+ </streamvideo>
64
+ </TimelineObject>`.replace(/\s+/g, '')
65
+ return xmlTemplate
66
+ }
67
+
68
+ export const genVideoSnsXml = (wxid: string, content: string, media: Media): string => {
69
+ const xmlTemplate = `
70
+ <TimelineObject>
71
+ <id>0</id>
72
+ <username>${wxid}</username>
73
+ <createTime>${Math.floor(Date.now() / 1000)}</createTime>
74
+ <contentDesc>${content}</contentDesc>
75
+ <contentDescShowType>0</contentDescShowType>
76
+ <contentDescScene>0</contentDescScene>
77
+ <private>0</private>
78
+ <sightFolded>0</sightFolded>
79
+ <showFlag>0</showFlag>
80
+ <appInfo>
81
+ <id></id>
82
+ <version></version>
83
+ <appName></appName>
84
+ <installUrl></installUrl>
85
+ <fromUrl></fromUrl>
86
+ <isForceUpdate>0</isForceUpdate>
87
+ <isHidden>0</isHidden>
88
+ </appInfo>
89
+ <sourceUserName></sourceUserName>
90
+ <sourceNickName></sourceNickName>
91
+ <statisticsData></statisticsData>
92
+ <statExtStr></statExtStr>
93
+ <ContentObject>
94
+ <contentStyle>15</contentStyle>
95
+ <title></title>
96
+ <description>Sight</description>
97
+ <mediaList>
98
+ <media>
99
+ <id>0</id>
100
+ <type>6</type>
101
+ <title></title>
102
+ <description>测试</description>
103
+ <private>0</private>
104
+ <userData></userData>
105
+ <subType>0</subType>
106
+ <videoSize width="${media.width}" height="${media.height}"/>
107
+ <url type="1" md5="951a7d7864d685a92fd2624155794bf9" videomd5="577f55635faf44f595a69ded26d87bcc">${media.fileUrl}</url>
108
+ <thumb type="1">${media.thumbUrl}</thumb>
109
+ <size width="${media.thumbWidth}.000000" height="${media.thumbHeight}.000000" totalSize="${media.fileSize}"/>
110
+ <videoDuration>${media.videoDuration}.000000</videoDuration>
111
+ </media>
112
+ </mediaList>
113
+ </ContentObject>
114
+ </TimelineObject>`.replace(/\s+/g, '')
115
+ return xmlTemplate
116
+ }
117
+
118
+ export const genImageSnsXml = (wxid: string, contentDesc: string, mediaList: Media[]): string => {
119
+ const mediaTemplate = (media: Media) => `
120
+ <media>
121
+ <id><![CDATA[0]]></id>
122
+ <type><![CDATA[2]]></type>
123
+ <title></title>
124
+ <description></description>
125
+ <private><![CDATA[0]]></private>
126
+ <url type="1" md5="951a7d7864d685a92fd2624155794bf9"><![CDATA[${media.fileUrl}]]></url>
127
+ <thumb type="1"><![CDATA[${media.thumbUrl}]]></thumb>
128
+ <videoDuration><![CDATA[0.0]]></videoDuration>
129
+ <size totalSize="${media.fileSize}" width="${media.width}" height="${media.height}"></size>
130
+ </media>`.replace(/\s+/g, '')
131
+
132
+ const mediaString = mediaList.map(media => mediaTemplate(media)).join('')
133
+
134
+ const xmlTemplate = `
135
+ <TimelineObject>
136
+ <id><![CDATA[0]]></id>
137
+ <username><![CDATA[${wxid}]]></username>
138
+ <createTime><![CDATA[${Math.floor(Date.now() / 1000)}]]></createTime>
139
+ <contentDescShowType>0</contentDescShowType>
140
+ <contentDescScene>0</contentDescScene>
141
+ <private><![CDATA[0]]></private>
142
+ <contentDesc><![CDATA[${contentDesc}]]></contentDesc>
143
+ <contentattr><![CDATA[0]]></contentattr>
144
+ <sourceUserName></sourceUserName>
145
+ <sourceNickName></sourceNickName>
146
+ <statisticsData></statisticsData>
147
+ <weappInfo>
148
+ <appUserName></appUserName>
149
+ <pagePath></pagePath>
150
+ <version><![CDATA[0]]></version>
151
+ <isHidden>0</isHidden>
152
+ <debugMode><![CDATA[0]]></debugMode>
153
+ <shareActionId></shareActionId>
154
+ <isGame><![CDATA[0]]></isGame>
155
+ <messageExtraData></messageExtraData>
156
+ <subType><![CDATA[0]]></subType>
157
+ <preloadResources></preloadResources>
158
+ </weappInfo>
159
+ <canvasInfoXml></canvasInfoXml>
160
+ <ContentObject>
161
+ <contentStyle><![CDATA[1]]></contentStyle>
162
+ <contentSubStyle><![CDATA[0]]></contentSubStyle>
163
+ <title></title>
164
+ <description></description>
165
+ <contentUrl></contentUrl>
166
+ <mediaList>${mediaString}</mediaList>
167
+ </ContentObject>
168
+ <actionInfo>
169
+ <appMsg>
170
+ <mediaTagName></mediaTagName>
171
+ <messageExt></messageExt>
172
+ <messageAction></messageAction>
173
+ </appMsg>
174
+ </actionInfo>
175
+ <appInfo><id></id></appInfo>
176
+ <publicUserName></publicUserName>
177
+ <streamvideo>
178
+ <streamvideourl></streamvideourl>
179
+ <streamvideothumburl></streamvideothumburl>
180
+ <streamvideoweburl></streamvideoweburl>
181
+ </streamvideo>
182
+ </TimelineObject>`.replace(/\s+/g, '')
183
+ return xmlTemplate
184
+ }