@xmanrui/dsh-im 2.2.0 → 2.2.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xmanrui/dsh-im",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "把九种 IM 机器人和公网 AI Office 接入本机 DeepSeek Harness。 Connect nine IM channels and a public AI Office to a local DeepSeek Harness.",
5
5
  "keywords": [
6
6
  "deepseek-harness",
@@ -99,7 +99,7 @@ export function WhatsappAccessSettings({ account, busy = false, onSave }) {
99
99
  h('span', null, '响应自聊和白名单联系人的私聊,忽略群聊。')),
100
100
  h('span', { className: 'dwa-accessTooltipItem' },
101
101
  h('strong', null, '开放响应模式'),
102
- h('span', null, '响应所有私聊,以及群聊中的提及或回复。')))))),
102
+ h('span', null, '响应所有私聊、已绑定账号自己发出的群聊消息,以及其他群成员的提及或回复。')))))),
103
103
  h('label', { className: 'dwa-accessField' },
104
104
  h('span', null, '模式'),
105
105
  h('select', {
@@ -373,7 +373,7 @@ const EN = Object.freeze({
373
373
  '已生效:': 'Active: ',
374
374
  '只响应已绑定 WhatsApp 账号的自聊消息。': 'Only respond to self-chat messages from the linked WhatsApp account.',
375
375
  '响应自聊和白名单联系人的私聊,忽略群聊。': 'Respond to self-chat and allowlisted direct messages; ignore group messages.',
376
- '响应所有私聊,以及群聊中的提及或回复。': 'Respond to all direct messages and to group mentions or replies.',
376
+ '响应所有私聊、已绑定账号自己发出的群聊消息,以及其他群成员的提及或回复。': 'Respond to all direct messages, group messages sent by the linked account, and mentions or replies from other group members.',
377
377
  '允许私聊的 WhatsApp 电话号码': 'WhatsApp phone numbers allowed to send direct messages',
378
378
  '每行一个含国家或地区代码的号码': 'One number with country or region code per line',
379
379
  '可以包含开头的 +,保存时会自动移除。': 'A leading + is allowed and removed when saved.',
@@ -1,4 +1,4 @@
1
- import { createHash } from 'node:crypto';
1
+ import { createHash, randomBytes } from 'node:crypto';
2
2
 
3
3
  import {
4
4
  areJidsSameUser,
@@ -217,9 +217,9 @@ export function normalizeWhatsappMessage(message, accountJid, {
217
217
  const fromMe = message.key.fromMe === true;
218
218
  const selfChat = fromMe && !group
219
219
  && [remoteJid, alternateRemoteJid].some((jid) => jid && areJidsSameUser(jid, accountJid));
220
- if (fromMe && !selfChat) return null;
221
- const senderJid = selfChat ? accountJid : group ? message.key.participant : remoteJid;
222
- const senderAlternateJid = group ? message.key.participantAlt : alternateRemoteJid;
220
+ if (fromMe && !selfChat && !group) return null;
221
+ const senderJid = fromMe ? accountJid : group ? message.key.participant : remoteJid;
222
+ const senderAlternateJid = group && !fromMe ? message.key.participantAlt : alternateRemoteJid;
223
223
  if (typeof senderJid !== 'string' || !senderJid) return null;
224
224
  const viewOnce = hasViewOnceWrapper(message.message);
225
225
  const content = normalizeMessageContent(message.message);
@@ -241,7 +241,7 @@ export function normalizeWhatsappMessage(message, accountJid, {
241
241
  content: messageText(content),
242
242
  images: image ? [image] : [],
243
243
  files: file ? [file] : [],
244
- addressed: !group || mentioned || replyToSelf,
244
+ addressed: !group || fromMe || mentioned || replyToSelf,
245
245
  selfChat,
246
246
  replyTarget: { jid: remoteJid, quoted: message, selfChat },
247
247
  };
@@ -272,6 +272,14 @@ class RecentWhatsappOutboundIds {
272
272
  }
273
273
 
274
274
  remember(id) {
275
+ this.#store(id);
276
+ }
277
+
278
+ reserve(id) {
279
+ this.#store(id);
280
+ }
281
+
282
+ #store(id) {
275
283
  if (typeof id !== 'string' || !id) return;
276
284
  this.#purge();
277
285
  this.#ids.set(id, Date.now() + 5 * 60_000);
@@ -360,10 +368,23 @@ export class WhatsappBotClient {
360
368
  await this.#stopTyping(target.jid);
361
369
  const providerMessageIds = [];
362
370
  for (const [index, chunk] of splitMessageText(text, 4_000).entries()) {
371
+ const messageId = randomBytes(10).toString('hex').toUpperCase();
372
+ const options = {
373
+ ...(index === 0 && target.quoted ? { quoted: target.quoted } : {}),
374
+ messageId,
375
+ };
376
+ // Linked-account group messages are valid inbound prompts in open mode.
377
+ // Reserve our own id before dispatch so an early local echo cannot loop
378
+ // back through the bridge as another owner-authored group prompt.
379
+ if (typeof this.#outboundIds.reserve === 'function') {
380
+ this.#outboundIds.reserve(messageId);
381
+ } else {
382
+ this.#outboundIds.remember(messageId);
383
+ }
363
384
  const result = await this.#socket.sendMessage(
364
385
  target.jid,
365
386
  { text: chunk },
366
- index === 0 && target.quoted ? { quoted: target.quoted } : undefined,
387
+ options,
367
388
  );
368
389
  this.#outboundIds.remember(result?.key?.id);
369
390
  if (typeof result?.key?.id === 'string' && result.key.id) {