@xmanrui/dsh-im 3.0.3 → 3.0.4
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/README.en.md +2 -2
- package/README.md +2 -2
- package/lib/client.js +12 -27
- package/lib/index.js +180 -180
- package/package.json +1 -1
- package/plugin-src/client/channels/whatsapp/api.js +0 -5
- package/plugin-src/client/channels/whatsapp/index.js +9 -30
- package/plugin-src/client/i18n.js +0 -3
- package/plugin-src/host/channels/whatsapp/rpc.mjs +2 -2
- package/src/channels/whatsapp/config-store.mjs +0 -1
- package/src/channels/whatsapp/whatsapp-controller.mjs +0 -1
- package/src/channels/whatsapp/whatsapp-runtime.mjs +6 -42
package/package.json
CHANGED
|
@@ -97,11 +97,6 @@ function normalizeBot(value) {
|
|
|
97
97
|
typeof entry === 'string' && /^[1-9]\d{4,14}$/.test(entry)
|
|
98
98
|
)))]
|
|
99
99
|
: [],
|
|
100
|
-
groupAllowedNumbers: Array.isArray(value.accessPolicy?.groupAllowedNumbers)
|
|
101
|
-
? [...new Set(value.accessPolicy.groupAllowedNumbers.filter((entry) => (
|
|
102
|
-
typeof entry === 'string' && /^[1-9]\d{4,14}$/.test(entry)
|
|
103
|
-
)))]
|
|
104
|
-
: [],
|
|
105
100
|
},
|
|
106
101
|
bot: {
|
|
107
102
|
name: text(value.bot?.name, 'WhatsApp机器人', 100),
|
|
@@ -37,8 +37,6 @@ function accessPolicyFor(account) {
|
|
|
37
37
|
accessMode,
|
|
38
38
|
allowedNumbers: Array.isArray(account?.accessPolicy?.allowedNumbers)
|
|
39
39
|
? account.accessPolicy.allowedNumbers : [],
|
|
40
|
-
groupAllowedNumbers: Array.isArray(account?.accessPolicy?.groupAllowedNumbers)
|
|
41
|
-
? account.accessPolicy.groupAllowedNumbers : [],
|
|
42
40
|
};
|
|
43
41
|
}
|
|
44
42
|
|
|
@@ -54,39 +52,30 @@ function allowedNumbersFromText(value) {
|
|
|
54
52
|
export function WhatsappAccessSettings({ account, busy = false, onSave }) {
|
|
55
53
|
const policy = accessPolicyFor(account);
|
|
56
54
|
const sourceNumbers = policy.allowedNumbers.join('\n');
|
|
57
|
-
const sourceGroupNumbers = policy.groupAllowedNumbers.join('\n');
|
|
58
55
|
const helpId = React.useId();
|
|
59
56
|
const [accessMode, setAccessMode] = React.useState(policy.accessMode);
|
|
60
57
|
const [allowedNumbers, setAllowedNumbers] = React.useState(sourceNumbers);
|
|
61
|
-
const [groupAllowedNumbers, setGroupAllowedNumbers] = React.useState(sourceGroupNumbers);
|
|
62
58
|
const [error, setError] = React.useState(null);
|
|
63
59
|
|
|
64
60
|
React.useEffect(() => {
|
|
65
61
|
setAccessMode(policy.accessMode);
|
|
66
62
|
setAllowedNumbers(sourceNumbers);
|
|
67
|
-
setGroupAllowedNumbers(sourceGroupNumbers);
|
|
68
63
|
setError(null);
|
|
69
|
-
}, [policy.accessMode, sourceNumbers
|
|
64
|
+
}, [policy.accessMode, sourceNumbers]);
|
|
70
65
|
|
|
71
66
|
const save = async (event) => {
|
|
72
67
|
event.preventDefault();
|
|
73
68
|
setError(null);
|
|
74
69
|
try {
|
|
75
70
|
const normalized = allowedNumbersFromText(allowedNumbers);
|
|
76
|
-
const normalizedGroup = allowedNumbersFromText(groupAllowedNumbers);
|
|
77
71
|
if (typeof onSave !== 'function') throw new Error('WhatsApp 访问设置暂不可用。');
|
|
78
|
-
await onSave({
|
|
79
|
-
accessMode,
|
|
80
|
-
allowedNumbers: normalized,
|
|
81
|
-
groupAllowedNumbers: normalizedGroup,
|
|
82
|
-
});
|
|
72
|
+
await onSave({ accessMode, allowedNumbers: normalized });
|
|
83
73
|
} catch (caught) {
|
|
84
74
|
setError(caught?.message ?? 'WhatsApp 访问设置保存失败。');
|
|
85
75
|
}
|
|
86
76
|
};
|
|
87
77
|
|
|
88
78
|
const allowlistEnabled = accessMode === 'private-allowlist';
|
|
89
|
-
const groupAllowlistEnabled = accessMode === 'open';
|
|
90
79
|
const labels = {
|
|
91
80
|
'self-only': '仅自己模式',
|
|
92
81
|
'private-allowlist': '指定联系人模式',
|
|
@@ -114,7 +103,7 @@ export function WhatsappAccessSettings({ account, busy = false, onSave }) {
|
|
|
114
103
|
h('span', null, '响应自聊和白名单联系人的私聊,忽略群聊。')),
|
|
115
104
|
h('span', { className: 'dwa-accessTooltipItem' },
|
|
116
105
|
h('strong', null, '开放响应模式'),
|
|
117
|
-
h('span', null, '
|
|
106
|
+
h('span', null, '响应所有私聊、已绑定账号自己发出的群聊消息,以及其他群成员的提及或回复。')))))),
|
|
118
107
|
h('label', { className: 'dwa-accessField' },
|
|
119
108
|
h('span', null, '模式'),
|
|
120
109
|
h('select', {
|
|
@@ -126,28 +115,18 @@ export function WhatsappAccessSettings({ account, busy = false, onSave }) {
|
|
|
126
115
|
h('option', { value: 'self-only' }, '仅自己模式(默认)'),
|
|
127
116
|
h('option', { value: 'private-allowlist' }, '指定联系人模式'),
|
|
128
117
|
h('option', { value: 'open' }, '开放响应模式'))),
|
|
129
|
-
|
|
118
|
+
allowlistEnabled
|
|
130
119
|
? h('label', { className: 'dwa-accessField' },
|
|
131
|
-
h('span', null,
|
|
132
|
-
? '允许私聊的 WhatsApp 电话号码'
|
|
133
|
-
: '允许在群聊中呼叫机器人的 WhatsApp 电话号码'),
|
|
120
|
+
h('span', null, '允许私聊的 WhatsApp 电话号码'),
|
|
134
121
|
h('textarea', {
|
|
135
|
-
value:
|
|
122
|
+
value: allowedNumbers,
|
|
136
123
|
disabled: busy,
|
|
137
124
|
rows: 3,
|
|
138
125
|
placeholder: '每行一个含国家或地区代码的号码',
|
|
139
|
-
'aria-label':
|
|
140
|
-
|
|
141
|
-
: '允许在群聊中呼叫机器人的 WhatsApp 电话号码',
|
|
142
|
-
onChange: (event) => {
|
|
143
|
-
if (allowlistEnabled) setAllowedNumbers(event.target.value);
|
|
144
|
-
else setGroupAllowedNumbers(event.target.value);
|
|
145
|
-
setError(null);
|
|
146
|
-
},
|
|
126
|
+
'aria-label': '允许私聊的 WhatsApp 电话号码',
|
|
127
|
+
onChange: (event) => { setAllowedNumbers(event.target.value); setError(null); },
|
|
147
128
|
}),
|
|
148
|
-
h('small', null,
|
|
149
|
-
? '可以包含开头的 +,保存时会自动移除。'
|
|
150
|
-
: '留空表示所有群成员都可以通过提及或回复呼叫机器人。'))
|
|
129
|
+
h('small', null, '可以包含开头的 +,保存时会自动移除。'))
|
|
151
130
|
: null,
|
|
152
131
|
allowlistEnabled && allowedNumbers.trim() === ''
|
|
153
132
|
? h('p', { className: 'dwa-accessWarning', role: 'status' },
|
|
@@ -380,12 +380,9 @@ const EN = Object.freeze({
|
|
|
380
380
|
'只响应已绑定 WhatsApp 账号的自聊消息。': 'Only respond to self-chat messages from the linked WhatsApp account.',
|
|
381
381
|
'响应自聊和白名单联系人的私聊,忽略群聊。': 'Respond to self-chat and allowlisted direct messages; ignore group messages.',
|
|
382
382
|
'响应所有私聊、已绑定账号自己发出的群聊消息,以及其他群成员的提及或回复。': 'Respond to all direct messages, group messages sent by the linked account, and mentions or replies from other group members.',
|
|
383
|
-
'响应所有私聊、已绑定账号自己发出的群聊消息,以及允许成员的提及或回复;群聊号码列表留空时允许所有群成员。': 'Respond to all direct messages, group messages sent by the linked account, and mentions or replies from allowed members; leaving the group number list empty allows every group member.',
|
|
384
383
|
'允许私聊的 WhatsApp 电话号码': 'WhatsApp phone numbers allowed to send direct messages',
|
|
385
|
-
'允许在群聊中呼叫机器人的 WhatsApp 电话号码': 'WhatsApp phone numbers allowed to call the bot in group chats',
|
|
386
384
|
'每行一个含国家或地区代码的号码': 'One number with country or region code per line',
|
|
387
385
|
'可以包含开头的 +,保存时会自动移除。': 'A leading + is allowed and removed when saved.',
|
|
388
|
-
'留空表示所有群成员都可以通过提及或回复呼叫机器人。': 'Leave empty to let every group member call the bot by mentioning it or replying to it.',
|
|
389
386
|
'仅指定联系人模式使用白名单,切换模式时会保留。': 'Only Selected contacts uses the allowlist; it is retained when modes change.',
|
|
390
387
|
'白名单为空;保存后将只接受自聊消息。': 'The allowlist is empty; only self-chat messages will be accepted after saving.',
|
|
391
388
|
'电话号码必须包含国家或地区代码,每行一个。': 'Each phone number must include a country or region code on its own line.',
|
|
@@ -53,8 +53,8 @@ function payloadFailure(endpoint, payload) {
|
|
|
53
53
|
&& payload.confirm === true ? null : 'bot.delete requires a botId and confirm=true.';
|
|
54
54
|
}
|
|
55
55
|
if (endpoint === WHATSAPP_ENDPOINTS.setAccessPolicy) {
|
|
56
|
-
if (!exactKeys(payload, ['botId', 'accessMode', 'allowedNumbers'
|
|
57
|
-
|| Object.keys(payload).length !==
|
|
56
|
+
if (!exactKeys(payload, ['botId', 'accessMode', 'allowedNumbers'])
|
|
57
|
+
|| Object.keys(payload).length !== 3
|
|
58
58
|
|| !validId(payload.botId)) return '请输入有效的 WhatsApp 访问模式和电话号码。';
|
|
59
59
|
try {
|
|
60
60
|
normalizeWhatsappAccessPolicy(payload);
|
|
@@ -63,7 +63,6 @@ export function normalizeWhatsappAccessPolicy(value = {}) {
|
|
|
63
63
|
return Object.freeze({
|
|
64
64
|
accessMode,
|
|
65
65
|
allowedNumbers: normalizeWhatsappAllowedNumbers(value.allowedNumbers),
|
|
66
|
-
groupAllowedNumbers: normalizeWhatsappAllowedNumbers(value.groupAllowedNumbers),
|
|
67
66
|
});
|
|
68
67
|
}
|
|
69
68
|
|
|
@@ -334,7 +334,6 @@ export class WhatsappController {
|
|
|
334
334
|
connectedAt: new Date().toISOString(),
|
|
335
335
|
accessMode: previous?.accessMode,
|
|
336
336
|
allowedNumbers: previous?.allowedNumbers,
|
|
337
|
-
groupAllowedNumbers: previous?.groupAllowedNumbers,
|
|
338
337
|
};
|
|
339
338
|
try {
|
|
340
339
|
if (record.controller.signal.aborted || this.#closed) throw Object.assign(new Error(), { name: 'AbortError' });
|
|
@@ -3,7 +3,6 @@ import { createHash, randomBytes } from 'node:crypto';
|
|
|
3
3
|
import {
|
|
4
4
|
areJidsSameUser,
|
|
5
5
|
downloadMediaMessage,
|
|
6
|
-
jidNormalizedUser,
|
|
7
6
|
normalizeMessageContent,
|
|
8
7
|
} from '@whiskeysockets/baileys';
|
|
9
8
|
|
|
@@ -14,7 +13,6 @@ import { trackOutboundArtifactProviderPromise } from '../shared/semantic/artifac
|
|
|
14
13
|
import { createWhatsappBridgeStatus, WhatsappHarnessBridge } from './whatsapp-bridge.mjs';
|
|
15
14
|
import {
|
|
16
15
|
WHATSAPP_ACCESS_MODES,
|
|
17
|
-
normalizeWhatsappAccountJid,
|
|
18
16
|
normalizeWhatsappAccessPolicy,
|
|
19
17
|
} from './config-store.mjs';
|
|
20
18
|
import { createWhatsappWebSession } from './whatsapp-web-session.mjs';
|
|
@@ -206,21 +204,8 @@ export function createWhatsappMediaDownloader({
|
|
|
206
204
|
});
|
|
207
205
|
}
|
|
208
206
|
|
|
209
|
-
function whatsappAccountMatcher(accountJid, aliases) {
|
|
210
|
-
const accountJids = new Set(
|
|
211
|
-
[accountJid, ...(Array.isArray(aliases) ? aliases : [])]
|
|
212
|
-
.map((jid) => normalizeWhatsappAccountJid(jidNormalizedUser(jid)))
|
|
213
|
-
.filter(Boolean),
|
|
214
|
-
);
|
|
215
|
-
return (jid) => {
|
|
216
|
-
const normalized = normalizeWhatsappAccountJid(jidNormalizedUser(jid));
|
|
217
|
-
return normalized !== null && accountJids.has(normalized);
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
|
|
221
207
|
export function normalizeWhatsappMessage(message, accountJid, {
|
|
222
208
|
download = downloadMediaMessage,
|
|
223
|
-
accountAliases = [],
|
|
224
209
|
} = {}) {
|
|
225
210
|
const remoteJid = typeof message?.key?.remoteJid === 'string' ? message.key.remoteJid : '';
|
|
226
211
|
const alternateRemoteJid = typeof message?.key?.remoteJidAlt === 'string'
|
|
@@ -230,9 +215,8 @@ export function normalizeWhatsappMessage(message, accountJid, {
|
|
|
230
215
|
|| remoteJid.endsWith('@newsletter')) return null;
|
|
231
216
|
const group = remoteJid.endsWith('@g.us');
|
|
232
217
|
const fromMe = message.key.fromMe === true;
|
|
233
|
-
const matchesAccount = whatsappAccountMatcher(accountJid, accountAliases);
|
|
234
218
|
const selfChat = fromMe && !group
|
|
235
|
-
&& [remoteJid, alternateRemoteJid].some(
|
|
219
|
+
&& [remoteJid, alternateRemoteJid].some((jid) => jid && areJidsSameUser(jid, accountJid));
|
|
236
220
|
if (fromMe && !selfChat && !group) return null;
|
|
237
221
|
const senderJid = fromMe ? accountJid : group ? message.key.participant : remoteJid;
|
|
238
222
|
const senderAlternateJid = group && !fromMe ? message.key.participantAlt : alternateRemoteJid;
|
|
@@ -241,9 +225,9 @@ export function normalizeWhatsappMessage(message, accountJid, {
|
|
|
241
225
|
const content = normalizeMessageContent(message.message);
|
|
242
226
|
const context = messageContext(content);
|
|
243
227
|
const mentioned = Array.isArray(context?.mentionedJid)
|
|
244
|
-
&& context.mentionedJid.some(
|
|
228
|
+
&& context.mentionedJid.some((jid) => areJidsSameUser(jid, accountJid));
|
|
245
229
|
const replyToSelf = typeof context?.participant === 'string'
|
|
246
|
-
&&
|
|
230
|
+
&& areJidsSameUser(context.participant, accountJid);
|
|
247
231
|
const image = whatsappImageSource(message, content, download, { viewOnce });
|
|
248
232
|
const file = whatsappFileSource(message, content, download);
|
|
249
233
|
return {
|
|
@@ -251,7 +235,6 @@ export function normalizeWhatsappMessage(message, accountJid, {
|
|
|
251
235
|
providerMessageId: messageId,
|
|
252
236
|
senderId: senderJid,
|
|
253
237
|
senderAlternateId: typeof senderAlternateJid === 'string' ? senderAlternateJid : '',
|
|
254
|
-
senderIsSelf: fromMe,
|
|
255
238
|
senderIsBot: false,
|
|
256
239
|
kind: group ? 'group' : 'direct',
|
|
257
240
|
conversationId: remoteJid,
|
|
@@ -270,22 +253,12 @@ export function normalizeWhatsappMessage(message, accountJid, {
|
|
|
270
253
|
export function whatsappInboundAllowed(message, {
|
|
271
254
|
accessMode = WHATSAPP_ACCESS_MODES.selfOnly,
|
|
272
255
|
allowedNumbers = new Set(),
|
|
273
|
-
groupAllowedNumbers = new Set(),
|
|
274
256
|
} = {}) {
|
|
275
|
-
if (accessMode === WHATSAPP_ACCESS_MODES.open)
|
|
276
|
-
if (message?.kind !== 'group' || message.senderIsSelf === true) return true;
|
|
277
|
-
if (!(groupAllowedNumbers instanceof Set)) return false;
|
|
278
|
-
if (groupAllowedNumbers.size === 0) return true;
|
|
279
|
-
const senderJids = [message.senderId, message.senderAlternateId]
|
|
280
|
-
.filter((jid) => typeof jid === 'string' && jid.endsWith('@s.whatsapp.net'));
|
|
281
|
-
return [...groupAllowedNumbers].some((number) => senderJids.some((jid) => (
|
|
282
|
-
areJidsSameUser(jid, `${number}@s.whatsapp.net`)
|
|
283
|
-
)));
|
|
284
|
-
}
|
|
257
|
+
if (accessMode === WHATSAPP_ACCESS_MODES.open) return true;
|
|
285
258
|
if (message?.kind !== 'direct') return false;
|
|
286
259
|
if (message.selfChat === true) return true;
|
|
287
|
-
if (accessMode !== WHATSAPP_ACCESS_MODES.privateAllowlist
|
|
288
|
-
|
|
260
|
+
if (accessMode !== WHATSAPP_ACCESS_MODES.privateAllowlist
|
|
261
|
+
|| !(allowedNumbers instanceof Set)) return false;
|
|
289
262
|
const senderJids = [message.senderId, message.senderAlternateId]
|
|
290
263
|
.filter((jid) => typeof jid === 'string' && jid.endsWith('@s.whatsapp.net'));
|
|
291
264
|
return [...allowedNumbers].some((number) => senderJids.some((jid) => (
|
|
@@ -569,7 +542,6 @@ export class WhatsappRuntime {
|
|
|
569
542
|
#mediaUploadTimeoutMs;
|
|
570
543
|
#accessMode;
|
|
571
544
|
#allowedPrivateNumbers;
|
|
572
|
-
#allowedGroupNumbers;
|
|
573
545
|
#createSession;
|
|
574
546
|
#status = createWhatsappRuntimeStatus();
|
|
575
547
|
#abortController = null;
|
|
@@ -618,7 +590,6 @@ export class WhatsappRuntime {
|
|
|
618
590
|
const policy = normalizeWhatsappAccessPolicy(value);
|
|
619
591
|
this.#accessMode = policy.accessMode;
|
|
620
592
|
this.#allowedPrivateNumbers = new Set(policy.allowedNumbers);
|
|
621
|
-
this.#allowedGroupNumbers = new Set(policy.groupAllowedNumbers);
|
|
622
593
|
this.#config = { ...this.#config, ...policy };
|
|
623
594
|
return policy;
|
|
624
595
|
}
|
|
@@ -653,13 +624,7 @@ export class WhatsappRuntime {
|
|
|
653
624
|
{ code: 'relink-required' },
|
|
654
625
|
)),
|
|
655
626
|
onMessage: async (raw, context) => {
|
|
656
|
-
const linkedAccount = context?.socket?.user;
|
|
657
627
|
const message = normalizeWhatsappMessage(raw, this.#config.accountJid, {
|
|
658
|
-
accountAliases: [
|
|
659
|
-
linkedAccount?.id,
|
|
660
|
-
linkedAccount?.lid,
|
|
661
|
-
linkedAccount?.phoneNumber,
|
|
662
|
-
],
|
|
663
628
|
download: createWhatsappMediaDownloader({
|
|
664
629
|
socket: context?.socket,
|
|
665
630
|
logger: this.#logger,
|
|
@@ -670,7 +635,6 @@ export class WhatsappRuntime {
|
|
|
670
635
|
if (!whatsappInboundAllowed(message, {
|
|
671
636
|
accessMode: this.#accessMode,
|
|
672
637
|
allowedNumbers: this.#allowedPrivateNumbers,
|
|
673
|
-
groupAllowedNumbers: this.#allowedGroupNumbers,
|
|
674
638
|
})) {
|
|
675
639
|
this.#status.messagesRejected += 1;
|
|
676
640
|
this.#status.lastRejectedAt = new Date().toISOString();
|