@alannxd/baileys 6.2.7 → 6.2.8
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.md +440 -203
- package/lib/Defaults/index.js +2 -2
- package/lib/Socket/Client/websocket.js +35 -5
- package/lib/Socket/chats.js +0 -5
- package/lib/Socket/index.js +1 -9
- package/lib/Socket/luxu.js +4 -5
- package/lib/Socket/messages-recv.js +1 -1
- package/lib/Socket/messages-send.js +56 -114
- package/lib/Socket/mex.js +38 -51
- package/lib/Socket/newsletter.js +276 -745
- package/lib/Socket/socket.js +27 -161
- package/lib/Socket/username.js +204 -206
- package/lib/Store/make-in-memory-store.js +1 -47
- package/lib/Types/Mex.js +2 -76
- package/lib/Utils/browser-utils.js +1 -0
- package/lib/Utils/generics.js +2 -2
- package/lib/Utils/index.js +0 -1
- package/lib/Utils/messages-media.js +0 -82
- package/lib/Utils/messages.js +2 -136
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +50 -52
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +52 -55
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +25 -28
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +36 -37
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +23 -25
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +51 -0
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +29 -0
- package/lib/WAUSync/Protocols/index.js +6 -12
- package/lib/WAUSync/USyncQuery.js +96 -149
- package/lib/WAUSync/USyncUser.js +29 -54
- package/lib/WAUSync/index.js +4 -3
- package/lib/index.js +5 -2
- package/package.json +14 -13
- package/lib/Socket/graphql.js +0 -716
- package/lib/Socket/interop.js +0 -463
- package/lib/Socket/message-builder.js +0 -475
- package/lib/Socket/privacy.js +0 -318
- package/lib/Utils/sticker.js +0 -133
- package/lib/Utils/use-sqlite-auth-state.js +0 -162
- package/lib/WAUSync/Protocols/USyncBotProfileProtocol.js +0 -53
- package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +0 -43
- package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +0 -58
- package/lib/WAUSync/Protocols/USyncLIDProtocol.js +0 -30
- package/lib/WAUSync/Protocols/USyncPictureProtocol.js +0 -32
- package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +0 -28
- package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +0 -38
package/lib/Socket/privacy.js
DELETED
|
@@ -1,318 +0,0 @@
|
|
|
1
|
-
import { executeWMexQuery } from './mex.js'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* MEX query IDs for privacy, status/profile, account/auth, and misc operations.
|
|
5
|
-
* Source: assets/whatsapp-android-mex_client_persist_ids.json
|
|
6
|
-
*/
|
|
7
|
-
const PRIVACY_MEX_IDS = {
|
|
8
|
-
// Privacy settings
|
|
9
|
-
GET_SETTINGS: '32774292262215380', // GetPrivacySettingsQuery
|
|
10
|
-
SET_SETTING: '26887749497493184', // SetPrivacySettingMutation
|
|
11
|
-
UPDATE_CONTACT_LIST: '26375158178762800', // UpdatePrivacyContactListMutation
|
|
12
|
-
GET_CONTACT_LIST: '25700444246275824', // GetPrivacyContactListQuery
|
|
13
|
-
// Status / profile
|
|
14
|
-
UPDATE_TEXT_STATUS: '25863197129975892', // UpdateTextStatus
|
|
15
|
-
GET_TEXT_STATUS_LIST: '25741205615468936', // GetTextStatusList
|
|
16
|
-
UPDATE_USER_STATUS: '7452341274886724', // UpdateUserStatus
|
|
17
|
-
FETCH_USER_PICTURE: '24983561624604410', // FetchUserPictureInfo
|
|
18
|
-
PROFILE_PICTURE_MUTATION: '24714239711610700', // ProfilePictureMutation
|
|
19
|
-
// Account / auth
|
|
20
|
-
ACCOUNT_LOGIN: '27298465499757130', // AccountLoginMutation
|
|
21
|
-
ACCOUNT_LOGOUT: '26863447609979190', // AccountLogoutMutation
|
|
22
|
-
MULTI_ACCOUNT_REVOKE: '25846242091639660', // MultiAccountRevokeAccount
|
|
23
|
-
ADD_MULTI_ACCOUNT_LINK: '25502812266025190', // AddMultiAccountLink
|
|
24
|
-
ADD_TRUSTED_DEVICE: '24522952587403290', // AddTrustedDeviceMutation
|
|
25
|
-
GET_TRUSTED_DEVICES: '25123358920671964', // GetTrustedDevicesQuery
|
|
26
|
-
UNTRUST_TRUSTED_DEVICE: '26574930682133620', // UntrustTrustedDeviceMutation
|
|
27
|
-
DELETE_TRUSTED_DEVICE: '33867503889559536', // DeleteTrustedDeviceMutation
|
|
28
|
-
// Misc
|
|
29
|
-
MOBILE_CONFIG_FETCH: '25676911271914596', // MobileConfigFetchQuery
|
|
30
|
-
NOTIFY_PUSH_NAME: '25900490552974544', // NotifyPushName
|
|
31
|
-
CONTACT_INTEGRITY: '25924358997169496', // ContactIntegrityQuery
|
|
32
|
-
BIZ_INTEGRITY: '25975613018777536', // BizIntegrityQuery
|
|
33
|
-
LINKED_PROFILES_SET: '25013968611531010', // LinkedProfilesSet
|
|
34
|
-
LINKED_PROFILES_REMOVE: '24537675509265524', // LinkedProfilesRemove
|
|
35
|
-
LINKED_PROFILES_UPDATE: '24876967165297616', // LinkedProfilesUpdate
|
|
36
|
-
MIGRATE_BLOCKLIST_LID: '25028600226770430', // MigrateBlocklistLid
|
|
37
|
-
QR_CODE_SCAN: '26287165600869744' // QRCodeScan
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export const makePrivacySocket = sock => {
|
|
41
|
-
const { query, generateMessageTag } = sock
|
|
42
|
-
|
|
43
|
-
const mexQuery = (variables, queryId, dataPath) =>
|
|
44
|
-
executeWMexQuery(variables, queryId, dataPath, query, generateMessageTag)
|
|
45
|
-
|
|
46
|
-
// ── Privacy Settings ────────────────────────────────────────────────────
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Fetch all privacy settings via MEX.
|
|
50
|
-
*/
|
|
51
|
-
const getPrivacySettings = (jid, features = null) => {
|
|
52
|
-
const users = [{ jid, ...(features ? { privacy_features: features } : {}) }]
|
|
53
|
-
return mexQuery({ users }, PRIVACY_MEX_IDS.GET_SETTINGS, 'xwa2_fetch_wa_users')
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Set a privacy setting via MEX.
|
|
58
|
-
* Features: "LAST_SEEN", "ONLINE", "PROFILE_PHOTO", "STATUS", "READ_RECEIPTS", "GROUPS", "CALLS", "SCREENSHOT", "LIVE_LOCATION"
|
|
59
|
-
* Settings: "ALL", "CONTACTS", "CONTACT_BLACKLIST", "NONE"
|
|
60
|
-
*/
|
|
61
|
-
const setPrivacySetting = (feature, setting) =>
|
|
62
|
-
mexQuery({ feature, setting }, PRIVACY_MEX_IDS.SET_SETTING, 'xwa2_privacy_feature_update')
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Update the contact list for a privacy setting.
|
|
66
|
-
*/
|
|
67
|
-
const updatePrivacyContactList = (feature, setting, jids) =>
|
|
68
|
-
mexQuery(
|
|
69
|
-
{ feature, setting, contacts: jids.map(jid => ({ jid })) },
|
|
70
|
-
PRIVACY_MEX_IDS.UPDATE_CONTACT_LIST,
|
|
71
|
-
'xwa2_privacy_contact_list_update'
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Fetch the contact list for a privacy setting.
|
|
76
|
-
*/
|
|
77
|
-
const getPrivacyContactList = (feature, setting) =>
|
|
78
|
-
mexQuery({ feature, setting }, PRIVACY_MEX_IDS.GET_CONTACT_LIST, 'xwa2_privacy_contact_list')
|
|
79
|
-
|
|
80
|
-
// ── Status / Profile ───────────────────────────────────────────────────
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Update own text status About via MEX.
|
|
84
|
-
*/
|
|
85
|
-
const updateTextStatus = (text, emoji = null) => {
|
|
86
|
-
const input = { text }
|
|
87
|
-
if (emoji) input.emoji = { content: emoji }
|
|
88
|
-
return mexQuery({ text_status_input: input }, PRIVACY_MEX_IDS.UPDATE_TEXT_STATUS, 'xwa2_text_status_update')
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Fetch text statuses About for a list of JIDs via MEX.
|
|
93
|
-
*/
|
|
94
|
-
const getTextStatusList = (jids, lastUpdateTime = null) => {
|
|
95
|
-
const input = jids.map(jid => ({ jid, last_update_time: lastUpdateTime }))
|
|
96
|
-
return mexQuery({ input }, PRIVACY_MEX_IDS.GET_TEXT_STATUS_LIST, 'xwa2_text_status_list')
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Update own user status string via MEX.
|
|
101
|
-
*/
|
|
102
|
-
const updateUserStatus = status =>
|
|
103
|
-
mexQuery({ status }, PRIVACY_MEX_IDS.UPDATE_USER_STATUS, 'xwa2_update_user_status')
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Fetch picture info for a user via MEX.
|
|
107
|
-
*/
|
|
108
|
-
const fetchUserPictureInfo = jid =>
|
|
109
|
-
mexQuery({ jid }, PRIVACY_MEX_IDS.FETCH_USER_PICTURE, 'xwa2_fetch_user_picture_info')
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Set own profile picture via MEX.
|
|
113
|
-
*/
|
|
114
|
-
const setProfilePictureMex = (imageBase64, type = 'image') =>
|
|
115
|
-
mexQuery(
|
|
116
|
-
{ input: { image: imageBase64, type } },
|
|
117
|
-
PRIVACY_MEX_IDS.PROFILE_PICTURE_MUTATION,
|
|
118
|
-
'xwa2_profile_picture_mutation'
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
// ── Account / Auth ─────────────────────────────────────────────────────
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Mark account as logged-in via MEX.
|
|
125
|
-
*/
|
|
126
|
-
const accountLogin = phoneNumber =>
|
|
127
|
-
mexQuery({ input: { phone_number: phoneNumber } }, PRIVACY_MEX_IDS.ACCOUNT_LOGIN, 'xwa2_account_login')
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Mark account as logged-out via MEX.
|
|
131
|
-
*/
|
|
132
|
-
const accountLogout = (phoneNumber, enabledBiometric = false) =>
|
|
133
|
-
mexQuery(
|
|
134
|
-
{ input: { phone_number: phoneNumber, enabled_biometric: enabledBiometric } },
|
|
135
|
-
PRIVACY_MEX_IDS.ACCOUNT_LOGOUT,
|
|
136
|
-
'xwa2_account_logout'
|
|
137
|
-
)
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Add a multi-account link via MEX.
|
|
141
|
-
*/
|
|
142
|
-
const addMultiAccountLink = phoneNumber =>
|
|
143
|
-
mexQuery(
|
|
144
|
-
{ input: { phone_number: phoneNumber } },
|
|
145
|
-
PRIVACY_MEX_IDS.ADD_MULTI_ACCOUNT_LINK,
|
|
146
|
-
'xwa2_add_multi_account_link'
|
|
147
|
-
)
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Add a trusted device via MEX.
|
|
151
|
-
*/
|
|
152
|
-
const addTrustedDevice = (deviceId, deviceName) =>
|
|
153
|
-
mexQuery({ device_id: deviceId, device_name: deviceName }, PRIVACY_MEX_IDS.ADD_TRUSTED_DEVICE, 'xwa2_add_trusted_device')
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Fetch list of trusted devices for the account via MEX.
|
|
157
|
-
*/
|
|
158
|
-
const getTrustedDevices = () => mexQuery({}, PRIVACY_MEX_IDS.GET_TRUSTED_DEVICES, 'xwa2_get_trusted_devices')
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Untrust a trusted device via MEX.
|
|
162
|
-
*/
|
|
163
|
-
const untrustTrustedDevice = (deviceId, reason = 'USER_INITIATED') =>
|
|
164
|
-
mexQuery(
|
|
165
|
-
{ device_id: deviceId, reason },
|
|
166
|
-
PRIVACY_MEX_IDS.UNTRUST_TRUSTED_DEVICE,
|
|
167
|
-
'xwa2_untrust_trusted_device'
|
|
168
|
-
)
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Delete a trusted device entirely via MEX.
|
|
172
|
-
*/
|
|
173
|
-
const deleteTrustedDevice = deviceId =>
|
|
174
|
-
mexQuery({ device_id: deviceId }, PRIVACY_MEX_IDS.DELETE_TRUSTED_DEVICE, 'xwa2_delete_trusted_device')
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Revoke multi-account link for a secondary account via MEX.
|
|
178
|
-
*/
|
|
179
|
-
const revokeMultiAccount = accountJid =>
|
|
180
|
-
mexQuery({ account_jid: accountJid }, PRIVACY_MEX_IDS.MULTI_ACCOUNT_REVOKE, 'xwa2_multi_account_revoke')
|
|
181
|
-
|
|
182
|
-
// ── Misc ───────────────────────────────────────────────────────────────
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Fetch mobile config via MEX.
|
|
186
|
-
*/
|
|
187
|
-
const fetchMobileConfig = (apiVersion = 0, epRefreshId = 0, flags = '') =>
|
|
188
|
-
mexQuery(
|
|
189
|
-
{ api_version: apiVersion, ep_refresh_id: epRefreshId, flags },
|
|
190
|
-
PRIVACY_MEX_IDS.MOBILE_CONFIG_FETCH,
|
|
191
|
-
'xwa2_mobile_config_fetch'
|
|
192
|
-
)
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Notify group members of your push name via MEX.
|
|
196
|
-
*/
|
|
197
|
-
const notifyPushName = (groupJid, participants) =>
|
|
198
|
-
mexQuery(
|
|
199
|
-
{
|
|
200
|
-
input: {
|
|
201
|
-
group_jid: groupJid,
|
|
202
|
-
participants: participants.map(({ jid, pushName }) => ({ jid, push_name: pushName }))
|
|
203
|
-
}
|
|
204
|
-
},
|
|
205
|
-
PRIVACY_MEX_IDS.NOTIFY_PUSH_NAME,
|
|
206
|
-
'xwa2_notify_push_name'
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Run a contact integrity check via MEX.
|
|
211
|
-
*/
|
|
212
|
-
const contactIntegrityQuery = (jids, useCase = 'START_CHAT_CONTEXT') =>
|
|
213
|
-
mexQuery(
|
|
214
|
-
{ users: jids.map(jid => ({ jid })), use_case: useCase },
|
|
215
|
-
PRIVACY_MEX_IDS.CONTACT_INTEGRITY,
|
|
216
|
-
'xwa2_fetch_wa_users'
|
|
217
|
-
)
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Run a business integrity check via MEX.
|
|
221
|
-
*/
|
|
222
|
-
const bizIntegrityQuery = jids =>
|
|
223
|
-
mexQuery(
|
|
224
|
-
{ users: jids.map(jid => ({ jid })) },
|
|
225
|
-
PRIVACY_MEX_IDS.BIZ_INTEGRITY,
|
|
226
|
-
'xwa2_fetch_wa_users'
|
|
227
|
-
)
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Set linked social profiles FB IG via MEX.
|
|
231
|
-
*/
|
|
232
|
-
const linkedProfilesSet = profiles => {
|
|
233
|
-
const mapped = profiles.map(p => {
|
|
234
|
-
const entry = { type: p.type }
|
|
235
|
-
if (p.vid) entry.vid = p.vid
|
|
236
|
-
else if (p.username) entry.username = p.username
|
|
237
|
-
return entry
|
|
238
|
-
})
|
|
239
|
-
return mexQuery({ profiles: mapped }, PRIVACY_MEX_IDS.LINKED_PROFILES_SET, 'xwa2_linked_profiles_set')
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* Remove linked social profiles via MEX.
|
|
244
|
-
*/
|
|
245
|
-
const linkedProfilesRemove = types =>
|
|
246
|
-
mexQuery(
|
|
247
|
-
{ profiles: types.map(type => ({ type })) },
|
|
248
|
-
PRIVACY_MEX_IDS.LINKED_PROFILES_REMOVE,
|
|
249
|
-
'xwa2_linked_profiles_remove'
|
|
250
|
-
)
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* Update linked social profile visibility settings via MEX.
|
|
254
|
-
*/
|
|
255
|
-
const linkedProfilesUpdate = profiles =>
|
|
256
|
-
mexQuery(
|
|
257
|
-
{ profiles: profiles.map(({ type, showOnProfile }) => ({ type, show_on_profile: showOnProfile })) },
|
|
258
|
-
PRIVACY_MEX_IDS.LINKED_PROFILES_UPDATE,
|
|
259
|
-
'xwa2_linked_profiles_update'
|
|
260
|
-
)
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Migrate blocklist to LID addressing via MEX.
|
|
264
|
-
*/
|
|
265
|
-
const migrateBlocklistLid = (jids, dhash = '', dirtyAck = true) =>
|
|
266
|
-
mexQuery(
|
|
267
|
-
{
|
|
268
|
-
input: {
|
|
269
|
-
blocklist: jids.map(jid => ({ jid })),
|
|
270
|
-
dhash,
|
|
271
|
-
dirty_ack: dirtyAck
|
|
272
|
-
}
|
|
273
|
-
},
|
|
274
|
-
PRIVACY_MEX_IDS.MIGRATE_BLOCKLIST_LID,
|
|
275
|
-
'xwa2_migrate_blocklist_lid'
|
|
276
|
-
)
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Scan a QR code via MEX.
|
|
280
|
-
*/
|
|
281
|
-
const qrCodeScan = qrData =>
|
|
282
|
-
mexQuery({ qr_data: qrData }, PRIVACY_MEX_IDS.QR_CODE_SCAN, 'xwa2_qr_code_scan')
|
|
283
|
-
|
|
284
|
-
return {
|
|
285
|
-
...sock,
|
|
286
|
-
// Privacy
|
|
287
|
-
getPrivacySettings,
|
|
288
|
-
setPrivacySetting,
|
|
289
|
-
updatePrivacyContactList,
|
|
290
|
-
getPrivacyContactList,
|
|
291
|
-
// Status / profile
|
|
292
|
-
updateTextStatus,
|
|
293
|
-
getTextStatusList,
|
|
294
|
-
updateUserStatus,
|
|
295
|
-
fetchUserPictureInfo,
|
|
296
|
-
setProfilePictureMex,
|
|
297
|
-
// Account / auth
|
|
298
|
-
accountLogin,
|
|
299
|
-
accountLogout,
|
|
300
|
-
addMultiAccountLink,
|
|
301
|
-
addTrustedDevice,
|
|
302
|
-
getTrustedDevices,
|
|
303
|
-
untrustTrustedDevice,
|
|
304
|
-
deleteTrustedDevice,
|
|
305
|
-
revokeMultiAccount,
|
|
306
|
-
// Misc
|
|
307
|
-
fetchMobileConfig,
|
|
308
|
-
notifyPushName,
|
|
309
|
-
contactIntegrityQuery,
|
|
310
|
-
bizIntegrityQuery,
|
|
311
|
-
linkedProfilesSet,
|
|
312
|
-
linkedProfilesRemove,
|
|
313
|
-
linkedProfilesUpdate,
|
|
314
|
-
migrateBlocklistLid,
|
|
315
|
-
qrCodeScan,
|
|
316
|
-
PRIVACY_MEX_IDS
|
|
317
|
-
}
|
|
318
|
-
}
|
package/lib/Utils/sticker.js
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import { Boom } from '@hapi/boom';
|
|
2
|
-
import { spawn } from 'child_process';
|
|
3
|
-
import { randomUUID } from 'crypto';
|
|
4
|
-
|
|
5
|
-
const runFfmpeg = (input, args) => new Promise((resolve, reject) => {
|
|
6
|
-
const proc = spawn('ffmpeg', ['-hide_banner', '-loglevel', 'error', '-i', 'pipe:0', ...args, 'pipe:1'], { stdio: ['pipe', 'pipe', 'pipe'] });
|
|
7
|
-
const outChunks = [];
|
|
8
|
-
const errChunks = [];
|
|
9
|
-
proc.stdout.on('data', chunk => outChunks.push(chunk));
|
|
10
|
-
proc.stderr.on('data', chunk => errChunks.push(chunk));
|
|
11
|
-
proc.on('error', err => reject(new Boom(`ffmpeg failed to start: ${err.message}`, { statusCode: 500 })));
|
|
12
|
-
proc.on('close', code => {
|
|
13
|
-
if (code !== 0) {
|
|
14
|
-
reject(new Boom(`ffmpeg exited with code ${code}: ${Buffer.concat(errChunks).toString('utf8').slice(0, 500)}`, { statusCode: 500 }));
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
resolve(Buffer.concat(outChunks));
|
|
18
|
-
});
|
|
19
|
-
proc.stdin.on('error', () => { });
|
|
20
|
-
proc.stdin.end(input);
|
|
21
|
-
});
|
|
22
|
-
export const isLikelyVideoBuffer = (buffer) => {
|
|
23
|
-
if (!Buffer.isBuffer(buffer) || buffer.length < 4) {
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
const header = buffer.subarray(0, 16).toString('ascii');
|
|
27
|
-
if (header.startsWith('RIFF') && /AVI|WEBM/.test(header)) {
|
|
28
|
-
return true;
|
|
29
|
-
}
|
|
30
|
-
if (buffer.subarray(0, 4).toString('hex') === '1a45dfa3') {
|
|
31
|
-
return true;
|
|
32
|
-
}
|
|
33
|
-
const ftyp = buffer.toString('ascii', 4, 12);
|
|
34
|
-
if (buffer.toString('ascii', 0, 4) === 'ftyp' || /^(ftyp)/.test(buffer.toString('ascii', 0, 8))) {
|
|
35
|
-
return /mp4|isom|M4V|M4A|qt\s/.test(ftyp);
|
|
36
|
-
}
|
|
37
|
-
return false;
|
|
38
|
-
};
|
|
39
|
-
export const convertToWebp = async (buffer, { animated = false, quality = 60 } = {}) => {
|
|
40
|
-
const scalePad = "scale='min(512,iw)':'min(512,ih)':force_original_aspect_ratio=decrease,format=rgba,pad=512:512:-1:-1:color=#00000000";
|
|
41
|
-
const args = animated
|
|
42
|
-
? ['-vf', `${scalePad},fps=15`, '-loop', '0', '-preset', 'default', '-an', '-vsync', '0', '-vcodec', 'libwebp', '-q:v', String(quality), '-f', 'webp']
|
|
43
|
-
: ['-vf', `${scalePad}`, '-vframes', '1', '-vcodec', 'libwebp', '-q:v', String(quality), '-f', 'webp'];
|
|
44
|
-
return runFfmpeg(buffer, args);
|
|
45
|
-
};
|
|
46
|
-
export const convertToTrayIcon = async (buffer) => {
|
|
47
|
-
const scalePad = "scale='min(96,iw)':'min(96,ih)':force_original_aspect_ratio=decrease,format=rgba,pad=96:96:-1:-1:color=#00000000";
|
|
48
|
-
const args = ['-vf', scalePad, '-vframes', '1', '-vcodec', 'png', '-f', 'image2'];
|
|
49
|
-
return runFfmpeg(buffer, args);
|
|
50
|
-
};
|
|
51
|
-
const readChunks = (buf) => {
|
|
52
|
-
const chunks = [];
|
|
53
|
-
let offset = 12;
|
|
54
|
-
while (offset + 8 <= buf.length) {
|
|
55
|
-
const fourCC = buf.toString('ascii', offset, offset + 4);
|
|
56
|
-
const size = buf.readUInt32LE(offset + 4);
|
|
57
|
-
const dataStart = offset + 8;
|
|
58
|
-
const data = buf.subarray(dataStart, dataStart + size);
|
|
59
|
-
chunks.push({ fourCC, data });
|
|
60
|
-
offset = dataStart + size + (size % 2);
|
|
61
|
-
}
|
|
62
|
-
return chunks;
|
|
63
|
-
};
|
|
64
|
-
const buildChunk = (fourCC, data) => {
|
|
65
|
-
const header = Buffer.alloc(8);
|
|
66
|
-
header.write(fourCC, 0, 4, 'ascii');
|
|
67
|
-
header.writeUInt32LE(data.length, 4);
|
|
68
|
-
const pad = data.length % 2 === 1 ? Buffer.from([0]) : Buffer.alloc(0);
|
|
69
|
-
return Buffer.concat([header, data, pad]);
|
|
70
|
-
};
|
|
71
|
-
const buildExifChunkData = (json) => {
|
|
72
|
-
const header = Buffer.from([0x49, 0x49, 0x2a, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x41, 0x57, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00]);
|
|
73
|
-
const payload = Buffer.from(JSON.stringify(json), 'utf8');
|
|
74
|
-
header.writeUInt32LE(payload.length, 14);
|
|
75
|
-
return Buffer.concat([header, payload]);
|
|
76
|
-
};
|
|
77
|
-
export const writeExifToWebp = (webpBuffer, { packId, packName = '', publisher = '', emojis = [], isAvatar = false } = {}) => {
|
|
78
|
-
if (webpBuffer.toString('ascii', 0, 4) !== 'RIFF' || webpBuffer.toString('ascii', 8, 12) !== 'WEBP') {
|
|
79
|
-
throw new Boom('writeExifToWebp: not a valid webp buffer', { statusCode: 400 });
|
|
80
|
-
}
|
|
81
|
-
const chunks = readChunks(webpBuffer);
|
|
82
|
-
const exifData = buildExifChunkData({
|
|
83
|
-
'sticker-pack-id': packId || randomUUID(),
|
|
84
|
-
'sticker-pack-name': packName,
|
|
85
|
-
'sticker-pack-publisher': publisher,
|
|
86
|
-
emojis: emojis.length ? emojis : ['🤖'],
|
|
87
|
-
'is-avatar-sticker': isAvatar ? 1 : 0
|
|
88
|
-
});
|
|
89
|
-
let vp8x = chunks.find(c => c.fourCC === 'VP8X');
|
|
90
|
-
const otherChunks = chunks.filter(c => c.fourCC !== 'VP8X' && c.fourCC !== 'EXIF');
|
|
91
|
-
if (!vp8x) {
|
|
92
|
-
const img = otherChunks.find(c => c.fourCC === 'VP8 ' || c.fourCC === 'VP8L');
|
|
93
|
-
if (!img) {
|
|
94
|
-
throw new Boom('writeExifToWebp: no VP8/VP8L image data found', { statusCode: 400 });
|
|
95
|
-
}
|
|
96
|
-
let width;
|
|
97
|
-
let height;
|
|
98
|
-
if (img.fourCC === 'VP8 ') {
|
|
99
|
-
width = (img.data.readUInt16LE(6) & 0x3fff);
|
|
100
|
-
height = (img.data.readUInt16LE(8) & 0x3fff);
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
const b = img.data;
|
|
104
|
-
width = 1 + (((b[2] & 0x3f) << 8) | b[1]);
|
|
105
|
-
height = 1 + (((b[4] & 0xf) << 10) | (b[3] << 2) | ((b[2] & 0xc0) >> 6));
|
|
106
|
-
}
|
|
107
|
-
const flags = Buffer.alloc(4);
|
|
108
|
-
flags[0] = 0x08;
|
|
109
|
-
const dims = Buffer.alloc(6);
|
|
110
|
-
dims.writeUIntLE(width - 1, 0, 3);
|
|
111
|
-
dims.writeUIntLE(height - 1, 3, 3);
|
|
112
|
-
vp8x = { fourCC: 'VP8X', data: Buffer.concat([flags, dims]) };
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
const flagsByte = vp8x.data[0] | 0x08;
|
|
116
|
-
vp8x = { fourCC: 'VP8X', data: Buffer.concat([Buffer.from([flagsByte]), vp8x.data.subarray(1)]) };
|
|
117
|
-
}
|
|
118
|
-
const rebuilt = [vp8x, ...otherChunks, { fourCC: 'EXIF', data: exifData }];
|
|
119
|
-
const chunkBuffers = rebuilt.map(c => buildChunk(c.fourCC, c.data));
|
|
120
|
-
const payload = Buffer.concat(chunkBuffers);
|
|
121
|
-
const riffSize = 4 + payload.length;
|
|
122
|
-
const out = Buffer.alloc(8 + 4 + payload.length);
|
|
123
|
-
out.write('RIFF', 0, 4, 'ascii');
|
|
124
|
-
out.writeUInt32LE(riffSize, 4);
|
|
125
|
-
out.write('WEBP', 8, 4, 'ascii');
|
|
126
|
-
payload.copy(out, 12);
|
|
127
|
-
return out;
|
|
128
|
-
};
|
|
129
|
-
export const makeSticker = async (buffer, { pack = '', author = '', emojis = [], isPrivate = false, animated = false, quality = 60 } = {}) => {
|
|
130
|
-
const shouldAnimate = animated || isLikelyVideoBuffer(buffer);
|
|
131
|
-
const webp = await convertToWebp(buffer, { animated: shouldAnimate, quality });
|
|
132
|
-
return writeExifToWebp(webp, { packName: pack, publisher: author, emojis, isAvatar: isPrivate });
|
|
133
|
-
};
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import { mkdir, readdir, readFile } from 'fs/promises';
|
|
2
|
-
import { dirname, join } from 'path';
|
|
3
|
-
import { proto } from '../../WAProto/index.js';
|
|
4
|
-
import { initAuthCreds } from './auth-utils.js';
|
|
5
|
-
export const useSqliteAuthState = async (pathOrFolder, options = {}) => {
|
|
6
|
-
const {
|
|
7
|
-
fileName = 'auth.db',
|
|
8
|
-
migrateFromFolder,
|
|
9
|
-
logger
|
|
10
|
-
} = options;
|
|
11
|
-
const dbPath = /\.(db|sqlite|sqlite3)$/i.test(pathOrFolder) ? pathOrFolder : join(pathOrFolder, fileName);
|
|
12
|
-
const bufReplacer = (_, value) => {
|
|
13
|
-
if (Buffer.isBuffer(value) || value instanceof Uint8Array || value?.type === 'Buffer') {
|
|
14
|
-
return {
|
|
15
|
-
type: 'Buffer',
|
|
16
|
-
data: Buffer.from(value?.data || value).toString('base64')
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
return value;
|
|
20
|
-
};
|
|
21
|
-
const bufReviver = (_, value) => {
|
|
22
|
-
if (value && typeof value === 'object' && value.type === 'Buffer' && typeof value.data === 'string') {
|
|
23
|
-
return Buffer.from(value.data, 'base64');
|
|
24
|
-
}
|
|
25
|
-
return value;
|
|
26
|
-
};
|
|
27
|
-
const encode = value => {
|
|
28
|
-
if (Buffer.isBuffer(value) || value instanceof Uint8Array) {
|
|
29
|
-
return Buffer.concat([Buffer.from([1]), Buffer.from(value)]);
|
|
30
|
-
}
|
|
31
|
-
return Buffer.concat([Buffer.from([0]), Buffer.from(JSON.stringify(value, bufReplacer), 'utf8')]);
|
|
32
|
-
};
|
|
33
|
-
const decode = blob => {
|
|
34
|
-
if (!blob || blob.length === 0) return null;
|
|
35
|
-
if (blob[0] === 1) return Buffer.from(blob.subarray(1));
|
|
36
|
-
return JSON.parse(Buffer.from(blob.subarray(1)).toString('utf8'), bufReviver);
|
|
37
|
-
};
|
|
38
|
-
const fixName = s => s?.replace(/\//g, '__')?.replace(/:/g, '-');
|
|
39
|
-
const keyOf = (category, id) => fixName(`${category}-${id}`);
|
|
40
|
-
let DatabaseSync;
|
|
41
|
-
try {
|
|
42
|
-
({
|
|
43
|
-
DatabaseSync
|
|
44
|
-
} = await import('node:sqlite'));
|
|
45
|
-
} catch (err) {
|
|
46
|
-
throw new Error("useSqliteAuthState needs the built-in 'node:sqlite' module (Node 22.5+). Upgrade Node, or use useMultiFileAuthState instead.");
|
|
47
|
-
}
|
|
48
|
-
await mkdir(dirname(dbPath), {
|
|
49
|
-
recursive: true
|
|
50
|
-
}).catch(() => {});
|
|
51
|
-
const db = new DatabaseSync(dbPath);
|
|
52
|
-
db.exec('PRAGMA journal_mode = TRUNCATE');
|
|
53
|
-
db.exec('PRAGMA synchronous = NORMAL');
|
|
54
|
-
db.exec('PRAGMA busy_timeout = 5000');
|
|
55
|
-
db.exec('CREATE TABLE IF NOT EXISTS auth_state (k TEXT PRIMARY KEY, v BLOB NOT NULL) WITHOUT ROWID');
|
|
56
|
-
const qGet = db.prepare('SELECT v FROM auth_state WHERE k = ?');
|
|
57
|
-
const qUpsert = db.prepare('INSERT INTO auth_state(k,v) VALUES(?,?) ON CONFLICT(k) DO UPDATE SET v=excluded.v');
|
|
58
|
-
const qDelete = db.prepare('DELETE FROM auth_state WHERE k = ?');
|
|
59
|
-
const runTx = fn => {
|
|
60
|
-
db.exec('BEGIN');
|
|
61
|
-
try {
|
|
62
|
-
const r = fn();
|
|
63
|
-
db.exec('COMMIT');
|
|
64
|
-
return r;
|
|
65
|
-
} catch (err) {
|
|
66
|
-
db.exec('ROLLBACK');
|
|
67
|
-
throw err;
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
const readRaw = k => {
|
|
71
|
-
const row = qGet.get(k);
|
|
72
|
-
if (!row) return null;
|
|
73
|
-
try {
|
|
74
|
-
return decode(row.v);
|
|
75
|
-
} catch (err) {
|
|
76
|
-
logger?.warn?.({
|
|
77
|
-
k,
|
|
78
|
-
err: err?.message
|
|
79
|
-
}, 'sqlite-auth: failed to decode row, treating as missing');
|
|
80
|
-
return null;
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
const runMigration = async folder => {
|
|
84
|
-
let files;
|
|
85
|
-
try {
|
|
86
|
-
files = await readdir(folder);
|
|
87
|
-
} catch {
|
|
88
|
-
return 0;
|
|
89
|
-
}
|
|
90
|
-
const rows = [];
|
|
91
|
-
for (const file of files) {
|
|
92
|
-
if (!file.endsWith('.json')) continue;
|
|
93
|
-
let value;
|
|
94
|
-
try {
|
|
95
|
-
value = JSON.parse(await readFile(join(folder, file), 'utf8'), bufReviver);
|
|
96
|
-
} catch {
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
if (value === null || value === undefined) continue;
|
|
100
|
-
rows.push([file.slice(0, -'.json'.length), encode(value)]);
|
|
101
|
-
}
|
|
102
|
-
runTx(() => {
|
|
103
|
-
for (const [k, v] of rows) qUpsert.run(k, v);
|
|
104
|
-
});
|
|
105
|
-
return rows.length;
|
|
106
|
-
};
|
|
107
|
-
const hasCreds = () => !!qGet.get('creds');
|
|
108
|
-
if (migrateFromFolder && !hasCreds()) {
|
|
109
|
-
const n = await runMigration(migrateFromFolder);
|
|
110
|
-
if (n > 0) logger?.info?.({
|
|
111
|
-
count: n,
|
|
112
|
-
from: migrateFromFolder
|
|
113
|
-
}, 'sqlite-auth: migrated legacy auth state');
|
|
114
|
-
}
|
|
115
|
-
let creds = readRaw('creds');
|
|
116
|
-
if (!creds) {
|
|
117
|
-
creds = initAuthCreds();
|
|
118
|
-
qUpsert.run('creds', encode(creds));
|
|
119
|
-
}
|
|
120
|
-
const keys = {
|
|
121
|
-
get: async (type, ids) => {
|
|
122
|
-
const data = {};
|
|
123
|
-
for (const id of ids) {
|
|
124
|
-
let value = readRaw(keyOf(type, id));
|
|
125
|
-
if (type === 'app-state-sync-key' && value) {
|
|
126
|
-
value = proto.Message.AppStateSyncKeyData.fromObject(value);
|
|
127
|
-
}
|
|
128
|
-
if (value !== null && value !== undefined) {
|
|
129
|
-
data[id] = value;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
return data;
|
|
133
|
-
},
|
|
134
|
-
set: async data => {
|
|
135
|
-
const ops = [];
|
|
136
|
-
for (const category in data) {
|
|
137
|
-
for (const id in data[category]) {
|
|
138
|
-
ops.push([keyOf(category, id), data[category][id]]);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
runTx(() => {
|
|
142
|
-
for (const [k, value] of ops) {
|
|
143
|
-
if (value === null || value === undefined) qDelete.run(k);else qUpsert.run(k, encode(value));
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
},
|
|
147
|
-
clear: async () => {
|
|
148
|
-
db.prepare("DELETE FROM auth_state WHERE k <> 'creds'").run();
|
|
149
|
-
}
|
|
150
|
-
};
|
|
151
|
-
return {
|
|
152
|
-
state: {
|
|
153
|
-
creds,
|
|
154
|
-
keys
|
|
155
|
-
},
|
|
156
|
-
saveCreds: async () => {
|
|
157
|
-
qUpsert.run('creds', encode(creds));
|
|
158
|
-
},
|
|
159
|
-
db,
|
|
160
|
-
close: () => db.close()
|
|
161
|
-
};
|
|
162
|
-
};
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { getBinaryNodeChild, getBinaryNodeChildren, getBinaryNodeChildString } from '../../WABinary/index.js'
|
|
2
|
-
|
|
3
|
-
export class USyncBotProfileProtocol {
|
|
4
|
-
constructor() {
|
|
5
|
-
this.name = 'bot'
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
getQueryElement() {
|
|
9
|
-
return {
|
|
10
|
-
tag: 'bot',
|
|
11
|
-
attrs: {},
|
|
12
|
-
content: [{ tag: 'profile', attrs: { v: '1' } }]
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
getUserElement(user) {
|
|
17
|
-
return {
|
|
18
|
-
tag: 'bot',
|
|
19
|
-
attrs: {},
|
|
20
|
-
content: [{ tag: 'profile', attrs: { persona_id: user.personaId } }]
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
parser(node) {
|
|
25
|
-
const botNode = getBinaryNodeChild(node, 'bot')
|
|
26
|
-
const profile = getBinaryNodeChild(botNode, 'profile')
|
|
27
|
-
const commandsNode = getBinaryNodeChild(profile, 'commands')
|
|
28
|
-
const promptsNode = getBinaryNodeChild(profile, 'prompts')
|
|
29
|
-
const commands = []
|
|
30
|
-
const prompts = []
|
|
31
|
-
for (const command of getBinaryNodeChildren(commandsNode, 'command')) {
|
|
32
|
-
commands.push({
|
|
33
|
-
name: getBinaryNodeChildString(command, 'name'),
|
|
34
|
-
description: getBinaryNodeChildString(command, 'description')
|
|
35
|
-
})
|
|
36
|
-
}
|
|
37
|
-
for (const prompt of getBinaryNodeChildren(promptsNode, 'prompt')) {
|
|
38
|
-
prompts.push(`${getBinaryNodeChildString(prompt, 'emoji')} ${getBinaryNodeChildString(prompt, 'text')}`)
|
|
39
|
-
}
|
|
40
|
-
return {
|
|
41
|
-
isDefault: !!getBinaryNodeChild(profile, 'default'),
|
|
42
|
-
jid: node.attrs.jid,
|
|
43
|
-
name: getBinaryNodeChildString(profile, 'name'),
|
|
44
|
-
attributes: getBinaryNodeChildString(profile, 'attributes'),
|
|
45
|
-
description: getBinaryNodeChildString(profile, 'description'),
|
|
46
|
-
category: getBinaryNodeChildString(profile, 'category'),
|
|
47
|
-
personaId: profile.attrs['persona_id'],
|
|
48
|
-
commandsDescription: getBinaryNodeChildString(commandsNode, 'description'),
|
|
49
|
-
commands,
|
|
50
|
-
prompts
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|