@oxidezap/baileyrs 0.2.12 → 0.3.0
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/lib/Compatibility/encode-proto.d.ts +7 -3
- package/lib/Compatibility/encode-proto.js +10 -5
- package/lib/Compatibility/proto-runtime.js +54 -7
- package/lib/Compatibility/signal-repository.d.ts +1 -1
- package/lib/Compatibility/signal-repository.js +61 -57
- package/lib/Compatibility/stanza-responses.d.ts +3 -3
- package/lib/Compatibility/stanza-responses.js +16 -13
- package/lib/Socket/blocking.js +11 -9
- package/lib/Socket/bridge-error-boundary.d.ts +2 -2
- package/lib/Socket/bridge-error-boundary.js +2 -2
- package/lib/Socket/business.js +63 -61
- package/lib/Socket/chat-actions.js +102 -98
- package/lib/Socket/client-operations.d.ts +6 -0
- package/lib/Socket/client-operations.js +2 -0
- package/lib/Socket/communities.js +8 -8
- package/lib/Socket/contacts.js +29 -26
- package/lib/Socket/events.js +2 -37
- package/lib/Socket/groups.js +22 -21
- package/lib/Socket/index.d.ts +2 -2
- package/lib/Socket/index.js +163 -107
- package/lib/Socket/internals.js +1 -1
- package/lib/Socket/messages.js +224 -218
- package/lib/Socket/newsletter.js +137 -133
- package/lib/Socket/prekeys.d.ts +1 -1
- package/lib/Socket/prekeys.js +20 -18
- package/lib/Socket/presence.js +15 -13
- package/lib/Socket/privacy.js +10 -10
- package/lib/Socket/profile.js +20 -18
- package/lib/Socket/server-queries.js +4 -4
- package/lib/Socket/terminal-close.d.ts +2 -36
- package/lib/Socket/terminal-close.js +29 -5
- package/lib/Socket/types.d.ts +2 -6
- package/package.json +4 -4
|
@@ -16,16 +16,16 @@ const rejectUnsupportedFields = (where, value, fields) => {
|
|
|
16
16
|
export const makeChatActionMethods = (ctx) => {
|
|
17
17
|
const methods = {
|
|
18
18
|
pinChat: async (jid, pin) => {
|
|
19
|
-
await
|
|
19
|
+
await ctx.withClient(client => client.pinChat(jid, pin));
|
|
20
20
|
},
|
|
21
21
|
muteChat: async (jid, muteUntil) => {
|
|
22
|
-
await
|
|
22
|
+
await ctx.withClient(client => client.muteChat(jid, muteUntil));
|
|
23
23
|
},
|
|
24
24
|
archiveChat: async (jid, archive) => {
|
|
25
|
-
await
|
|
25
|
+
await ctx.withClient(client => client.archiveChat(jid, archive));
|
|
26
26
|
},
|
|
27
27
|
starMessage: async (jid, messageId, star) => {
|
|
28
|
-
await
|
|
28
|
+
await ctx.withClient(client => client.starMessage(jid, messageId, star));
|
|
29
29
|
},
|
|
30
30
|
/**
|
|
31
31
|
* Compatibility wrapper for original Baileys chatModify API.
|
|
@@ -36,106 +36,110 @@ export const makeChatActionMethods = (ctx) => {
|
|
|
36
36
|
* synced, and no signature or type catches that.
|
|
37
37
|
*/
|
|
38
38
|
chatModify: async (mod, jid) => {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
else if ('pin' in mod) {
|
|
44
|
-
await client.pinChat(jid, mod.pin);
|
|
45
|
-
}
|
|
46
|
-
else if ('mute' in mod) {
|
|
47
|
-
await client.muteChat(jid, mod.mute);
|
|
48
|
-
}
|
|
49
|
-
else if ('star' in mod) {
|
|
50
|
-
for (const msg of mod.star.messages) {
|
|
51
|
-
await client.starMessage(jid, msg.id, mod.star.star);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
else if ('markRead' in mod) {
|
|
55
|
-
await client.markChatAsRead(jid, mod.markRead);
|
|
56
|
-
}
|
|
57
|
-
else if ('delete' in mod) {
|
|
58
|
-
await client.deleteChat(jid);
|
|
59
|
-
}
|
|
60
|
-
else if ('deleteForMe' in mod) {
|
|
61
|
-
await client.deleteMessageForMe(jid, mod.deleteForMe.key.id, !!mod.deleteForMe.key.fromMe);
|
|
62
|
-
}
|
|
63
|
-
else if ('pushNameSetting' in mod) {
|
|
64
|
-
await client.setPushName(mod.pushNameSetting);
|
|
65
|
-
}
|
|
66
|
-
else if ('contact' in mod) {
|
|
67
|
-
// Save/rename a contact (syncs the name to linked devices). `jid` is the
|
|
68
|
-
// contact's bare PN jid.
|
|
69
|
-
if (mod.contact) {
|
|
70
|
-
rejectUnsupportedFields('chatModify contact', mod.contact, ['lidJid', 'pnJid', 'username']);
|
|
71
|
-
await client.saveContact(jid, mod.contact.fullName ?? undefined, mod.contact.firstName ?? undefined, mod.contact.saveOnPrimaryAddressbook ?? true);
|
|
39
|
+
return ctx.withClient(async (client) => {
|
|
40
|
+
if ('archive' in mod) {
|
|
41
|
+
await client.archiveChat(jid, mod.archive);
|
|
72
42
|
}
|
|
73
|
-
else {
|
|
74
|
-
|
|
75
|
-
// the one contact mutation the wire models as a `Remove`, and a
|
|
76
|
-
// `Set` carrying empty values renames the contact to "".
|
|
77
|
-
await client.removeContact(jid);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
else if ('clear' in mod) {
|
|
81
|
-
// Clear a chat's messages while keeping the chat. `lastMessages` (the
|
|
82
|
-
// message range) is ignored, same as the `delete` branch — the bridge
|
|
83
|
-
// clears the whole chat. deleteStarred/deleteMedia aren't part of the
|
|
84
|
-
// Baileys `clear` shape, so default both to false (keep starred + media).
|
|
85
|
-
if (mod.clear) {
|
|
86
|
-
await client.clearChat(jid, false, false);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
else if ('disableLinkPreviews' in mod) {
|
|
90
|
-
await client.setLinkPreviewsDisabled(mod.disableLinkPreviews.isPreviewsDisabled ?? false);
|
|
91
|
-
}
|
|
92
|
-
else if ('addLabel' in mod) {
|
|
93
|
-
// One upstream variant, two wire actions: an edit carrying
|
|
94
|
-
// `deleted` is the delete, not a separate modification.
|
|
95
|
-
if (mod.addLabel.deleted) {
|
|
96
|
-
await client.deleteLabel(mod.addLabel.id);
|
|
43
|
+
else if ('pin' in mod) {
|
|
44
|
+
await client.pinChat(jid, mod.pin);
|
|
97
45
|
}
|
|
98
|
-
else {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
46
|
+
else if ('mute' in mod) {
|
|
47
|
+
await client.muteChat(jid, mod.mute);
|
|
48
|
+
}
|
|
49
|
+
else if ('star' in mod) {
|
|
50
|
+
for (const msg of mod.star.messages) {
|
|
51
|
+
await client.starMessage(jid, msg.id, mod.star.star);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else if ('markRead' in mod) {
|
|
55
|
+
await client.markChatAsRead(jid, mod.markRead);
|
|
56
|
+
}
|
|
57
|
+
else if ('delete' in mod) {
|
|
58
|
+
await client.deleteChat(jid);
|
|
59
|
+
}
|
|
60
|
+
else if ('deleteForMe' in mod) {
|
|
61
|
+
await client.deleteMessageForMe(jid, mod.deleteForMe.key.id, !!mod.deleteForMe.key.fromMe);
|
|
62
|
+
}
|
|
63
|
+
else if ('pushNameSetting' in mod) {
|
|
64
|
+
await client.setPushName(mod.pushNameSetting);
|
|
65
|
+
}
|
|
66
|
+
else if ('contact' in mod) {
|
|
67
|
+
// Save/rename a contact (syncs the name to linked devices). `jid` is the
|
|
68
|
+
// contact's bare PN jid.
|
|
69
|
+
if (mod.contact) {
|
|
70
|
+
rejectUnsupportedFields('chatModify contact', mod.contact, ['lidJid', 'pnJid', 'username']);
|
|
71
|
+
await client.saveContact(jid, mod.contact.fullName ?? undefined, mod.contact.firstName ?? undefined, mod.contact.saveOnPrimaryAddressbook ?? true);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
// Its own method, not `saveContact` with empty fields: removal is
|
|
75
|
+
// the one contact mutation the wire models as a `Remove`, and a
|
|
76
|
+
// `Set` carrying empty values renames the contact to "".
|
|
77
|
+
await client.removeContact(jid);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else if ('clear' in mod) {
|
|
81
|
+
// Clear a chat's messages while keeping the chat. `lastMessages` (the
|
|
82
|
+
// message range) is ignored, same as the `delete` branch — the bridge
|
|
83
|
+
// clears the whole chat. deleteStarred/deleteMedia aren't part of the
|
|
84
|
+
// Baileys `clear` shape, so default both to false (keep starred + media).
|
|
85
|
+
if (mod.clear) {
|
|
86
|
+
await client.clearChat(jid, false, false);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
else if ('disableLinkPreviews' in mod) {
|
|
90
|
+
await client.setLinkPreviewsDisabled(mod.disableLinkPreviews.isPreviewsDisabled ?? false);
|
|
91
|
+
}
|
|
92
|
+
else if ('addLabel' in mod) {
|
|
93
|
+
// One upstream variant, two wire actions: an edit carrying
|
|
94
|
+
// `deleted` is the delete, not a separate modification.
|
|
95
|
+
if (mod.addLabel.deleted) {
|
|
96
|
+
await client.deleteLabel(mod.addLabel.id);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
rejectUnsupportedFields('chatModify addLabel', mod.addLabel, ['predefinedId']);
|
|
100
|
+
// The mutation replaces the whole label, so a missing field is
|
|
101
|
+
// not "leave it alone", it is "set it to nothing". Upstream can
|
|
102
|
+
// omit one because it builds the proto directly; this call
|
|
103
|
+
// cannot, so both are required rather than defaulted.
|
|
104
|
+
if (mod.addLabel.name === undefined || mod.addLabel.color === undefined) {
|
|
105
|
+
throw new Boom('chatModify addLabel: name and color are both required, because the label mutation is a full replace and an omitted field would reset it', { statusCode: 400 });
|
|
106
|
+
}
|
|
107
|
+
await client.createLabel(mod.addLabel.id, mod.addLabel.name, mod.addLabel.color);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
else if ('addChatLabel' in mod) {
|
|
111
|
+
await client.addChatLabel(mod.addChatLabel.labelId, jid);
|
|
112
|
+
}
|
|
113
|
+
else if ('removeChatLabel' in mod) {
|
|
114
|
+
await client.removeChatLabel(mod.removeChatLabel.labelId, jid);
|
|
115
|
+
}
|
|
116
|
+
else if ('addMessageLabel' in mod) {
|
|
117
|
+
await client.addMessageLabel(mod.addMessageLabel.labelId, jid, mod.addMessageLabel.messageId);
|
|
118
|
+
}
|
|
119
|
+
else if ('removeMessageLabel' in mod) {
|
|
120
|
+
await client.removeMessageLabel(mod.removeMessageLabel.labelId, jid, mod.removeMessageLabel.messageId);
|
|
121
|
+
}
|
|
122
|
+
else if ('quickReply' in mod) {
|
|
123
|
+
// Upstream's `timestamp` is the app-state index key, the same slot
|
|
124
|
+
// the core calls `id`. Deleting is the same upsert with `deleted`.
|
|
125
|
+
// `||`, not `??`: upstream treats an empty timestamp as absent and
|
|
126
|
+
// mints a key, and the core rejects an empty index outright.
|
|
127
|
+
const id = mod.quickReply.timestamp || String(Math.floor(Date.now() / 1000));
|
|
128
|
+
if (mod.quickReply.deleted) {
|
|
129
|
+
await client.deleteQuickReply(id);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
await client.setQuickReply(id, mod.quickReply.shortcut ?? '', mod.quickReply.message ?? '', mod.quickReply.keywords ?? [], mod.quickReply.count ?? 0);
|
|
106
133
|
}
|
|
107
|
-
await client.createLabel(mod.addLabel.id, mod.addLabel.name, mod.addLabel.color);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
else if ('addChatLabel' in mod) {
|
|
111
|
-
await client.addChatLabel(mod.addChatLabel.labelId, jid);
|
|
112
|
-
}
|
|
113
|
-
else if ('removeChatLabel' in mod) {
|
|
114
|
-
await client.removeChatLabel(mod.removeChatLabel.labelId, jid);
|
|
115
|
-
}
|
|
116
|
-
else if ('addMessageLabel' in mod) {
|
|
117
|
-
await client.addMessageLabel(mod.addMessageLabel.labelId, jid, mod.addMessageLabel.messageId);
|
|
118
|
-
}
|
|
119
|
-
else if ('removeMessageLabel' in mod) {
|
|
120
|
-
await client.removeMessageLabel(mod.removeMessageLabel.labelId, jid, mod.removeMessageLabel.messageId);
|
|
121
|
-
}
|
|
122
|
-
else if ('quickReply' in mod) {
|
|
123
|
-
// Upstream's `timestamp` is the app-state index key, the same slot
|
|
124
|
-
// the core calls `id`. Deleting is the same upsert with `deleted`.
|
|
125
|
-
// `||`, not `??`: upstream treats an empty timestamp as absent and
|
|
126
|
-
// mints a key, and the core rejects an empty index outright.
|
|
127
|
-
const id = mod.quickReply.timestamp || String(Math.floor(Date.now() / 1000));
|
|
128
|
-
if (mod.quickReply.deleted) {
|
|
129
|
-
await client.deleteQuickReply(id);
|
|
130
134
|
}
|
|
131
135
|
else {
|
|
132
|
-
|
|
136
|
+
const variant = Object.keys(mod)[0] ?? '(empty)';
|
|
137
|
+
throw new Boom(`chatModify: unsupported modification '${variant}'`, {
|
|
138
|
+
statusCode: 400,
|
|
139
|
+
data: { variant, jid }
|
|
140
|
+
});
|
|
133
141
|
}
|
|
134
|
-
}
|
|
135
|
-
else {
|
|
136
|
-
const variant = Object.keys(mod)[0] ?? '(empty)';
|
|
137
|
-
throw new Boom(`chatModify: unsupported modification '${variant}'`, { statusCode: 400, data: { variant, jid } });
|
|
138
|
-
}
|
|
142
|
+
});
|
|
139
143
|
},
|
|
140
144
|
// Upstream models all of these as sugar over `chatModify`, and so do we:
|
|
141
145
|
// one place decides which bridge call a modification becomes, so the
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { WasmWhatsAppClient } from '@oxidezap/whatsapp-rust-bridge';
|
|
2
|
+
export interface ClientOperations<Client = WasmWhatsAppClient> {
|
|
3
|
+
withClient<T>(operation: (client: Client) => T | Promise<T>): Promise<T>;
|
|
4
|
+
}
|
|
5
|
+
export declare const makeWithClient: <Client>(getClient: () => Promise<Client>) => ClientOperations<Client>['withClient'];
|
|
6
|
+
//# sourceMappingURL=client-operations.d.ts.map
|
|
@@ -6,7 +6,7 @@ import { GROUP_REQUEST_ACTIONS, GROUP_SETTINGS, JOIN_APPROVAL_MODES, makeGroupMe
|
|
|
6
6
|
export const makeCommunityMethods = (ctx, groups = makeGroupMethods(ctx)) => {
|
|
7
7
|
const communityMetadata = async (jid) => groups.groupMetadata(jid);
|
|
8
8
|
const communityFetchAllParticipating = async () => {
|
|
9
|
-
const bridgeCommunities = await
|
|
9
|
+
const bridgeCommunities = await ctx.withClient(client => client.communityFetchAllParticipating());
|
|
10
10
|
const result = {};
|
|
11
11
|
for (const [communityJid, metadata] of Object.entries(bridgeCommunities)) {
|
|
12
12
|
result[communityJid] = bridgeGroupMetadataToBaileys(metadata);
|
|
@@ -17,30 +17,30 @@ export const makeCommunityMethods = (ctx, groups = makeGroupMethods(ctx)) => {
|
|
|
17
17
|
return {
|
|
18
18
|
communityMetadata,
|
|
19
19
|
communityCreate: async (subject, body) => {
|
|
20
|
-
const metadata = await
|
|
20
|
+
const metadata = await ctx.withClient(client => client.createCommunity(subject, body || undefined, true, true, true));
|
|
21
21
|
return bridgeGroupMetadataToBaileys(metadata);
|
|
22
22
|
},
|
|
23
23
|
communityCreateGroup: async (subject, participants, parentCommunityJid) => {
|
|
24
|
-
const metadata = await
|
|
24
|
+
const metadata = await ctx.withClient(client => client.createCommunitySubgroup(subject, participants, parentCommunityJid));
|
|
25
25
|
return bridgeGroupMetadataToBaileys(metadata);
|
|
26
26
|
},
|
|
27
27
|
communityLeave: async (id) => {
|
|
28
|
-
await
|
|
28
|
+
await ctx.withClient(client => client.deactivateCommunity(id));
|
|
29
29
|
},
|
|
30
30
|
communityUpdateSubject: async (jid, subject) => {
|
|
31
31
|
await groups.groupUpdateSubject(jid, subject);
|
|
32
32
|
},
|
|
33
33
|
communityLinkGroup: async (groupJid, parentCommunityJid) => {
|
|
34
|
-
await
|
|
34
|
+
await ctx.withClient(client => client.linkCommunitySubgroups(parentCommunityJid, [groupJid]));
|
|
35
35
|
},
|
|
36
36
|
communityUnlinkGroup: async (groupJid, parentCommunityJid) => {
|
|
37
|
-
await
|
|
37
|
+
await ctx.withClient(client => client.unlinkCommunitySubgroups(parentCommunityJid, [groupJid], false));
|
|
38
38
|
},
|
|
39
39
|
communityFetchLinkedGroups: async (jid) => {
|
|
40
40
|
const metadata = await groups.groupMetadata(jid);
|
|
41
41
|
const communityJid = metadata.linkedParent || jid;
|
|
42
42
|
const isCommunity = !metadata.linkedParent;
|
|
43
|
-
const subgroups = await
|
|
43
|
+
const subgroups = await ctx.withClient(client => client.getCommunitySubgroups(communityJid));
|
|
44
44
|
const linkedGroups = subgroups.map(group => ({
|
|
45
45
|
id: group.id,
|
|
46
46
|
subject: group.subject,
|
|
@@ -61,7 +61,7 @@ export const makeCommunityMethods = (ctx, groups = makeGroupMethods(ctx)) => {
|
|
|
61
61
|
},
|
|
62
62
|
communityParticipantsUpdate: async (jid, participants, action) => {
|
|
63
63
|
assertArgumentDomain('communityParticipantsUpdate', 'action', action, PARTICIPANT_ACTIONS);
|
|
64
|
-
return bridgeParticipantChangesToBaileys(await
|
|
64
|
+
return bridgeParticipantChangesToBaileys(await ctx.withClient(client => client.communityParticipantsUpdate(jid, participants, action)));
|
|
65
65
|
},
|
|
66
66
|
communityUpdateDescription: groups.groupUpdateDescription,
|
|
67
67
|
communityInviteCode: groups.groupInviteCode,
|
package/lib/Socket/contacts.js
CHANGED
|
@@ -1,29 +1,32 @@
|
|
|
1
1
|
import { assertArgumentDomain } from '../Utils/argument-domain.js';
|
|
2
2
|
export const PROFILE_PICTURE_TYPES = ['preview', 'image'];
|
|
3
|
-
export const makeContactMethods = (ctx) =>
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
3
|
+
export const makeContactMethods = (ctx) => {
|
|
4
|
+
return {
|
|
5
|
+
onWhatsApp: async (...phoneNumber) => {
|
|
6
|
+
return ctx.withClient(async (client) => {
|
|
7
|
+
// Single batched usync — the bridge splits PN/LID inputs internally and
|
|
8
|
+
// returns lid/pnJid/isBusiness in the same payload, so no secondary IQ.
|
|
9
|
+
const results = await client.isOnWhatsApp(phoneNumber);
|
|
10
|
+
return results.map(r => {
|
|
11
|
+
const out = { exists: r.isRegistered, jid: r.jid, isBusiness: r.isBusiness };
|
|
12
|
+
if (r.lid)
|
|
13
|
+
out.lid = r.lid;
|
|
14
|
+
if (r.pnJid)
|
|
15
|
+
out.pnJid = r.pnJid;
|
|
16
|
+
if (r.verifiedName)
|
|
17
|
+
out.verifiedName = r.verifiedName;
|
|
18
|
+
return out;
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
},
|
|
22
|
+
profilePictureUrl: async (jid, type = 'preview', timeoutMs) => {
|
|
23
|
+
assertArgumentDomain('profilePictureUrl', 'type', type, PROFILE_PICTURE_TYPES);
|
|
24
|
+
const result = await ctx.withClient(client => client.profilePictureUrl(jid, type, timeoutMs));
|
|
25
|
+
return result?.url;
|
|
26
|
+
},
|
|
27
|
+
fetchUserInfo: async (...jids) => {
|
|
28
|
+
return await ctx.withClient(client => client.fetchUserInfo(jids));
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
};
|
|
29
32
|
//# sourceMappingURL=contacts.js.map
|
package/lib/Socket/events.js
CHANGED
|
@@ -22,7 +22,7 @@ import { buildGroupCreateStubMessage, buildGroupJoinRequestEvents, buildGroupNot
|
|
|
22
22
|
import { emitMessageUpsert } from '../Compatibility/message-upsert.js';
|
|
23
23
|
import { extractMessageCappingPayload } from './message-capping.js';
|
|
24
24
|
import { mapReachoutTimelock } from './reachout.js';
|
|
25
|
-
import { isReconnectableConnectFailure } from './terminal-close.js';
|
|
25
|
+
import { isReconnectableConnectFailure, mapConnectFailureToDisconnect } from './terminal-close.js';
|
|
26
26
|
const CANONICAL_MESSAGE_EVENT = 'message';
|
|
27
27
|
const MESSAGE_UPSERT_APPEND = 'append';
|
|
28
28
|
const MESSAGE_UPSERT_NOTIFY = 'notify';
|
|
@@ -191,41 +191,6 @@ const emitRetrying = (ctx) => ctx.ev.emit('connection.update', {
|
|
|
191
191
|
* dispatcher for why `badSession` was the wrong home.
|
|
192
192
|
*/
|
|
193
193
|
const CLIENT_OUTDATED_STATUS = 405;
|
|
194
|
-
/**
|
|
195
|
-
* Map bridge `ConnectFailureReason` wire codes (per the bridge's
|
|
196
|
-
* `.d.ts` annotation) onto upstream Baileys' `DisconnectReason`.
|
|
197
|
-
* Unknown codes fall through to `connectionClosed` so existing
|
|
198
|
-
* reconnect heuristics keep working.
|
|
199
|
-
*
|
|
200
|
-
* Several cases here are belt-and-braces: the engine dispatches its own event
|
|
201
|
-
* for `is_logged_out()` reasons (401/403/406) and for 405, so those never
|
|
202
|
-
* reach `connectFailure` in practice. Kept because they cost nothing and the
|
|
203
|
-
* engine's routing is not ours to depend on.
|
|
204
|
-
*/
|
|
205
|
-
const mapConnectFailureToDisconnect = (reason) => {
|
|
206
|
-
switch (reason) {
|
|
207
|
-
case 401: // LoggedOut
|
|
208
|
-
case 403: // MainDeviceGone
|
|
209
|
-
case 406: // UnknownLogout
|
|
210
|
-
return DisconnectReason.loggedOut;
|
|
211
|
-
case 402: // TempBanned
|
|
212
|
-
return DisconnectReason.forbidden;
|
|
213
|
-
case 405: // ClientOutdated
|
|
214
|
-
return CLIENT_OUTDATED_STATUS;
|
|
215
|
-
case 411: // MultideviceMismatch (legacy alias)
|
|
216
|
-
return DisconnectReason.multideviceMismatch;
|
|
217
|
-
case 503: // ServiceUnavailable
|
|
218
|
-
case 501: // Experimental
|
|
219
|
-
return DisconnectReason.unavailableService;
|
|
220
|
-
case 408: // Timed out
|
|
221
|
-
return DisconnectReason.timedOut;
|
|
222
|
-
case 515: // RestartRequired
|
|
223
|
-
return DisconnectReason.restartRequired;
|
|
224
|
-
// 400, 409, 413, 414, 415, 418, 500, undefined → generic close
|
|
225
|
-
default:
|
|
226
|
-
return DisconnectReason.connectionClosed;
|
|
227
|
-
}
|
|
228
|
-
};
|
|
229
194
|
const describeTempBan = (code) => {
|
|
230
195
|
switch (code) {
|
|
231
196
|
case 101:
|
|
@@ -581,7 +546,7 @@ const DISPATCHERS = {
|
|
|
581
546
|
// the same complete lifecycle and ordering as upstream.
|
|
582
547
|
void (async () => {
|
|
583
548
|
try {
|
|
584
|
-
const bridgeMetadata = await
|
|
549
|
+
const bridgeMetadata = await ctx.withClient(client => client.getGroupMetadata(evt.groupJid));
|
|
585
550
|
const metadata = bridgeGroupMetadataToBaileys(bridgeMetadata);
|
|
586
551
|
ctx.ev.emit('chats.upsert', [
|
|
587
552
|
{ id: metadata.id, name: metadata.subject, conversationTimestamp: metadata.creation }
|
package/lib/Socket/groups.js
CHANGED
|
@@ -22,22 +22,23 @@ export const JOIN_APPROVAL_MODES = ['on', 'off'];
|
|
|
22
22
|
const inviteExpirationNumber = (value) => toNumber(value);
|
|
23
23
|
export const makeGroupMethods = (ctx) => {
|
|
24
24
|
const groupMetadata = async (jid) => {
|
|
25
|
-
const metadata = await
|
|
25
|
+
const metadata = await ctx.withClient(client => client.getGroupMetadata(jid));
|
|
26
26
|
return bridgeGroupMetadataToBaileys(metadata);
|
|
27
27
|
};
|
|
28
28
|
const groupSettingUpdate = async (jid, setting) => {
|
|
29
29
|
const checked = assertArgumentDomain('groupSettingUpdate', 'setting', setting, GROUP_SETTINGS);
|
|
30
30
|
const mapped = GROUP_SETTING_ALIASES[checked];
|
|
31
|
-
await
|
|
31
|
+
await ctx.withClient(client => client.groupSettingUpdate(jid, mapped.setting, mapped.value));
|
|
32
32
|
};
|
|
33
33
|
const groupAcceptInviteV4 = ctx.ev.createBufferedFunction(async (key, inviteMessage
|
|
34
34
|
// oxlint-disable-next-line typescript/no-explicit-any -- the established public contract returns Promise<any>.
|
|
35
35
|
) => {
|
|
36
36
|
const messageKey = typeof key === 'string' ? { remoteJid: key } : key;
|
|
37
|
-
|
|
37
|
+
const groupJid = inviteMessage.groupJid;
|
|
38
|
+
if (!groupJid || !inviteMessage.inviteCode || !messageKey.remoteJid) {
|
|
38
39
|
throw new TypeError('groupAcceptInviteV4 requires groupJid, inviteCode and inviter JID');
|
|
39
40
|
}
|
|
40
|
-
const joinedJid = await
|
|
41
|
+
const joinedJid = await ctx.withClient(client => client.groupAcceptInviteV4(groupJid, inviteMessage.inviteCode, inviteExpirationNumber(inviteMessage.inviteExpiration), messageKey.remoteJid));
|
|
41
42
|
if (messageKey.id) {
|
|
42
43
|
const expiredInvite = proto.Message.GroupInviteMessage.fromObject(inviteMessage);
|
|
43
44
|
expiredInvite.inviteExpiration = 0;
|
|
@@ -68,61 +69,61 @@ export const makeGroupMethods = (ctx) => {
|
|
|
68
69
|
return {
|
|
69
70
|
groupMetadata,
|
|
70
71
|
groupCreate: async (subject, participants) => {
|
|
71
|
-
const metadata = await
|
|
72
|
+
const metadata = await ctx.withClient(client => client.createGroup(subject, participants));
|
|
72
73
|
return bridgeGroupMetadataToBaileys(metadata);
|
|
73
74
|
},
|
|
74
75
|
groupLeave: async (id) => {
|
|
75
|
-
await
|
|
76
|
+
await ctx.withClient(client => client.groupLeave(id));
|
|
76
77
|
},
|
|
77
78
|
groupUpdateSubject: async (jid, subject) => {
|
|
78
|
-
await
|
|
79
|
+
await ctx.withClient(client => client.groupUpdateSubject(jid, subject));
|
|
79
80
|
},
|
|
80
81
|
groupRequestParticipantsList: async (jid) => {
|
|
81
|
-
return bridgeMembershipRequestsToBaileys(await
|
|
82
|
+
return bridgeMembershipRequestsToBaileys(await ctx.withClient(client => client.groupRequestParticipantsList(jid)));
|
|
82
83
|
},
|
|
83
84
|
groupRequestParticipantsUpdate: async (jid, participants, action) => {
|
|
84
85
|
assertArgumentDomain('groupRequestParticipantsUpdate', 'action', action, GROUP_REQUEST_ACTIONS);
|
|
85
|
-
return bridgeMembershipRequestUpdatesToBaileys(await
|
|
86
|
+
return bridgeMembershipRequestUpdatesToBaileys(await ctx.withClient(client => client.groupRequestParticipantsUpdate(jid, participants, action)));
|
|
86
87
|
},
|
|
87
88
|
groupParticipantsUpdate: async (jid, participants, action) => {
|
|
88
89
|
assertArgumentDomain('groupParticipantsUpdate', 'action', action, PARTICIPANT_ACTIONS);
|
|
89
|
-
return bridgeParticipantChangesToBaileys(await
|
|
90
|
+
return bridgeParticipantChangesToBaileys(await ctx.withClient(client => client.groupParticipantsUpdate(jid, participants, action)));
|
|
90
91
|
},
|
|
91
92
|
groupUpdateDescription: async (jid, description) => {
|
|
92
|
-
await
|
|
93
|
+
await ctx.withClient(client => client.groupUpdateDescription(jid, description));
|
|
93
94
|
},
|
|
94
95
|
groupInviteCode: async (jid) => {
|
|
95
|
-
return bridgeInviteLinkToCode(await
|
|
96
|
+
return bridgeInviteLinkToCode(await ctx.withClient(client => client.groupInviteCode(jid)));
|
|
96
97
|
},
|
|
97
98
|
groupRevokeInvite: async (jid) => {
|
|
98
|
-
return bridgeInviteLinkToCode(await
|
|
99
|
+
return bridgeInviteLinkToCode(await ctx.withClient(client => client.groupRevokeInvite(jid)));
|
|
99
100
|
},
|
|
100
101
|
groupAcceptInvite: async (code) => {
|
|
101
|
-
return
|
|
102
|
+
return ctx.withClient(client => client.groupAcceptInvite(code));
|
|
102
103
|
},
|
|
103
104
|
groupRevokeInviteV4: async (groupJid, invitedJid) => {
|
|
104
|
-
return
|
|
105
|
+
return ctx.withClient(client => client.groupRevokeInviteV4(groupJid, invitedJid));
|
|
105
106
|
},
|
|
106
107
|
groupAcceptInviteV4,
|
|
107
108
|
groupGetInviteInfo: async (code) => {
|
|
108
|
-
return bridgeGroupMetadataToBaileys(await
|
|
109
|
+
return bridgeGroupMetadataToBaileys(await ctx.withClient(client => client.groupGetInviteInfo(code)));
|
|
109
110
|
},
|
|
110
111
|
groupToggleEphemeral: async (jid, ephemeralExpiration) => {
|
|
111
|
-
await
|
|
112
|
+
await ctx.withClient(client => client.groupToggleEphemeral(jid, ephemeralExpiration));
|
|
112
113
|
},
|
|
113
114
|
groupSettingUpdate,
|
|
114
115
|
groupMemberAddMode: async (jid, mode) => {
|
|
115
116
|
assertArgumentDomain('groupMemberAddMode', 'mode', mode, MEMBER_ADD_MODES);
|
|
116
|
-
await
|
|
117
|
+
await ctx.withClient(client => client.groupMemberAddMode(jid, mode));
|
|
117
118
|
},
|
|
118
119
|
groupJoinApprovalMode: async (jid, mode) => {
|
|
119
120
|
// Anything but 'on' used to mean off, so a typo turned approvals off
|
|
120
121
|
// and reported success.
|
|
121
122
|
assertArgumentDomain('groupJoinApprovalMode', 'mode', mode, JOIN_APPROVAL_MODES);
|
|
122
|
-
await
|
|
123
|
+
await ctx.withClient(client => client.groupSettingUpdate(jid, 'membership_approval', mode === 'on'));
|
|
123
124
|
},
|
|
124
125
|
groupFetchAllParticipating: async () => {
|
|
125
|
-
const bridgeGroups = await
|
|
126
|
+
const bridgeGroups = await ctx.withClient(client => client.groupFetchAllParticipating());
|
|
126
127
|
const result = {};
|
|
127
128
|
for (const [groupJid, metadata] of Object.entries(bridgeGroups)) {
|
|
128
129
|
result[groupJid] = bridgeGroupMetadataToBaileys(metadata);
|
|
@@ -131,7 +132,7 @@ export const makeGroupMethods = (ctx) => {
|
|
|
131
132
|
return result;
|
|
132
133
|
},
|
|
133
134
|
updateMemberLabel: async (jid, memberLabel) => {
|
|
134
|
-
return
|
|
135
|
+
return ctx.withClient(client => client.updateMemberLabel(jid, memberLabel.slice(0, 30)));
|
|
135
136
|
}
|
|
136
137
|
};
|
|
137
138
|
};
|
package/lib/Socket/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer';
|
|
2
|
-
import { type UploadMediaResult } from '@oxidezap/whatsapp-rust-bridge';
|
|
2
|
+
import { type WasmWhatsAppClient, type UploadMediaResult } from '@oxidezap/whatsapp-rust-bridge';
|
|
3
3
|
import { WebSocketClient } from '../Compatibility/websocket-client.js';
|
|
4
4
|
import { type MediaType } from '../Defaults/index.js';
|
|
5
5
|
import type { BinaryNode, AuthenticationCreds, ConnectionState, Contact, ReachoutTimelockState, SignalKeyStoreWithTransaction, UserFacingSocketConfig, WABusinessProfile, WAMessage, WAMessageKey, WAPresence } from '../Types/index.js';
|
|
@@ -220,7 +220,7 @@ declare const makeWASocket: (config: UserFacingSocketConfig) => {
|
|
|
220
220
|
*/
|
|
221
221
|
[Symbol.asyncDispose](): Promise<void>;
|
|
222
222
|
user: Contact | undefined;
|
|
223
|
-
waClient:
|
|
223
|
+
waClient: WasmWhatsAppClient | undefined;
|
|
224
224
|
isConnected: boolean;
|
|
225
225
|
isLoggedIn: boolean;
|
|
226
226
|
authState: {
|