@oxidezap/baileyrs 0.2.13 → 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/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 +1 -1
- package/lib/Socket/groups.js +20 -52
- package/lib/Socket/index.d.ts +2 -2
- package/lib/Socket/index.js +68 -90
- 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/types.d.ts +2 -6
- package/package.json +4 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { LIDMapping, SignalAuthState, SignalRepositoryWithLIDStore } from '../Types/index.js';
|
|
2
2
|
import type { ILogger } from '../Utils/logger.js';
|
|
3
3
|
import type { SocketContext } from '../Socket/types.js';
|
|
4
|
-
type SignalRepositoryContext = Pick<SocketContext, '
|
|
4
|
+
type SignalRepositoryContext = Pick<SocketContext, 'logger' | 'withClient'>;
|
|
5
5
|
export declare const jidToSignalProtocolAddressCompat: (jid: string) => string;
|
|
6
6
|
export declare const bindSignalRepositoryContext: (auth: SignalAuthState, ctx: SignalRepositoryContext) => void;
|
|
7
7
|
/** Default public factory; the socket binds its native context before invoking it. */
|
|
@@ -34,12 +34,12 @@ export const bindSignalRepositoryContext = (auth, ctx) => {
|
|
|
34
34
|
/** Default public factory; the socket binds its native context before invoking it. */
|
|
35
35
|
export const makeDefaultSignalRepository = (auth, logger, _pnToLIDFunc) => makeSignalRepository({
|
|
36
36
|
logger,
|
|
37
|
-
|
|
37
|
+
withClient: operation => {
|
|
38
38
|
const ctx = nativeContexts.get(auth);
|
|
39
39
|
if (!ctx) {
|
|
40
40
|
return Promise.reject(new Error('Signal repository is not bound to an active socket; pass it through SocketConfig'));
|
|
41
41
|
}
|
|
42
|
-
return ctx.
|
|
42
|
+
return ctx.withClient(operation);
|
|
43
43
|
}
|
|
44
44
|
});
|
|
45
45
|
const resolveMappings = async (client, inputs, direction, ctx) => {
|
|
@@ -69,63 +69,67 @@ const resolveMappings = async (client, inputs, direction, ctx) => {
|
|
|
69
69
|
return mappings.length ? mappings : null;
|
|
70
70
|
};
|
|
71
71
|
/** Translate the neutral bridge Signal surface into the public repository contract. */
|
|
72
|
-
export const makeSignalRepository = (ctx) =>
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
validateSession: async (jid) => ({ exists: await (await ctx.getClient()).signalValidateSession(jid) }),
|
|
94
|
-
jidToSignalProtocolAddress: jidToSignalProtocolAddressCompat,
|
|
95
|
-
migrateSession: async (fromJid, toJid) => {
|
|
96
|
-
if (!fromJid || !isLidNamespace(toJid))
|
|
97
|
-
return { migrated: 0, skipped: 0, total: 0 };
|
|
98
|
-
if (!isPnNamespace(fromJid))
|
|
99
|
-
return { migrated: 0, skipped: 0, total: 1 };
|
|
100
|
-
return (await ctx.getClient()).signalMigrateSessions(fromJid, toJid);
|
|
101
|
-
},
|
|
102
|
-
deleteSession: async (jids) => {
|
|
103
|
-
if (jids.length)
|
|
104
|
-
await (await ctx.getClient()).signalDeleteSessions(jids);
|
|
105
|
-
},
|
|
106
|
-
lidMapping: {
|
|
107
|
-
storeLIDPNMappings: async (pairs) => {
|
|
108
|
-
const valid = pairs.filter(({ lid, pn }) => {
|
|
109
|
-
const accepted = isLidNamespace(lid) && isPnNamespace(pn);
|
|
110
|
-
if (!accepted)
|
|
111
|
-
ctx.logger.warn(`Invalid LID-PN mapping: ${lid}, ${pn}`);
|
|
112
|
-
return accepted;
|
|
113
|
-
});
|
|
114
|
-
if (valid.length)
|
|
115
|
-
await (await ctx.getClient()).addLidPnMappings(valid);
|
|
72
|
+
export const makeSignalRepository = (ctx) => {
|
|
73
|
+
return {
|
|
74
|
+
decryptMessage: async (opts) => ctx.withClient(client => client.signalDecryptMessage(opts.jid, opts.type, opts.ciphertext)),
|
|
75
|
+
encryptMessage: async (opts) => ctx.withClient(client => client.signalEncryptMessage(opts.jid, opts.data)),
|
|
76
|
+
decryptGroupMessage: async (opts) => ctx.withClient(client => client.signalDecryptGroupMessage(opts.group, opts.authorJid, opts.msg)),
|
|
77
|
+
encryptGroupMessage: async (opts) => ctx.withClient(client => client.signalEncryptGroupMessage(opts.group, opts.data, opts.meId)),
|
|
78
|
+
processSenderKeyDistributionMessage: async ({ item, authorJid }) => {
|
|
79
|
+
if (!item.groupId) {
|
|
80
|
+
throw new Error('Group ID is required for sender key distribution message');
|
|
81
|
+
}
|
|
82
|
+
const distribution = item.axolotlSenderKeyDistributionMessage;
|
|
83
|
+
if (!distribution) {
|
|
84
|
+
throw new Error('Sender key distribution payload is required');
|
|
85
|
+
}
|
|
86
|
+
await ctx.withClient(client => client.signalProcessSenderKeyDistribution(item.groupId, authorJid, distribution));
|
|
87
|
+
},
|
|
88
|
+
getSenderKeyDistributionMessage: async ({ group, meId }) => ctx.withClient(client => client.signalGetSenderKeyDistribution(group, meId)),
|
|
89
|
+
hasSenderKey: async ({ group, meId }) => ctx.withClient(client => client.signalHasSenderKey(group, meId)),
|
|
90
|
+
getSessionInfo: async (jid) => (await ctx.withClient(client => client.signalGetSessionInfo(jid))) ?? null,
|
|
91
|
+
injectE2ESession: async ({ jid, session }) => {
|
|
92
|
+
await ctx.withClient(client => client.signalInstallPreKeyBundle(jid, session));
|
|
116
93
|
},
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
94
|
+
validateSession: async (jid) => ({ exists: await ctx.withClient(client => client.signalValidateSession(jid)) }),
|
|
95
|
+
jidToSignalProtocolAddress: jidToSignalProtocolAddressCompat,
|
|
96
|
+
migrateSession: async (fromJid, toJid) => {
|
|
97
|
+
if (!fromJid || !isLidNamespace(toJid))
|
|
98
|
+
return { migrated: 0, skipped: 0, total: 0 };
|
|
99
|
+
if (!isPnNamespace(fromJid))
|
|
100
|
+
return { migrated: 0, skipped: 0, total: 1 };
|
|
101
|
+
return ctx.withClient(client => client.signalMigrateSessions(fromJid, toJid));
|
|
120
102
|
},
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
103
|
+
deleteSession: async (jids) => {
|
|
104
|
+
if (jids.length)
|
|
105
|
+
await ctx.withClient(client => client.signalDeleteSessions(jids));
|
|
106
|
+
},
|
|
107
|
+
lidMapping: {
|
|
108
|
+
storeLIDPNMappings: async (pairs) => {
|
|
109
|
+
const valid = pairs.filter(({ lid, pn }) => {
|
|
110
|
+
const accepted = isLidNamespace(lid) && isPnNamespace(pn);
|
|
111
|
+
if (!accepted)
|
|
112
|
+
ctx.logger.warn(`Invalid LID-PN mapping: ${lid}, ${pn}`);
|
|
113
|
+
return accepted;
|
|
114
|
+
});
|
|
115
|
+
if (valid.length)
|
|
116
|
+
await ctx.withClient(client => client.addLidPnMappings(valid));
|
|
117
|
+
},
|
|
118
|
+
getLIDForPN: async (pn) => {
|
|
119
|
+
return ctx.withClient(async (client) => {
|
|
120
|
+
return (await resolveMappings(client, [pn], 'pn-to-lid', ctx))?.[0]?.lid ?? null;
|
|
121
|
+
});
|
|
122
|
+
},
|
|
123
|
+
getLIDsForPNs: async (pns) => ctx.withClient(client => resolveMappings(client, pns, 'pn-to-lid', ctx)),
|
|
124
|
+
getPNForLID: async (lid) => {
|
|
125
|
+
return ctx.withClient(async (client) => {
|
|
126
|
+
return (await resolveMappings(client, [lid], 'lid-to-pn', ctx))?.[0]?.pn ?? null;
|
|
127
|
+
});
|
|
128
|
+
},
|
|
129
|
+
getPNsForLIDs: async (lids) => ctx.withClient(client => resolveMappings(client, lids, 'lid-to-pn', ctx)),
|
|
130
|
+
close: () => undefined
|
|
125
131
|
},
|
|
126
|
-
getPNsForLIDs: async (lids) => resolveMappings(await ctx.getClient(), lids, 'lid-to-pn', ctx),
|
|
127
132
|
close: () => undefined
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
});
|
|
133
|
+
};
|
|
134
|
+
};
|
|
131
135
|
//# sourceMappingURL=signal-repository.js.map
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { WasmWhatsAppClient } from '@oxidezap/whatsapp-rust-bridge';
|
|
2
2
|
import type { BinaryNode } from '../Types/index.js';
|
|
3
|
+
import type { ClientOperations } from '../Socket/client-operations.js';
|
|
3
4
|
type StanzaResponseClient = Pick<WasmWhatsAppClient, 'acknowledgeStanza' | 'rejectStanza' | 'requestMessageRetry'>;
|
|
4
|
-
export interface StanzaResponseContext {
|
|
5
|
-
getClient: () => Promise<StanzaResponseClient>;
|
|
5
|
+
export interface StanzaResponseContext extends ClientOperations<StanzaResponseClient> {
|
|
6
6
|
}
|
|
7
7
|
/** Translate the public socket calls into the core-owned response operations. */
|
|
8
|
-
export declare const makeStanzaResponseMethods: (
|
|
8
|
+
export declare const makeStanzaResponseMethods: (ctx: StanzaResponseContext) => {
|
|
9
9
|
sendMessageAck: (node: BinaryNode, errorCode?: number) => Promise<void>;
|
|
10
10
|
sendRetryRequest: (node: BinaryNode, forceIncludeKeys?: boolean) => Promise<void>;
|
|
11
11
|
};
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
/** Translate the public socket calls into the core-owned response operations. */
|
|
2
|
-
export const makeStanzaResponseMethods = (
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
export const makeStanzaResponseMethods = (ctx) => {
|
|
3
|
+
return {
|
|
4
|
+
sendMessageAck: async (node, errorCode) => {
|
|
5
|
+
return ctx.withClient(async (client) => {
|
|
6
|
+
if (errorCode) {
|
|
7
|
+
await client.rejectStanza(node, errorCode);
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
await client.acknowledgeStanza(node);
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
},
|
|
14
|
+
sendRetryRequest: async (node, forceIncludeKeys = false) => {
|
|
15
|
+
await ctx.withClient(client => client.requestMessageRetry(node, forceIncludeKeys));
|
|
7
16
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
},
|
|
12
|
-
sendRetryRequest: async (node, forceIncludeKeys = false) => {
|
|
13
|
-
await (await getClient()).requestMessageRetry(node, forceIncludeKeys);
|
|
14
|
-
}
|
|
15
|
-
});
|
|
17
|
+
};
|
|
18
|
+
};
|
|
16
19
|
//# sourceMappingURL=stanza-responses.js.map
|
package/lib/Socket/blocking.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { bridgeBlocklistToBaileys } from '../Compatibility/socket-results.js';
|
|
2
2
|
import { assertArgumentDomain } from '../Utils/argument-domain.js';
|
|
3
3
|
export const BLOCK_ACTIONS = ['block', 'unblock'];
|
|
4
|
-
export const makeBlockingMethods = (ctx) =>
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
4
|
+
export const makeBlockingMethods = (ctx) => {
|
|
5
|
+
return {
|
|
6
|
+
updateBlockStatus: async (jid, action) => {
|
|
7
|
+
assertArgumentDomain('updateBlockStatus', 'action', action, BLOCK_ACTIONS);
|
|
8
|
+
await ctx.withClient(client => client.updateBlockStatus(jid, action));
|
|
9
|
+
},
|
|
10
|
+
fetchBlocklist: async () => {
|
|
11
|
+
return bridgeBlocklistToBaileys(await ctx.withClient(client => client.fetchBlocklist()));
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
};
|
|
13
15
|
//# sourceMappingURL=blocking.js.map
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
* - Anything that is not the bridge's shape (an `Error` carrying a string
|
|
22
22
|
* `kind`) passes through with the same identity it arrived with.
|
|
23
23
|
*
|
|
24
|
-
* The
|
|
25
|
-
*
|
|
24
|
+
* The private `getClient()` getter returns a cached `Proxy` for each client.
|
|
25
|
+
* Its method wrappers are built on first access and cached, so
|
|
26
26
|
* the happy path pays one property trap and one extra promise layer, and the
|
|
27
27
|
* error path pays for everything else.
|
|
28
28
|
*/
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
* - Anything that is not the bridge's shape (an `Error` carrying a string
|
|
22
22
|
* `kind`) passes through with the same identity it arrived with.
|
|
23
23
|
*
|
|
24
|
-
* The
|
|
25
|
-
*
|
|
24
|
+
* The private `getClient()` getter returns a cached `Proxy` for each client.
|
|
25
|
+
* Its method wrappers are built on first access and cached, so
|
|
26
26
|
* the happy path pays one property trap and one extra promise layer, and the
|
|
27
27
|
* error path pays for everything else.
|
|
28
28
|
*/
|
package/lib/Socket/business.js
CHANGED
|
@@ -39,66 +39,68 @@ const catalogSubject = (method, ctx, jid) => {
|
|
|
39
39
|
}
|
|
40
40
|
return subject;
|
|
41
41
|
};
|
|
42
|
-
export const makeBusinessMethods = (ctx) =>
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
42
|
+
export const makeBusinessMethods = (ctx) => {
|
|
43
|
+
return {
|
|
44
|
+
getCatalog: async ({ jid, limit, cursor }) => {
|
|
45
|
+
const subject = catalogSubject('getCatalog', ctx, jid);
|
|
46
|
+
return await ctx.withClient(client => client.getCatalog(subject, { limit, after: cursor }));
|
|
47
|
+
},
|
|
48
|
+
getCollections: async (jid, limit) => {
|
|
49
|
+
const subject = catalogSubject('getCollections', ctx, jid);
|
|
50
|
+
return await ctx.withClient(client => client.getCollections(subject, { collectionLimit: limit }));
|
|
51
|
+
},
|
|
52
|
+
/**
|
|
53
|
+
* `sellerJid` is not in upstream's signature because upstream reads orders
|
|
54
|
+
* over the legacy `fb:thrift_iq` route, which addresses the server. The
|
|
55
|
+
* route the real client uses is a MEX query keyed by the seller, so the JID
|
|
56
|
+
* is required here. It is on the order message the token came from.
|
|
57
|
+
*/
|
|
58
|
+
getOrderDetails: async (orderId, tokenBase64, sellerJid) => {
|
|
59
|
+
if (!sellerJid) {
|
|
60
|
+
throw new Boom('getOrderDetails: a third argument with the seller jid is required, because orders are read through a query keyed by the business rather than the legacy server-addressed one', { statusCode: 400 });
|
|
61
|
+
}
|
|
62
|
+
return await ctx.withClient(client => client.getOrder(sellerJid, orderId, tokenBase64));
|
|
63
|
+
},
|
|
64
|
+
updateBussinesProfile: async (args) => {
|
|
65
|
+
const update = {
|
|
66
|
+
address: args.address,
|
|
67
|
+
description: args.description,
|
|
68
|
+
email: args.email,
|
|
69
|
+
websites: args.websites,
|
|
70
|
+
...(args.hours !== undefined
|
|
71
|
+
? {
|
|
72
|
+
businessHours: {
|
|
73
|
+
timezone: args.hours.timezone,
|
|
74
|
+
// Minutes past midnight as a number; upstream types the two
|
|
75
|
+
// as strings and the core rejects them on the other modes.
|
|
76
|
+
config: args.hours.days.map(day => ({
|
|
77
|
+
dayOfWeek: day.day,
|
|
78
|
+
mode: day.mode,
|
|
79
|
+
openTime: day.mode === 'specific_hours' ? minutesPastMidnight(day.openTimeInMinutes, 'open') : undefined,
|
|
80
|
+
closeTime: day.mode === 'specific_hours' ? minutesPastMidnight(day.closeTimeInMinutes, 'close') : undefined
|
|
81
|
+
}))
|
|
82
|
+
}
|
|
81
83
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
84
|
+
: {})
|
|
85
|
+
};
|
|
86
|
+
await ctx.withClient(client => client.updateBusinessProfile(update));
|
|
87
|
+
},
|
|
88
|
+
/**
|
|
89
|
+
* Declared but not callable end to end. The core and the bridge take the
|
|
90
|
+
* `{fbid, meta_hmac, ts}` receipt of a cover photo upload, and this
|
|
91
|
+
* package's upload path cannot produce one: it requires a url and a
|
|
92
|
+
* direct path, which that endpoint does not return.
|
|
93
|
+
*/
|
|
94
|
+
updateCoverPhoto: async (photo) => {
|
|
95
|
+
void photo;
|
|
96
|
+
throw new Boom('updateCoverPhoto is not available yet: the cover photo upload returns an {fbid, meta_hmac, ts} receipt that this package cannot obtain. removeCoverPhoto works.', { statusCode: 501 });
|
|
97
|
+
},
|
|
98
|
+
removeCoverPhoto: async (id) => {
|
|
99
|
+
await ctx.withClient(client => client.removeBusinessCoverPhoto(id));
|
|
100
|
+
},
|
|
101
|
+
productCreate: async (create) => noProductWriteRoute('productCreate', create),
|
|
102
|
+
productUpdate: async (productId, update) => noProductWriteRoute('productUpdate', productId, update),
|
|
103
|
+
productDelete: async (productIds) => noProductWriteRoute('productDelete', productIds)
|
|
104
|
+
};
|
|
105
|
+
};
|
|
104
106
|
//# sourceMappingURL=business.js.map
|
|
@@ -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
|