wechaty-puppet-matrix 0.0.55 → 0.0.56
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/engine-schema.d.ts +1 -0
- package/dist/cjs/src/engine-schema.d.ts.map +1 -1
- package/dist/cjs/src/matrix/messages/message-contact-card.d.ts +16 -0
- package/dist/cjs/src/matrix/messages/message-contact-card.d.ts.map +1 -0
- package/dist/cjs/src/matrix/messages/message-contact-card.js +25 -0
- package/dist/cjs/src/matrix/messages/message-emotion.d.ts +7 -0
- package/dist/cjs/src/matrix/messages/message-emotion.d.ts.map +1 -1
- package/dist/cjs/src/matrix/messages/message-emotion.js +20 -0
- package/dist/cjs/src/matrix/service/request.d.ts +12 -0
- package/dist/cjs/src/matrix/service/request.d.ts.map +1 -1
- package/dist/cjs/src/matrix/service/request.js +138 -0
- package/dist/cjs/src/puppet-matrix.d.ts +2 -0
- package/dist/cjs/src/puppet-matrix.d.ts.map +1 -1
- package/dist/cjs/src/puppet-matrix.js +105 -32
- package/dist/esm/src/engine-schema.d.ts +1 -0
- package/dist/esm/src/engine-schema.d.ts.map +1 -1
- package/dist/esm/src/matrix/messages/message-contact-card.d.ts +16 -0
- package/dist/esm/src/matrix/messages/message-contact-card.d.ts.map +1 -0
- package/dist/esm/src/matrix/messages/message-contact-card.js +22 -0
- package/dist/esm/src/matrix/messages/message-emotion.d.ts +7 -0
- package/dist/esm/src/matrix/messages/message-emotion.d.ts.map +1 -1
- package/dist/esm/src/matrix/messages/message-emotion.js +19 -0
- package/dist/esm/src/matrix/service/request.d.ts +12 -0
- package/dist/esm/src/matrix/service/request.d.ts.map +1 -1
- package/dist/esm/src/matrix/service/request.js +138 -0
- package/dist/esm/src/puppet-matrix.d.ts +2 -0
- package/dist/esm/src/puppet-matrix.d.ts.map +1 -1
- package/dist/esm/src/puppet-matrix.js +105 -32
- package/package.json +1 -1
- package/src/engine-schema.ts +1 -0
- package/src/matrix/messages/message-contact-card.ts +48 -0
- package/src/matrix/messages/message-emotion.ts +36 -0
- package/src/matrix/service/request.ts +189 -0
- package/src/puppet-matrix.ts +130 -34
package/src/puppet-matrix.ts
CHANGED
|
@@ -20,11 +20,12 @@ import { isIMContactId, isRoomId } from './matrix/utils/is-type.js'
|
|
|
20
20
|
import { AppMessagePayload, AppMessageType, parseAppmsgMessagePayload } from './matrix/messages/message-appmsg.js'
|
|
21
21
|
import { parseRecordItem, RecordItem, RecordItemType } from './matrix/messages/message-chat-history.js'
|
|
22
22
|
import { parseMiniProgramMessagePayload } from './matrix/messages/message-miniprogram.js'
|
|
23
|
-
import { parseEmotionMessagePayload } from './matrix/messages/message-emotion.js'
|
|
23
|
+
import { EmojiMessagePayload, parseEmotionMessagePayload } from './matrix/messages/message-emotion.js'
|
|
24
24
|
import { ImageMessagePayload, parseImageMessagePayload } from './matrix/messages/message-image.js'
|
|
25
25
|
import { parseAudioMessagePayload, AudioMessagePayload } from './matrix/messages/message-audio.js'
|
|
26
26
|
import { parseVideoMessagePayload, VideoMessagePayload } from './matrix/messages/message-video.js'
|
|
27
27
|
import { parseLocationMessagePayload } from './matrix/messages/message-location.js'
|
|
28
|
+
import { ContactCardPayload, parseContactCardMessagePayload } from './matrix/messages/message-contact-card.js'
|
|
28
29
|
import { CachedPromiseFunc } from './matrix/utils/cached-promise.js'
|
|
29
30
|
import { engineMessageToWechaty } from './matrix/schema-mapper/message.js'
|
|
30
31
|
import { engineContactToWechaty } from './matrix/schema-mapper/contact.js'
|
|
@@ -36,7 +37,6 @@ import {
|
|
|
36
37
|
import { parseEvent, EventType } from './matrix/events/mod.js'
|
|
37
38
|
import { addRunningPuppet, removeRunningPuppet } from './cleanup.js'
|
|
38
39
|
import { packageJson } from './package-json.js'
|
|
39
|
-
import { JsonToXml } from './matrix/utils/xml-to-json.js'
|
|
40
40
|
|
|
41
41
|
const VERSION = packageJson.version || '0.0.0'
|
|
42
42
|
const PRE = '[PuppetMatrix]'
|
|
@@ -656,7 +656,8 @@ class PuppetMatrix extends PUPPET.Puppet {
|
|
|
656
656
|
hello = (option as any).hello
|
|
657
657
|
}
|
|
658
658
|
}
|
|
659
|
-
|
|
659
|
+
// 搜索联系人的 id 带有 SEARCH_CONTACT_PREFIX, 真实用户名在缓存的 wxid 里
|
|
660
|
+
await this._client?.addFriendByWxid({ content: hello, ticket, scene: addContactScene, wxid: cachedContactSearch?.wxid || contactId })
|
|
660
661
|
}
|
|
661
662
|
|
|
662
663
|
/**
|
|
@@ -725,7 +726,70 @@ class PuppetMatrix extends PUPPET.Puppet {
|
|
|
725
726
|
// 名片
|
|
726
727
|
override async messageContact (messageId: string): Promise<string> {
|
|
727
728
|
log.verbose('PuppetWeChat', 'messageContact(%s)', messageId)
|
|
728
|
-
|
|
729
|
+
const rawPayload = await this.messageRawPayload(messageId)
|
|
730
|
+
if (rawPayload.msgType !== WechatMessageType.ShareCard) {
|
|
731
|
+
throw new Error(`message ${messageId} is not a contact card`)
|
|
732
|
+
}
|
|
733
|
+
const card = await parseContactCardMessagePayload(rawPayload.msg)
|
|
734
|
+
if (!card) {
|
|
735
|
+
throw new Error(`can not parse contact card from message ${messageId}`)
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// 好友名片: username 就是 wxid
|
|
739
|
+
if (!card.username.includes(STRANGER_SUFFIX)) {
|
|
740
|
+
return card.username
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// 微信发出的名片即便是好友也只带 v3 加密名, 尝试在本地联系人中匹配出 wxid
|
|
744
|
+
const friendId = await this._findFriendByCard(card)
|
|
745
|
+
if (friendId) {
|
|
746
|
+
log.verbose(PRE, 'messageContact(%s) matched friend %s', messageId, friendId)
|
|
747
|
+
return friendId
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// 非好友名片: 缓存为搜索联系人, friendshipAdd 可用 scene 17 + v4 票据添加
|
|
751
|
+
const searchId = `${SEARCH_CONTACT_PREFIX}${card.username}`
|
|
752
|
+
await this._cacheMgr!.setContactSearch(searchId, {
|
|
753
|
+
alias: '',
|
|
754
|
+
avatar: card.avatar,
|
|
755
|
+
city: card.city,
|
|
756
|
+
country: '',
|
|
757
|
+
isFriend: 0,
|
|
758
|
+
name: card.nickname,
|
|
759
|
+
province: card.province,
|
|
760
|
+
scene: card.scene,
|
|
761
|
+
sex: card.sex,
|
|
762
|
+
sign: card.sign,
|
|
763
|
+
ticket: card.antispamticket,
|
|
764
|
+
wxNum: card.alias,
|
|
765
|
+
wxid: card.username,
|
|
766
|
+
})
|
|
767
|
+
return searchId
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* 名片(v3 加密名) -> 本地好友 wxid
|
|
772
|
+
* 先按头像路径匹配(忽略 /0 /132 尺寸后缀), 再按唯一昵称匹配
|
|
773
|
+
*/
|
|
774
|
+
private async _findFriendByCard (card: ContactCardPayload): Promise<string | undefined> {
|
|
775
|
+
const avatarKey = (url?: string) => (url || '').replace(/^https?:\/\//, '').replace(/\/\d+$/, '')
|
|
776
|
+
const cardAvatar = avatarKey(card.avatar)
|
|
777
|
+
const contacts = (await this._cacheMgr!.getAllContacts())
|
|
778
|
+
.filter(c => c.wxid && !isRoomId(c.wxid) && !c.wxid.includes(STRANGER_SUFFIX) && c.isFriend !== 0)
|
|
779
|
+
|
|
780
|
+
if (cardAvatar) {
|
|
781
|
+
const byAvatar = contacts.find(c => avatarKey(c.avatar) === cardAvatar)
|
|
782
|
+
if (byAvatar) {
|
|
783
|
+
return byAvatar.wxid
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
if (card.nickname) {
|
|
787
|
+
const byName = contacts.filter(c => c.name === card.nickname)
|
|
788
|
+
if (byName.length === 1) {
|
|
789
|
+
return byName[0]!.wxid
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
return undefined
|
|
729
793
|
}
|
|
730
794
|
|
|
731
795
|
override async messageChannel (
|
|
@@ -775,12 +839,22 @@ class PuppetMatrix extends PUPPET.Puppet {
|
|
|
775
839
|
return this._getMessageFileFileBox(messageId, messagePayload)
|
|
776
840
|
case PUPPET.types.Message.Emoticon: {
|
|
777
841
|
const emotionPayload = await parseEmotionMessagePayload(messagePayload)
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
842
|
+
// downloadWWMedia 返回的是下载地址字符串
|
|
843
|
+
const url: string = emotionPayload.is_ww
|
|
844
|
+
? (await this._client?.downloadWWMedia({
|
|
845
|
+
aes_key: emotionPayload.aeskey || '',
|
|
846
|
+
auth_key: emotionPayload.authkey || '',
|
|
847
|
+
file_id: emotionPayload.cdnurl || '',
|
|
848
|
+
file_name: `message-${messageId}-emoticon.gif`,
|
|
849
|
+
}, 3)) || ''
|
|
850
|
+
: emotionPayload.cdnurl
|
|
851
|
+
if (!url) {
|
|
852
|
+
throw new Error(`Can not get emoticon url for message: ${messageId}`)
|
|
853
|
+
}
|
|
854
|
+
// 按文件头确定扩展名(静态多为 png,动图为 gif),识别失败时按表情类型猜测
|
|
855
|
+
const head = await fetchRemoteHead(url)
|
|
856
|
+
const ext = (head && detectImageExt(head)) || (emotionPayload.type === 2 && !emotionPayload.game ? 'gif' : 'png')
|
|
857
|
+
const emoticonBox = FileBox.fromUrl(url, { name: `message-${messageId}-emoticon.${ext}` })
|
|
784
858
|
|
|
785
859
|
emoticonBox.metadata = {
|
|
786
860
|
payload: emotionPayload,
|
|
@@ -954,28 +1028,14 @@ class PuppetMatrix extends PUPPET.Puppet {
|
|
|
954
1028
|
***************************************************************************/
|
|
955
1029
|
// 发送名片
|
|
956
1030
|
override async messageSendContact (toUserId: string, contactId: string): Promise<void | string> {
|
|
957
|
-
log.verbose('PuppetWeChat', '
|
|
1031
|
+
log.verbose('PuppetWeChat', 'messageSendContact("%s", %s)', toUserId, contactId)
|
|
958
1032
|
const contactPayload = await this.contactRawPayload(contactId)
|
|
959
|
-
const
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
city: contactPayload?.city,
|
|
966
|
-
fullpy: contactPayload?.name,
|
|
967
|
-
imagestatus: '3',
|
|
968
|
-
nickname: contactPayload?.name,
|
|
969
|
-
province: contactPayload?.province,
|
|
970
|
-
regionCode: 'CN_Shanghai',
|
|
971
|
-
scene: '17',
|
|
972
|
-
sex: contactPayload?.sex,
|
|
973
|
-
smallheadimgurl: contactPayload?.avatar,
|
|
974
|
-
username: contactPayload?.ticket,
|
|
975
|
-
},
|
|
976
|
-
}
|
|
977
|
-
const xml = JsonToXml(xmlObj)
|
|
978
|
-
const res = await this._client?.sendContactCard(toUserId, xml, this._self?.wxid)
|
|
1033
|
+
const res = await this._client?.sendContact({
|
|
1034
|
+
shareHeadImg: contactPayload?.avatar,
|
|
1035
|
+
shareId: contactPayload?.wxid || contactId,
|
|
1036
|
+
shareNickname: contactPayload?.name,
|
|
1037
|
+
toId: toUserId,
|
|
1038
|
+
}, this._self?.wxid)
|
|
979
1039
|
if (res) {
|
|
980
1040
|
await this._cacheMgr?.setMessage(res.id, res)
|
|
981
1041
|
return res.id
|
|
@@ -994,7 +1054,17 @@ class PuppetMatrix extends PUPPET.Puppet {
|
|
|
994
1054
|
// @ts-ignore
|
|
995
1055
|
const fileUrl = fileBox.remoteUrl || ''
|
|
996
1056
|
if (metadata.type === 'emoticon') {
|
|
997
|
-
|
|
1057
|
+
// 优先 md5 发送(收到的表情原样转发、骰子/猜拳均可),失败再按 url 发送
|
|
1058
|
+
const emoji = metadata.payload as EmojiMessagePayload | undefined
|
|
1059
|
+
const byMd5 = emoji?.md5 && emoji.len
|
|
1060
|
+
? await this._client?.sendEmojiMd5(conversationId, emoji.md5, emoji.len, this._self?.wxid)
|
|
1061
|
+
: undefined
|
|
1062
|
+
const msgInfo = byMd5 || (fileUrl ? await this._client?.sendEmojiUrl(conversationId, fileUrl, this._self?.wxid) : undefined)
|
|
1063
|
+
if (!msgInfo) {
|
|
1064
|
+
throw new Error(`send emoticon failed: ${fileBox.name}`)
|
|
1065
|
+
}
|
|
1066
|
+
await this._cacheMgr?.setMessage(msgInfo.id, msgInfo)
|
|
1067
|
+
return msgInfo.id
|
|
998
1068
|
} else if (fileBox.mediaType.startsWith('image/')) {
|
|
999
1069
|
if (fileUrl) {
|
|
1000
1070
|
const msgInfo = await this._client?.sendUrlImg(conversationId, fileUrl, this._self?.wxid)
|
|
@@ -1115,11 +1185,37 @@ class PuppetMatrix extends PUPPET.Puppet {
|
|
|
1115
1185
|
}
|
|
1116
1186
|
|
|
1117
1187
|
/**
|
|
1118
|
-
*
|
|
1188
|
+
* 拍一拍(Wechaty 无标准接口,通过 bot.puppet 调用)
|
|
1189
|
+
* @param conversationId 会话 id:私聊为好友 wxid,群聊为群 id
|
|
1190
|
+
* @param contactId 被拍的人 wxid,私聊时默认拍对方
|
|
1191
|
+
*/
|
|
1192
|
+
public async sendPat (conversationId: string, contactId?: string): Promise<boolean> {
|
|
1193
|
+
log.verbose(PRE, 'sendPat(%s, %s)', conversationId, contactId)
|
|
1194
|
+
const patted = contactId || conversationId
|
|
1195
|
+
if (isRoomId(patted)) {
|
|
1196
|
+
throw new Error('sendPat: contactId is required in room')
|
|
1197
|
+
}
|
|
1198
|
+
return (await this._client?.sendPat(conversationId, patted)) || false
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
/**
|
|
1202
|
+
* 消息撤回(只能撤回自己发送的消息)
|
|
1119
1203
|
* @param messageId
|
|
1120
1204
|
*/
|
|
1121
1205
|
override async messageRecall (messageId: string): Promise<boolean> {
|
|
1122
|
-
|
|
1206
|
+
log.verbose(PRE, 'messageRecall(%s)', messageId)
|
|
1207
|
+
const payload = await this._cacheMgr?.getMessage(messageId)
|
|
1208
|
+
if (!payload) {
|
|
1209
|
+
log.warn(PRE, 'messageRecall(%s): message not found in cache', messageId)
|
|
1210
|
+
return false
|
|
1211
|
+
}
|
|
1212
|
+
// 自己发送的消息 listenerId 为会话 id(好友 wxid 或群 id)
|
|
1213
|
+
const toId = payload.listenerId || payload.fromWxid || ''
|
|
1214
|
+
if (!toId) {
|
|
1215
|
+
log.warn(PRE, 'messageRecall(%s): can not find conversation id', messageId)
|
|
1216
|
+
return false
|
|
1217
|
+
}
|
|
1218
|
+
return (await this._client?.revokeMsg(toId, messageId, payload.clientMsgId)) || false
|
|
1123
1219
|
}
|
|
1124
1220
|
|
|
1125
1221
|
/**
|