wechaty-web-panel 1.6.121 → 1.6.123

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.
@@ -34,6 +34,16 @@ async function onRecordMessage(msg) {
34
34
  if (isOfficial)
35
35
  return;
36
36
  console.log('msg', msg);
37
+ let isMention = false;
38
+ if (room) {
39
+ const userSelfName = this.currentUser?.name() || '';
40
+ const msgText = msg.type() === this.Message.Type.Text ? msg.text() : '';
41
+ isMention = (await msg.mentionSelf()) || msgText.includes(`@${userSelfName}`);
42
+ const isMentionAll = await msg.isMentionAll();
43
+ if (isMentionAll && isMention) {
44
+ isMention = false;
45
+ }
46
+ }
37
47
  const baseMsg = {
38
48
  conversionId: room ? room.id : contact.id,
39
49
  conversionName: room ? roomName : contactName,
@@ -43,7 +53,8 @@ async function onRecordMessage(msg) {
43
53
  chatAlias: contactAlias,
44
54
  chatUserWeixin: userWeixin,
45
55
  isMyself: !!msgSelf,
46
- time: timestamp.length > 10 ? parseInt(timestamp / 1000) : timestamp
56
+ isMention,
57
+ time: String(timestamp).length > 10 ? Math.floor(timestamp / 1000) : timestamp
47
58
  };
48
59
  switch (type) {
49
60
  case this.Message.Type.Channel:
@@ -127,12 +138,12 @@ async function onRecordMessage(msg) {
127
138
  break;
128
139
  case this.Message.Type.ChatHistory:
129
140
  const historys = await msg.toChatHistory();
130
- const contents = await formatHistory(this, historys.payload, conversationRecord);
141
+ const contents = await formatHistory(this, historys.payload, conversationRecord, msgId);
131
142
  baseMsg.type = '历史记录';
132
143
  baseMsg.content = contents;
133
144
  break;
134
145
  case this.Message.Type.Location:
135
- const location = await msg.toLocation();
146
+ const locationInfo = await msg.toLocation();
136
147
  const locationParse = `【位置解析结果】\n纬度:${locationInfo?.payload?.latitude}\n经度:${locationInfo?.payload?.longitude}\n地点名:${locationInfo?.payload?.name}\n城市:${locationInfo?.payload?.city || ''}\n具体地址:${locationInfo?.payload?.address}\nPoiId:${locationInfo?.payload?.poiId || ''}`;
137
148
  baseMsg.type = '位置';
138
149
  baseMsg.content = locationParse;
@@ -149,7 +160,6 @@ async function onRecordMessage(msg) {
149
160
  baseMsg.content = sysText;
150
161
  }
151
162
  }
152
- default:
153
163
  break;
154
164
  }
155
165
  console.log('baseMsg', baseMsg);
@@ -159,9 +169,9 @@ async function onRecordMessage(msg) {
159
169
  console.log('记录消息失败', e);
160
170
  }
161
171
  }
162
- async function formatHistory(that, history, conversationRecord) {
172
+ async function formatHistory(that, historyList, conversationRecord, msgId) {
163
173
  const contents = [];
164
- for (const history of historys) {
174
+ for (const history of historyList) {
165
175
  if (history.type === that.Message.Type.Text) {
166
176
  contents.push(history.message);
167
177
  }
@@ -169,20 +179,19 @@ async function formatHistory(that, history, conversationRecord) {
169
179
  const attachFileBox = await history.message.toFileBox();
170
180
  const fileExtname = path.extname(attachFileBox.name);
171
181
  const isImage = fileExtname.includes('.png') || fileExtname.includes('.jpg') || fileExtname.includes('.jpeg') || fileExtname.includes('.gif');
172
- baseMsg.type = isImage ? '图片' : '文件';
173
182
  const buffer = await attachFileBox.toBuffer();
174
183
  const url = await uploadOssFile(`${conversationRecord?.ossConfig?.custom_path || ''}${msgId}_${dayjs().valueOf()}_${attachFileBox.name}`, buffer);
175
- contents.push(`()[${url}]`);
184
+ contents.push(`[${isImage ? '图片' : '文件'}](${url})`);
176
185
  }
177
186
  else if (history.type === that.Message.Type.ChatHistory) {
178
- const res = await formatHistory(that, history.message, conversationRecord);
187
+ const res = await formatHistory(that, history.message, conversationRecord, msgId);
179
188
  contents.push(res);
180
189
  }
181
190
  }
182
191
  return contents.join('\n');
183
192
  }
184
193
  function sendMessage(msgInfo, recordConfig, robotInfo) {
185
- const blackKey = ['conversationId', 'conversionName', 'isRoom', 'isRobot', 'chatName', 'chatId', 'chatAlias', 'time', 'type', 'url', 'mediaInfo', 'content', 'isMyself'];
194
+ const blackKey = Object.keys(msgInfo);
186
195
  const baseData = {
187
196
  ...msgInfo
188
197
  };
@@ -19,15 +19,15 @@ async function checkAllow(clawConfig, contactId, roomId) {
19
19
  }
20
20
  else if (allowScope === 2) {
21
21
  // 仅所有群
22
- return !!room;
22
+ return !!roomId;
23
23
  }
24
24
  else if (allowScope === 3) {
25
25
  // 仅所有好友
26
- return !room;
26
+ return !roomId;
27
27
  }
28
28
  else if (allowScope === 4) {
29
29
  // 部分群和部分好友
30
- if (room) {
30
+ if (roomId) {
31
31
  return allowRooms.some(r => r.id === roomId);
32
32
  }
33
33
  else {
@@ -71,6 +71,16 @@ async function onClawMessage(msg) {
71
71
  if (isOfficial)
72
72
  return;
73
73
  console.log('msg', msg);
74
+ let isMention = false;
75
+ if (room) {
76
+ const userSelfName = this.currentUser?.name() || '';
77
+ const msgText = type === this.Message.Type.Text ? msg.text() : '';
78
+ isMention = (await msg.mentionSelf()) || msgText.includes(`@${userSelfName}`);
79
+ const isMentionAll = await msg.isMentionAll();
80
+ if (isMentionAll && isMention) {
81
+ isMention = false;
82
+ }
83
+ }
74
84
  const baseMsg = {
75
85
  conversionId: room ? room.id : contact.id,
76
86
  conversionName: room ? roomName : contactName,
@@ -80,14 +90,15 @@ async function onClawMessage(msg) {
80
90
  chatAlias: contactAlias,
81
91
  chatUserWeixin: userWeixin,
82
92
  isMyself: !!msgSelf,
83
- time: timestamp.length > 10 ? parseInt(timestamp / 1000) : timestamp,
93
+ time: String(timestamp).length > 10 ? Math.floor(timestamp / 1000) : timestamp,
84
94
  msgId: msg.id,
95
+ isMention,
85
96
  };
86
97
  switch (type) {
87
98
  case this.Message.Type.Channel:
88
99
  baseMsg.type = '视频号';
89
100
  const channelInfo = await msg.toChannel();
90
- baseMsg.content = `【视频号消息】\n视频号昵称:${channelInfo.nickname()}\n视频号简介:${channelInfo.desc}\n视频号链接:${channelInfo.url}`;
101
+ baseMsg.content = `【视频号消息】\n视频号昵称:${channelInfo.nickname()}\n视频号简介:${channelInfo.desc()}\n视频号链接:${channelInfo.url()}`;
91
102
  baseMsg.mediaInfo = {
92
103
  nickname: channelInfo.nickname(),
93
104
  coverUrl: channelInfo.coverUrl(),
@@ -144,7 +155,7 @@ async function onClawMessage(msg) {
144
155
  }
145
156
  else {
146
157
  // 没有配置OSS,使用base64
147
- const base64 = attachFileBox.toBase64();
158
+ const base64 = await attachFileBox.toBase64();
148
159
  baseMsg.url = base64;
149
160
  baseMsg.content = `【${isImage ? '图片' : '文件'}消息】\n文件名:${attachFileBox.name}\n文件大小:${buffer.length} bytes\n(文件内容未上传,仅提供base64字符串)`;
150
161
  }
@@ -165,7 +176,7 @@ async function onClawMessage(msg) {
165
176
  }
166
177
  else {
167
178
  // 没有配置OSS,使用base64
168
- const audioBase64 = audioFileBox.toBase64();
179
+ const audioBase64 = await audioFileBox.toBase64();
169
180
  baseMsg.url = audioBase64;
170
181
  baseMsg.content = `【语音消息】\n文件名:${audioFileBox.name}\n文件大小:${audioBuffer.length} bytes\n(文件内容未上传,仅提供base64字符串)`;
171
182
  }
@@ -177,7 +188,7 @@ async function onClawMessage(msg) {
177
188
  baseMsg.content = contents;
178
189
  break;
179
190
  case this.Message.Type.Location:
180
- const location = await msg.toLocation();
191
+ const locationInfo = await msg.toLocation();
181
192
  const locationParse = `【位置解析结果】\n纬度:${locationInfo?.payload?.latitude}\n经度:${locationInfo?.payload?.longitude}\n地点名:${locationInfo?.payload?.name}\n城市:${locationInfo?.payload?.city || ''}\n具体地址:${locationInfo?.payload?.address}\nPoiId:${locationInfo?.payload?.poiId || ''}`;
182
193
  baseMsg.type = '位置';
183
194
  baseMsg.content = locationParse;
@@ -197,6 +208,10 @@ async function onClawMessage(msg) {
197
208
  else {
198
209
  if (!forwardMediaMsg)
199
210
  return;
211
+ // 媒体消息无法 @,若 forwardMediaMsg 开启且在允许的群中,视为已提及,避免被 requireMention 拦截
212
+ if (room) {
213
+ baseMsg.isMention = true;
214
+ }
200
215
  }
201
216
  publishClawMessage(baseMsg, robotInfo);
202
217
  }
@@ -204,9 +219,9 @@ async function onClawMessage(msg) {
204
219
  console.log('claw 消息处理失败', e);
205
220
  }
206
221
  }
207
- async function formatHistory(that, history, clawConfig) {
222
+ async function formatHistory(that, historyList, clawConfig) {
208
223
  const contents = [];
209
- for (const history of historys) {
224
+ for (const history of historyList) {
210
225
  if (history.type === that.Message.Type.Text) {
211
226
  contents.push(history.message);
212
227
  }
@@ -214,7 +229,6 @@ async function formatHistory(that, history, clawConfig) {
214
229
  const attachFileBox = await history.message.toFileBox();
215
230
  const fileExtname = path.extname(attachFileBox.name);
216
231
  const isImage = fileExtname.includes('.png') || fileExtname.includes('.jpg') || fileExtname.includes('.jpeg') || fileExtname.includes('.gif');
217
- baseMsg.type = isImage ? '图片' : '文件';
218
232
  if (attachFileBox.remoteUrl) {
219
233
  // 直接使用远程链接,无需上传
220
234
  const url = attachFileBox.remoteUrl;
@@ -223,7 +237,7 @@ async function formatHistory(that, history, clawConfig) {
223
237
  }
224
238
  const buffer = await attachFileBox.toBuffer();
225
239
  if (!clawConfig?.ossConfig.type) {
226
- const base64 = attachFileBox.toBase64();
240
+ const base64 = await attachFileBox.toBase64();
227
241
  contents.push(`【${isImage ? '图片' : '文件'}消息】\n文件名:${attachFileBox.name}\n文件大小:${buffer.length} bytes\n(文件内容未上传,仅提供base64字符串)`);
228
242
  continue;
229
243
  }
@@ -252,7 +266,8 @@ function publishClawMessage(baseMsg, robotInfo) {
252
266
  isGroup: baseMsg.isRoom,
253
267
  groupId: baseMsg.isRoom ? baseMsg.conversionId : undefined,
254
268
  groupName: baseMsg.isRoom ? baseMsg.conversionName : undefined,
255
- timestamp: baseMsg.time * 1000, // 转换为 11 位毫秒时间戳
269
+ isGroupMention: baseMsg.isMention,
270
+ timestamp: baseMsg.time * 1000, // 转换为 13 位毫秒时间戳
256
271
  messageId: baseMsg.msgId,
257
272
  type: baseMsg.type,
258
273
  mediaInfo: baseMsg.mediaInfo,
@@ -285,7 +300,7 @@ async function initClawMqtt(that) {
285
300
  const { port, host, username, password, clientId, reciveTopic, sendTopic } = mqttConfig;
286
301
  if (!host)
287
302
  return;
288
- clawMqttClient = mqtt.connect(`mqtt://${host}:${port}`, {
303
+ clawMqttClient = mqtt.connect(`${host}:${port}`, {
289
304
  username,
290
305
  password,
291
306
  clientId: clientId + randomRange(1, 10000),
@@ -139,7 +139,7 @@ async function dispatchFriendFilterByMsgType(that, msg) {
139
139
  const text = puppetInfo.puppetType.includes('PuppetService') && !msg.text().startsWith('@') ? msg.text().trim() : await getVoiceText(audioFileBox, finalConfig.botConfig.whisperConfig);
140
140
  console.log('语音解析结果:', text);
141
141
  const keyword = finalConfig.botConfig.whisperConfig?.keywords?.length ? finalConfig.botConfig?.whisperConfig.keywords?.find((item) => text.includes(item)) : true;
142
- const isIgnore = checkIgnore(content.trim(), aibotConfig.ignoreMessages);
142
+ const isIgnore = checkIgnore(text.trim(), aibotConfig.ignoreMessages);
143
143
  if (text.trim() && !isIgnore && keyword) {
144
144
  const gpt4vReplys = await getGpt4vChat({
145
145
  that,
@@ -332,7 +332,7 @@ async function dispatchRoomFilterByMsgType(that, room, msg) {
332
332
  const type = msg.type();
333
333
  const receiver = msg.to();
334
334
  let content = '';
335
- let replys = '';
335
+ let replys = [];
336
336
  let contactId = contact.id;
337
337
  let contactAvatar = await contact.avatar();
338
338
  const userSelfName = that.currentUser?.name() || that.userSelf()?.name();
@@ -526,7 +526,7 @@ async function dispatchRoomFilterByMsgType(that, room, msg) {
526
526
  const text = puppetInfo.puppetType.includes('PuppetService') && !msg.text().startsWith('@') ? msg.text().trim() : await getVoiceText(audioFileBox, finalConfig.botConfig.whisperConfig);
527
527
  console.log('语音解析结果', text);
528
528
  const keyword = finalConfig.botConfig.whisperConfig?.keywords?.length ? finalConfig.botConfig?.whisperConfig?.keywords?.find((item) => text.includes(item)) : true;
529
- const isIgnore = checkIgnore(content.trim(), aibotConfig.ignoreMessages);
529
+ const isIgnore = checkIgnore(text.trim(), aibotConfig.ignoreMessages);
530
530
  if (text.trim() && !isIgnore && keyword) {
531
531
  const gpt4vReplys = await getGpt4vChat({
532
532
  that,
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const packageJson = {
5
5
  "name": "wechaty-web-panel",
6
- "version": "1.6.121",
6
+ "version": "1.6.123",
7
7
  "exports": {
8
8
  ".": {
9
9
  "import": "./dist/index.js"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wechaty-web-panel",
3
- "version": "1.6.121",
3
+ "version": "1.6.123",
4
4
  "exports": {
5
5
  ".": {
6
6
  "import": "./dist/index.js"
@@ -34,6 +34,16 @@ async function onRecordMessage(msg) {
34
34
  const userWeixin = contact && contact.weixin() || ''
35
35
  if (isOfficial) return
36
36
  console.log('msg', msg)
37
+ let isMention = false
38
+ if (room) {
39
+ const userSelfName = this.currentUser?.name() || ''
40
+ const msgText = msg.type() === this.Message.Type.Text ? msg.text() : ''
41
+ isMention = (await msg.mentionSelf()) || msgText.includes(`@${userSelfName}`)
42
+ const isMentionAll = await msg.isMentionAll()
43
+ if (isMentionAll && isMention) {
44
+ isMention = false
45
+ }
46
+ }
37
47
  const baseMsg = {
38
48
  conversionId: room ? room.id : contact.id,
39
49
  conversionName: room ? roomName : contactName,
@@ -43,7 +53,8 @@ async function onRecordMessage(msg) {
43
53
  chatAlias: contactAlias,
44
54
  chatUserWeixin: userWeixin,
45
55
  isMyself: !!msgSelf,
46
- time: timestamp.length > 10 ? parseInt(timestamp / 1000) : timestamp
56
+ isMention,
57
+ time: String(timestamp).length > 10 ? Math.floor(timestamp / 1000) : timestamp
47
58
  }
48
59
  switch (type) {
49
60
  case this.Message.Type.Channel:
@@ -127,12 +138,12 @@ async function onRecordMessage(msg) {
127
138
  break
128
139
  case this.Message.Type.ChatHistory:
129
140
  const historys = await msg.toChatHistory()
130
- const contents = await formatHistory(this, historys.payload, conversationRecord)
141
+ const contents = await formatHistory(this, historys.payload, conversationRecord, msgId)
131
142
  baseMsg.type = '历史记录'
132
143
  baseMsg.content = contents
133
144
  break
134
145
  case this.Message.Type.Location:
135
- const location = await msg.toLocation()
146
+ const locationInfo = await msg.toLocation()
136
147
  const locationParse = `【位置解析结果】\n纬度:${locationInfo?.payload?.latitude}\n经度:${locationInfo?.payload?.longitude}\n地点名:${locationInfo?.payload?.name}\n城市:${locationInfo?.payload?.city || ''}\n具体地址:${locationInfo?.payload?.address}\nPoiId:${locationInfo?.payload?.poiId || ''}`
137
148
  baseMsg.type = '位置'
138
149
  baseMsg.content = locationParse
@@ -148,7 +159,6 @@ async function onRecordMessage(msg) {
148
159
  baseMsg.content = sysText
149
160
  }
150
161
  }
151
- default:
152
162
  break
153
163
  }
154
164
  console.log('baseMsg', baseMsg)
@@ -158,21 +168,20 @@ async function onRecordMessage(msg) {
158
168
  }
159
169
  }
160
170
 
161
- async function formatHistory(that, history, conversationRecord) {
171
+ async function formatHistory(that, historyList, conversationRecord, msgId) {
162
172
  const contents = []
163
- for (const history of historys) {
173
+ for (const history of historyList) {
164
174
  if (history.type === that.Message.Type.Text) {
165
175
  contents.push(history.message)
166
176
  } else if (history.type === that.Message.Type.Recalled || history.type === that.Message.Type.Attachment || history.type === that.Message.Type.Image || history.type === that.Message.Type.Video) {
167
177
  const attachFileBox = await history.message.toFileBox();
168
178
  const fileExtname = path.extname(attachFileBox.name);
169
179
  const isImage = fileExtname.includes('.png') || fileExtname.includes('.jpg') || fileExtname.includes('.jpeg') || fileExtname.includes('.gif')
170
- baseMsg.type = isImage ? '图片' : '文件'
171
180
  const buffer = await attachFileBox.toBuffer()
172
181
  const url = await uploadOssFile(`${conversationRecord?.ossConfig?.custom_path || ''}${msgId}_${dayjs().valueOf()}_${attachFileBox.name}`, buffer)
173
- contents.push(`()[${url}]`)
182
+ contents.push(`[${isImage ? '图片' : '文件'}](${url})`)
174
183
  } else if (history.type === that.Message.Type.ChatHistory) {
175
- const res = await formatHistory(that, history.message, conversationRecord)
184
+ const res = await formatHistory(that, history.message, conversationRecord, msgId)
176
185
  contents.push(res)
177
186
  }
178
187
  }
@@ -180,7 +189,7 @@ async function formatHistory(that, history, conversationRecord) {
180
189
  }
181
190
 
182
191
  function sendMessage(msgInfo, recordConfig, robotInfo) {
183
- const blackKey = ['conversationId', 'conversionName', 'isRoom', 'isRobot', 'chatName', 'chatId', 'chatAlias', 'time', 'type', 'url', 'mediaInfo', 'content', 'isMyself']
192
+ const blackKey = Object.keys(msgInfo)
184
193
  const baseData = {
185
194
  ...msgInfo
186
195
  }
@@ -21,13 +21,13 @@ async function checkAllow(clawConfig, contactId, roomId) {
21
21
  return true
22
22
  } else if (allowScope === 2) {
23
23
  // 仅所有群
24
- return !!room
24
+ return !!roomId
25
25
  } else if (allowScope === 3) {
26
26
  // 仅所有好友
27
- return !room
27
+ return !roomId
28
28
  } else if (allowScope === 4) {
29
29
  // 部分群和部分好友
30
- if (room) {
30
+ if (roomId) {
31
31
  return allowRooms.some(r => r.id === roomId)
32
32
  } else {
33
33
  return allowFriends.some(f => f.id === contactId)
@@ -70,6 +70,16 @@ async function onClawMessage(msg) {
70
70
  const userWeixin = contact && contact.weixin() || ''
71
71
  if (isOfficial) return
72
72
  console.log('msg', msg)
73
+ let isMention = false
74
+ if (room) {
75
+ const userSelfName = this.currentUser?.name() || ''
76
+ const msgText = type === this.Message.Type.Text ? msg.text() : ''
77
+ isMention = (await msg.mentionSelf()) || msgText.includes(`@${userSelfName}`)
78
+ const isMentionAll = await msg.isMentionAll()
79
+ if (isMentionAll && isMention) {
80
+ isMention = false
81
+ }
82
+ }
73
83
  const baseMsg = {
74
84
  conversionId: room ? room.id : contact.id,
75
85
  conversionName: room ? roomName : contactName,
@@ -79,14 +89,15 @@ async function onClawMessage(msg) {
79
89
  chatAlias: contactAlias,
80
90
  chatUserWeixin: userWeixin,
81
91
  isMyself: !!msgSelf,
82
- time: timestamp.length > 10 ? parseInt(timestamp / 1000) : timestamp,
92
+ time: String(timestamp).length > 10 ? Math.floor(timestamp / 1000) : timestamp,
83
93
  msgId: msg.id,
94
+ isMention,
84
95
  }
85
96
  switch (type) {
86
97
  case this.Message.Type.Channel:
87
98
  baseMsg.type = '视频号'
88
99
  const channelInfo = await msg.toChannel();
89
- baseMsg.content = `【视频号消息】\n视频号昵称:${channelInfo.nickname()}\n视频号简介:${channelInfo.desc}\n视频号链接:${channelInfo.url}`
100
+ baseMsg.content = `【视频号消息】\n视频号昵称:${channelInfo.nickname()}\n视频号简介:${channelInfo.desc()}\n视频号链接:${channelInfo.url()}`
90
101
  baseMsg.mediaInfo = {
91
102
  nickname: channelInfo.nickname(),
92
103
  coverUrl: channelInfo.coverUrl(),
@@ -143,7 +154,7 @@ async function onClawMessage(msg) {
143
154
  baseMsg.content = `【${isImage ? '图片' : '文件'}消息】\n文件名:${attachFileBox.name}\n下载链接:${url}`
144
155
  } else {
145
156
  // 没有配置OSS,使用base64
146
- const base64 = attachFileBox.toBase64()
157
+ const base64 = await attachFileBox.toBase64()
147
158
  baseMsg.url = base64
148
159
  baseMsg.content = `【${isImage ? '图片' : '文件'}消息】\n文件名:${attachFileBox.name}\n文件大小:${buffer.length} bytes\n(文件内容未上传,仅提供base64字符串)`
149
160
  }
@@ -163,7 +174,7 @@ async function onClawMessage(msg) {
163
174
  baseMsg.content = `【语音消息】\n文件名:${audioFileBox.name}\n下载链接:${audioUrl}`
164
175
  } else {
165
176
  // 没有配置OSS,使用base64
166
- const audioBase64 = audioFileBox.toBase64()
177
+ const audioBase64 = await audioFileBox.toBase64()
167
178
  baseMsg.url = audioBase64
168
179
  baseMsg.content = `【语音消息】\n文件名:${audioFileBox.name}\n文件大小:${audioBuffer.length} bytes\n(文件内容未上传,仅提供base64字符串)`
169
180
  }
@@ -175,7 +186,7 @@ async function onClawMessage(msg) {
175
186
  baseMsg.content = contents
176
187
  break
177
188
  case this.Message.Type.Location:
178
- const location = await msg.toLocation()
189
+ const locationInfo = await msg.toLocation()
179
190
  const locationParse = `【位置解析结果】\n纬度:${locationInfo?.payload?.latitude}\n经度:${locationInfo?.payload?.longitude}\n地点名:${locationInfo?.payload?.name}\n城市:${locationInfo?.payload?.city || ''}\n具体地址:${locationInfo?.payload?.address}\nPoiId:${locationInfo?.payload?.poiId || ''}`
180
191
  baseMsg.type = '位置'
181
192
  baseMsg.content = locationParse
@@ -192,6 +203,10 @@ async function onClawMessage(msg) {
192
203
  }
193
204
  } else {
194
205
  if (!forwardMediaMsg) return
206
+ // 媒体消息无法 @,若 forwardMediaMsg 开启且在允许的群中,视为已提及,避免被 requireMention 拦截
207
+ if (room) {
208
+ baseMsg.isMention = true
209
+ }
195
210
  }
196
211
  publishClawMessage(baseMsg, robotInfo)
197
212
  } catch (e) {
@@ -199,16 +214,15 @@ async function onClawMessage(msg) {
199
214
  }
200
215
  }
201
216
 
202
- async function formatHistory(that, history, clawConfig) {
217
+ async function formatHistory(that, historyList, clawConfig) {
203
218
  const contents = []
204
- for (const history of historys) {
219
+ for (const history of historyList) {
205
220
  if (history.type === that.Message.Type.Text) {
206
221
  contents.push(history.message)
207
222
  } else if (history.type === that.Message.Type.Recalled || history.type === that.Message.Type.Attachment || history.type === that.Message.Type.Image || history.type === that.Message.Type.Video) {
208
223
  const attachFileBox = await history.message.toFileBox();
209
224
  const fileExtname = path.extname(attachFileBox.name);
210
225
  const isImage = fileExtname.includes('.png') || fileExtname.includes('.jpg') || fileExtname.includes('.jpeg') || fileExtname.includes('.gif')
211
- baseMsg.type = isImage ? '图片' : '文件'
212
226
  if (attachFileBox.remoteUrl) {
213
227
  // 直接使用远程链接,无需上传
214
228
  const url = attachFileBox.remoteUrl
@@ -217,7 +231,7 @@ async function formatHistory(that, history, clawConfig) {
217
231
  }
218
232
  const buffer = await attachFileBox.toBuffer()
219
233
  if (!clawConfig?.ossConfig.type) {
220
- const base64 = attachFileBox.toBase64()
234
+ const base64 = await attachFileBox.toBase64()
221
235
  contents.push(`【${isImage ? '图片' : '文件'}消息】\n文件名:${attachFileBox.name}\n文件大小:${buffer.length} bytes\n(文件内容未上传,仅提供base64字符串)`)
222
236
  continue
223
237
  }
@@ -245,7 +259,8 @@ function publishClawMessage(baseMsg, robotInfo) {
245
259
  isGroup: baseMsg.isRoom,
246
260
  groupId: baseMsg.isRoom ? baseMsg.conversionId : undefined,
247
261
  groupName: baseMsg.isRoom ? baseMsg.conversionName : undefined,
248
- timestamp: baseMsg.time * 1000, // 转换为 11 位毫秒时间戳
262
+ isGroupMention: baseMsg.isMention,
263
+ timestamp: baseMsg.time * 1000, // 转换为 13 位毫秒时间戳
249
264
  messageId: baseMsg.msgId,
250
265
  type: baseMsg.type,
251
266
  mediaInfo: baseMsg.mediaInfo,
@@ -277,7 +292,7 @@ async function initClawMqtt(that) {
277
292
  const { port, host, username, password, clientId, reciveTopic, sendTopic } = mqttConfig
278
293
  if (!host) return
279
294
 
280
- clawMqttClient = mqtt.connect(`mqtt://${host}:${port}`, {
295
+ clawMqttClient = mqtt.connect(`${host}:${port}`, {
281
296
  username,
282
297
  password,
283
298
  clientId: clientId + randomRange(1, 10000),
@@ -137,7 +137,7 @@ async function dispatchFriendFilterByMsgType(that, msg) {
137
137
  const text = puppetInfo.puppetType.includes('PuppetService') && !msg.text().startsWith('@') ? msg.text().trim() : await getVoiceText(audioFileBox, finalConfig.botConfig.whisperConfig)
138
138
  console.log('语音解析结果:', text)
139
139
  const keyword = finalConfig.botConfig.whisperConfig?.keywords?.length ? finalConfig.botConfig?.whisperConfig.keywords?.find((item) => text.includes(item)) : true
140
- const isIgnore = checkIgnore(content.trim(), aibotConfig.ignoreMessages)
140
+ const isIgnore = checkIgnore(text.trim(), aibotConfig.ignoreMessages)
141
141
  if (text.trim() && !isIgnore && keyword) {
142
142
  const gpt4vReplys = await getGpt4vChat({
143
143
  that,
@@ -333,7 +333,7 @@ async function dispatchRoomFilterByMsgType(that, room, msg) {
333
333
  const type = msg.type()
334
334
  const receiver = msg.to()
335
335
  let content = ''
336
- let replys = ''
336
+ let replys = []
337
337
  let contactId = contact.id
338
338
  let contactAvatar = await contact.avatar()
339
339
  const userSelfName = that.currentUser?.name() || that.userSelf()?.name()
@@ -532,7 +532,7 @@ async function dispatchRoomFilterByMsgType(that, room, msg) {
532
532
  const text = puppetInfo.puppetType.includes('PuppetService') && !msg.text().startsWith('@') ? msg.text().trim() : await getVoiceText(audioFileBox, finalConfig.botConfig.whisperConfig)
533
533
  console.log('语音解析结果', text)
534
534
  const keyword = finalConfig.botConfig.whisperConfig?.keywords?.length ? finalConfig.botConfig?.whisperConfig?.keywords?.find((item) => text.includes(item)) : true
535
- const isIgnore = checkIgnore(content.trim(), aibotConfig.ignoreMessages)
535
+ const isIgnore = checkIgnore(text.trim(), aibotConfig.ignoreMessages)
536
536
  if (text.trim() && !isIgnore && keyword) {
537
537
  const gpt4vReplys = await getGpt4vChat({
538
538
  that,
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const packageJson = {
5
5
  "name": "wechaty-web-panel",
6
- "version": "1.6.121",
6
+ "version": "1.6.123",
7
7
  "exports": {
8
8
  ".": {
9
9
  "import": "./dist/index.js"