whatsapp-web.js 1.16.7 → 1.18.0-alpha.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/README.md +2 -1
- package/example.js +7 -1
- package/index.d.ts +73 -8
- package/index.js +1 -0
- package/package.json +6 -1
- package/src/Client.js +100 -7
- package/src/authStrategies/BaseAuthStrategy.js +3 -0
- package/src/authStrategies/RemoteAuth.js +204 -0
- package/src/structures/Chat.js +12 -3
- package/src/structures/GroupChat.js +1 -1
- package/src/structures/Message.js +21 -6
- package/src/structures/MessageMedia.js +13 -5
- package/src/structures/Reaction.js +69 -0
- package/src/structures/index.js +2 -1
- package/src/util/Constants.js +5 -2
- package/src/util/Injected.js +16 -1
- package/src/util/Util.js +0 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
[](https://www.npmjs.com/package/whatsapp-web.js) [](https://depfu.com/github/pedroslopez/whatsapp-web.js?project_id=9765) ](https://www.npmjs.com/package/whatsapp-web.js) [](https://depfu.com/github/pedroslopez/whatsapp-web.js?project_id=9765)  [](https://discord.gg/H7DqQs4)
|
|
2
2
|
|
|
3
3
|
# whatsapp-web.js
|
|
4
4
|
A WhatsApp API client that connects through the WhatsApp Web browser app
|
|
@@ -80,6 +80,7 @@ For more information on saving and restoring sessions, check out the available [
|
|
|
80
80
|
| Get contact info | ✅ |
|
|
81
81
|
| Get profile pictures | ✅ |
|
|
82
82
|
| Set user status message | ✅ |
|
|
83
|
+
| React to messages | ✅ |
|
|
83
84
|
|
|
84
85
|
Something missing? Make an issue and let us know!
|
|
85
86
|
|
package/example.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { Client, Location, List, Buttons, LocalAuth
|
|
1
|
+
const { Client, Location, List, Buttons, LocalAuth} = require('./index');
|
|
2
2
|
|
|
3
3
|
const client = new Client({
|
|
4
4
|
authStrategy: new LocalAuth(),
|
|
@@ -7,6 +7,10 @@ const client = new Client({
|
|
|
7
7
|
|
|
8
8
|
client.initialize();
|
|
9
9
|
|
|
10
|
+
client.on('loading_screen', (percent, message) => {
|
|
11
|
+
console.log('LOADING SCREEN', percent, message);
|
|
12
|
+
});
|
|
13
|
+
|
|
10
14
|
client.on('qr', (qr) => {
|
|
11
15
|
// NOTE: This event will not be fired if a session is specified.
|
|
12
16
|
console.log('QR RECEIVED', qr);
|
|
@@ -191,6 +195,8 @@ client.on('message', async msg => {
|
|
|
191
195
|
let sections = [{title:'sectionTitle',rows:[{title:'ListItem1', description: 'desc'},{title:'ListItem2'}]}];
|
|
192
196
|
let list = new List('List body','btnText',sections,'Title','footer');
|
|
193
197
|
client.sendMessage(msg.from, list);
|
|
198
|
+
} else if (msg.body === '!reaction') {
|
|
199
|
+
msg.react('👍');
|
|
194
200
|
}
|
|
195
201
|
});
|
|
196
202
|
|
package/index.d.ts
CHANGED
|
@@ -114,7 +114,7 @@ declare namespace WAWebJS {
|
|
|
114
114
|
|
|
115
115
|
/** Send a message to a specific chatId */
|
|
116
116
|
sendMessage(chatId: string, content: MessageContent, options?: MessageSendOptions): Promise<Message>
|
|
117
|
-
|
|
117
|
+
|
|
118
118
|
/** Searches for messages */
|
|
119
119
|
searchMessages(query: string, options?: { chatId?: string, page?: number, limit?: number }): Promise<Message[]>
|
|
120
120
|
|
|
@@ -141,7 +141,7 @@ declare namespace WAWebJS {
|
|
|
141
141
|
* @param displayName New display name
|
|
142
142
|
*/
|
|
143
143
|
setDisplayName(displayName: string): Promise<boolean>
|
|
144
|
-
|
|
144
|
+
|
|
145
145
|
/** Changes and returns the archive state of the Chat */
|
|
146
146
|
unarchiveChat(chatId: string): Promise<boolean>
|
|
147
147
|
|
|
@@ -241,6 +241,15 @@ declare namespace WAWebJS {
|
|
|
241
241
|
message: Message
|
|
242
242
|
) => void): this
|
|
243
243
|
|
|
244
|
+
/** Emitted when a reaction is sent, received, updated or removed */
|
|
245
|
+
on(event: 'message_reaction', listener: (
|
|
246
|
+
/** The reaction object */
|
|
247
|
+
reaction: Reaction
|
|
248
|
+
) => void): this
|
|
249
|
+
|
|
250
|
+
/** Emitted when loading screen is appearing */
|
|
251
|
+
on(event: 'loading_screen', listener: (percent: string, message: string) => void): this
|
|
252
|
+
|
|
244
253
|
/** Emitted when the QR code is received */
|
|
245
254
|
on(event: 'qr', listener: (
|
|
246
255
|
/** qr code string
|
|
@@ -256,6 +265,9 @@ declare namespace WAWebJS {
|
|
|
256
265
|
|
|
257
266
|
/** Emitted when the client has initialized and is ready to receive messages */
|
|
258
267
|
on(event: 'ready', listener: () => void): this
|
|
268
|
+
|
|
269
|
+
/** Emitted when the RemoteAuth session is saved successfully on the external Database */
|
|
270
|
+
on(event: 'remote_session_saved', listener: () => void): this
|
|
259
271
|
}
|
|
260
272
|
|
|
261
273
|
/** Current connection information */
|
|
@@ -345,6 +357,9 @@ declare namespace WAWebJS {
|
|
|
345
357
|
failureEventPayload?: any
|
|
346
358
|
}>;
|
|
347
359
|
getAuthEventPayload: () => Promise<any>;
|
|
360
|
+
afterAuthReady: () => Promise<void>;
|
|
361
|
+
disconnect: () => Promise<void>;
|
|
362
|
+
destroy: () => Promise<void>;
|
|
348
363
|
logout: () => Promise<void>;
|
|
349
364
|
}
|
|
350
365
|
|
|
@@ -365,6 +380,30 @@ declare namespace WAWebJS {
|
|
|
365
380
|
dataPath?: string
|
|
366
381
|
})
|
|
367
382
|
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Remote-based authentication
|
|
386
|
+
*/
|
|
387
|
+
export class RemoteAuth extends AuthStrategy {
|
|
388
|
+
public clientId?: string;
|
|
389
|
+
public dataPath?: string;
|
|
390
|
+
constructor(options?: {
|
|
391
|
+
store: Store,
|
|
392
|
+
clientId?: string,
|
|
393
|
+
dataPath?: string,
|
|
394
|
+
backupSyncIntervalMs: number
|
|
395
|
+
})
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Remote store interface
|
|
400
|
+
*/
|
|
401
|
+
export interface Store {
|
|
402
|
+
sessionExists: ({session: string}) => Promise<boolean> | boolean,
|
|
403
|
+
delete: ({session: string}) => Promise<any> | any,
|
|
404
|
+
save: ({session: string}) => Promise<any> | any,
|
|
405
|
+
extract: ({session: string, path: string}) => Promise<any> | any,
|
|
406
|
+
}
|
|
368
407
|
|
|
369
408
|
/**
|
|
370
409
|
* Legacy session auth strategy
|
|
@@ -463,9 +502,11 @@ declare namespace WAWebJS {
|
|
|
463
502
|
GROUP_LEAVE = 'group_leave',
|
|
464
503
|
GROUP_UPDATE = 'group_update',
|
|
465
504
|
QR_RECEIVED = 'qr',
|
|
505
|
+
LOADING_SCREEN = 'loading_screen',
|
|
466
506
|
DISCONNECTED = 'disconnected',
|
|
467
507
|
STATE_CHANGED = 'change_state',
|
|
468
508
|
BATTERY_CHANGED = 'change_battery',
|
|
509
|
+
REMOTE_SESSION_SAVED = 'remote_session_saved'
|
|
469
510
|
}
|
|
470
511
|
|
|
471
512
|
/** Group notification types */
|
|
@@ -604,6 +645,8 @@ declare namespace WAWebJS {
|
|
|
604
645
|
ack: MessageAck,
|
|
605
646
|
/** If the message was sent to a group, this field will contain the user that sent the message. */
|
|
606
647
|
author?: string,
|
|
648
|
+
/** String that represents from which device type the message was sent */
|
|
649
|
+
deviceType: string,
|
|
607
650
|
/** Message content */
|
|
608
651
|
body: string,
|
|
609
652
|
/** Indicates if the message was a broadcast */
|
|
@@ -687,7 +730,7 @@ declare namespace WAWebJS {
|
|
|
687
730
|
acceptGroupV4Invite: () => Promise<{status: number}>,
|
|
688
731
|
/** Deletes the message from the chat */
|
|
689
732
|
delete: (everyone?: boolean) => Promise<void>,
|
|
690
|
-
/** Downloads and returns the
|
|
733
|
+
/** Downloads and returns the attached message media */
|
|
691
734
|
downloadMedia: () => Promise<MessageMedia>,
|
|
692
735
|
/** Returns the Chat this message was sent in */
|
|
693
736
|
getChat: () => Promise<Chat>,
|
|
@@ -703,15 +746,17 @@ declare namespace WAWebJS {
|
|
|
703
746
|
* If not, it will send the message in the same Chat as the original message was sent.
|
|
704
747
|
*/
|
|
705
748
|
reply: (content: MessageContent, chatId?: string, options?: MessageSendOptions) => Promise<Message>,
|
|
749
|
+
/** React to this message with an emoji*/
|
|
750
|
+
react: (reaction: string) => Promise<void>,
|
|
706
751
|
/**
|
|
707
|
-
* Forwards this message to another chat
|
|
752
|
+
* Forwards this message to another chat (that you chatted before, otherwise it will fail)
|
|
708
753
|
*/
|
|
709
754
|
forward: (chat: Chat | string) => Promise<void>,
|
|
710
755
|
/** Star this message */
|
|
711
756
|
star: () => Promise<void>,
|
|
712
757
|
/** Unstar this message */
|
|
713
758
|
unstar: () => Promise<void>,
|
|
714
|
-
/** Get information about message delivery
|
|
759
|
+
/** Get information about message delivery status */
|
|
715
760
|
getInfo: () => Promise<MessageInfo | null>,
|
|
716
761
|
/**
|
|
717
762
|
* Gets the order associated with a given message
|
|
@@ -801,13 +846,16 @@ declare namespace WAWebJS {
|
|
|
801
846
|
data: string
|
|
802
847
|
/** Document file name. Value can be null */
|
|
803
848
|
filename?: string | null
|
|
849
|
+
/** Document file size in bytes. Value can be null. */
|
|
850
|
+
filesize?: number | null
|
|
804
851
|
|
|
805
852
|
/**
|
|
806
853
|
* @param {string} mimetype MIME type of the attachment
|
|
807
854
|
* @param {string} data Base64-encoded data of the file
|
|
808
855
|
* @param {?string} filename Document file name. Value can be null
|
|
856
|
+
* @param {?number} filesize Document file size in bytes. Value can be null.
|
|
809
857
|
*/
|
|
810
|
-
constructor(mimetype: string, data: string, filename?: string | null)
|
|
858
|
+
constructor(mimetype: string, data: string, filename?: string | null, filesize?: number | null)
|
|
811
859
|
|
|
812
860
|
/** Creates a MessageMedia instance from a local file path */
|
|
813
861
|
static fromFilePath: (filePath: string) => MessageMedia
|
|
@@ -816,7 +864,7 @@ declare namespace WAWebJS {
|
|
|
816
864
|
static fromUrl: (url: string, options?: MediaFromURLOptions) => Promise<MessageMedia>
|
|
817
865
|
}
|
|
818
866
|
|
|
819
|
-
export type MessageContent = string | MessageMedia | Location | Contact | Contact[] | List | Buttons
|
|
867
|
+
export type MessageContent = string | MessageMedia | Location | Contact | Contact[] | List | Buttons
|
|
820
868
|
|
|
821
869
|
/**
|
|
822
870
|
* Represents a Contact on WhatsApp
|
|
@@ -1014,6 +1062,10 @@ declare namespace WAWebJS {
|
|
|
1014
1062
|
* Set this to Infinity to load all messages.
|
|
1015
1063
|
*/
|
|
1016
1064
|
limit?: number
|
|
1065
|
+
/**
|
|
1066
|
+
* Return only messages from the bot number or vise versa. To get all messages, leave the option undefined.
|
|
1067
|
+
*/
|
|
1068
|
+
fromMe?: boolean
|
|
1017
1069
|
}
|
|
1018
1070
|
|
|
1019
1071
|
/**
|
|
@@ -1287,7 +1339,7 @@ declare namespace WAWebJS {
|
|
|
1287
1339
|
constructor(body: string, buttonText: string, sections: Array<any>, title?: string | null, footer?: string | null)
|
|
1288
1340
|
}
|
|
1289
1341
|
|
|
1290
|
-
/** Message type
|
|
1342
|
+
/** Message type Buttons */
|
|
1291
1343
|
export class Buttons {
|
|
1292
1344
|
body: string | MessageMedia
|
|
1293
1345
|
buttons: Array<{ buttonId: string; buttonText: {displayText: string}; type: number }>
|
|
@@ -1296,6 +1348,19 @@ declare namespace WAWebJS {
|
|
|
1296
1348
|
|
|
1297
1349
|
constructor(body: string, buttons: Array<{ id?: string; body: string }>, title?: string | null, footer?: string | null)
|
|
1298
1350
|
}
|
|
1351
|
+
|
|
1352
|
+
/** Message type Reaction */
|
|
1353
|
+
export class Reaction {
|
|
1354
|
+
id: MessageId
|
|
1355
|
+
orphan: number
|
|
1356
|
+
orphanReason?: string
|
|
1357
|
+
timestamp: number
|
|
1358
|
+
reaction: string
|
|
1359
|
+
read: boolean
|
|
1360
|
+
msgId: MessageId
|
|
1361
|
+
senderId: string
|
|
1362
|
+
ack?: number
|
|
1363
|
+
}
|
|
1299
1364
|
}
|
|
1300
1365
|
|
|
1301
1366
|
export = WAWebJS
|
package/index.js
CHANGED
|
@@ -25,6 +25,7 @@ module.exports = {
|
|
|
25
25
|
// Auth Strategies
|
|
26
26
|
NoAuth: require('./src/authStrategies/NoAuth'),
|
|
27
27
|
LocalAuth: require('./src/authStrategies/LocalAuth'),
|
|
28
|
+
RemoteAuth: require('./src/authStrategies/RemoteAuth'),
|
|
28
29
|
LegacySessionAuth: require('./src/authStrategies/LegacySessionAuth'),
|
|
29
30
|
|
|
30
31
|
...Constants
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "whatsapp-web.js",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.18.0-alpha.0",
|
|
4
4
|
"description": "Library for interacting with the WhatsApp Web API ",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"typings": "./index.d.ts",
|
|
@@ -51,5 +51,10 @@
|
|
|
51
51
|
},
|
|
52
52
|
"engines": {
|
|
53
53
|
"node": ">=12.0.0"
|
|
54
|
+
},
|
|
55
|
+
"optionalDependencies": {
|
|
56
|
+
"archiver": "^5.3.1",
|
|
57
|
+
"fs-extra": "^10.1.0",
|
|
58
|
+
"unzipper": "^0.10.11"
|
|
54
59
|
}
|
|
55
60
|
}
|
package/src/Client.js
CHANGED
|
@@ -10,7 +10,7 @@ const { WhatsWebURL, DefaultOptions, Events, WAState } = require('./util/Constan
|
|
|
10
10
|
const { ExposeStore, LoadUtils } = require('./util/Injected');
|
|
11
11
|
const ChatFactory = require('./factories/ChatFactory');
|
|
12
12
|
const ContactFactory = require('./factories/ContactFactory');
|
|
13
|
-
const { ClientInfo, Message, MessageMedia, Contact, Location, GroupNotification, Label, Call, Buttons, List } = require('./structures');
|
|
13
|
+
const { ClientInfo, Message, MessageMedia, Contact, Location, GroupNotification, Label, Call, Buttons, List, Reaction } = require('./structures');
|
|
14
14
|
const LegacySessionAuth = require('./authStrategies/LegacySessionAuth');
|
|
15
15
|
const NoAuth = require('./authStrategies/NoAuth');
|
|
16
16
|
|
|
@@ -92,7 +92,12 @@ class Client extends EventEmitter {
|
|
|
92
92
|
browser = await puppeteer.connect(puppeteerOpts);
|
|
93
93
|
page = await browser.newPage();
|
|
94
94
|
} else {
|
|
95
|
-
|
|
95
|
+
const browserArgs = [...(puppeteerOpts.args || [])];
|
|
96
|
+
if(!browserArgs.find(arg => arg.includes('--user-agent'))) {
|
|
97
|
+
browserArgs.push(`--user-agent=${this.options.userAgent}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
browser = await puppeteer.launch({...puppeteerOpts, args: browserArgs});
|
|
96
101
|
page = (await browser.pages())[0];
|
|
97
102
|
}
|
|
98
103
|
|
|
@@ -110,6 +115,52 @@ class Client extends EventEmitter {
|
|
|
110
115
|
referer: 'https://whatsapp.com/'
|
|
111
116
|
});
|
|
112
117
|
|
|
118
|
+
await page.evaluate(`function getElementByXpath(path) {
|
|
119
|
+
return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
|
|
120
|
+
}`);
|
|
121
|
+
|
|
122
|
+
let lastPercent = null,
|
|
123
|
+
lastPercentMessage = null;
|
|
124
|
+
|
|
125
|
+
await page.exposeFunction('loadingScreen', async (percent, message) => {
|
|
126
|
+
if (lastPercent !== percent || lastPercentMessage !== message) {
|
|
127
|
+
this.emit(Events.LOADING_SCREEN, percent, message);
|
|
128
|
+
lastPercent = percent;
|
|
129
|
+
lastPercentMessage = message;
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
await page.evaluate(
|
|
134
|
+
async function (selectors) {
|
|
135
|
+
var observer = new MutationObserver(function () {
|
|
136
|
+
let progressBar = window.getElementByXpath(
|
|
137
|
+
selectors.PROGRESS
|
|
138
|
+
);
|
|
139
|
+
let progressMessage = window.getElementByXpath(
|
|
140
|
+
selectors.PROGRESS_MESSAGE
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
if (progressBar) {
|
|
144
|
+
window.loadingScreen(
|
|
145
|
+
progressBar.value,
|
|
146
|
+
progressMessage.innerText
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
observer.observe(document, {
|
|
152
|
+
attributes: true,
|
|
153
|
+
childList: true,
|
|
154
|
+
characterData: true,
|
|
155
|
+
subtree: true,
|
|
156
|
+
});
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
PROGRESS: '//*[@id=\'app\']/div/div/div[2]/progress',
|
|
160
|
+
PROGRESS_MESSAGE: '//*[@id=\'app\']/div/div/div[3]',
|
|
161
|
+
}
|
|
162
|
+
);
|
|
163
|
+
|
|
113
164
|
const INTRO_IMG_SELECTOR = '[data-testid="intro-md-beta-logo-dark"], [data-testid="intro-md-beta-logo-light"], [data-asset-intro-image-light="true"], [data-asset-intro-image-dark="true"]';
|
|
114
165
|
const INTRO_QRCODE_SELECTOR = 'div[data-ref] canvas';
|
|
115
166
|
|
|
@@ -127,7 +178,7 @@ class Client extends EventEmitter {
|
|
|
127
178
|
})
|
|
128
179
|
]);
|
|
129
180
|
|
|
130
|
-
// Checks if an error
|
|
181
|
+
// Checks if an error occurred on the first found selector. The second will be discarded and ignored by .race;
|
|
131
182
|
if (needAuthentication instanceof Error) throw needAuthentication;
|
|
132
183
|
|
|
133
184
|
// Scan-qrcode selector was found. Needs authentication
|
|
@@ -368,7 +419,7 @@ class Client extends EventEmitter {
|
|
|
368
419
|
this.emit(Events.MEDIA_UPLOADED, message);
|
|
369
420
|
});
|
|
370
421
|
|
|
371
|
-
await page.exposeFunction('onAppStateChangedEvent', (state) => {
|
|
422
|
+
await page.exposeFunction('onAppStateChangedEvent', async (state) => {
|
|
372
423
|
|
|
373
424
|
/**
|
|
374
425
|
* Emitted when the connection state changes
|
|
@@ -395,6 +446,7 @@ class Client extends EventEmitter {
|
|
|
395
446
|
* @event Client#disconnected
|
|
396
447
|
* @param {WAState|"NAVIGATION"} reason reason that caused the disconnect
|
|
397
448
|
*/
|
|
449
|
+
await this.authStrategy.disconnect();
|
|
398
450
|
this.emit(Events.DISCONNECTED, state);
|
|
399
451
|
this.destroy();
|
|
400
452
|
}
|
|
@@ -434,6 +486,27 @@ class Client extends EventEmitter {
|
|
|
434
486
|
this.emit(Events.INCOMING_CALL, cll);
|
|
435
487
|
});
|
|
436
488
|
|
|
489
|
+
await page.exposeFunction('onReaction', (reactions) => {
|
|
490
|
+
for (const reaction of reactions) {
|
|
491
|
+
/**
|
|
492
|
+
* Emitted when a reaction is sent, received, updated or removed
|
|
493
|
+
* @event Client#message_reaction
|
|
494
|
+
* @param {object} reaction
|
|
495
|
+
* @param {object} reaction.id - Reaction id
|
|
496
|
+
* @param {number} reaction.orphan - Orphan
|
|
497
|
+
* @param {?string} reaction.orphanReason - Orphan reason
|
|
498
|
+
* @param {number} reaction.timestamp - Timestamp
|
|
499
|
+
* @param {string} reaction.reaction - Reaction
|
|
500
|
+
* @param {boolean} reaction.read - Read
|
|
501
|
+
* @param {object} reaction.msgId - Parent message id
|
|
502
|
+
* @param {string} reaction.senderId - Sender id
|
|
503
|
+
* @param {?number} reaction.ack - Ack
|
|
504
|
+
*/
|
|
505
|
+
|
|
506
|
+
this.emit(Events.MESSAGE_REACTION, new Reaction(this, reaction));
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
|
|
437
510
|
await page.evaluate(() => {
|
|
438
511
|
window.Store.Msg.on('change', (msg) => { window.onChangeMessageEvent(window.WWebJS.getMessageModel(msg)); });
|
|
439
512
|
window.Store.Msg.on('change:type', (msg) => { window.onChangeMessageTypeEvent(window.WWebJS.getMessageModel(msg)); });
|
|
@@ -453,6 +526,22 @@ class Client extends EventEmitter {
|
|
|
453
526
|
}
|
|
454
527
|
}
|
|
455
528
|
});
|
|
529
|
+
|
|
530
|
+
{
|
|
531
|
+
const module = window.Store.createOrUpdateReactionsModule;
|
|
532
|
+
const ogMethod = module.createOrUpdateReactions;
|
|
533
|
+
module.createOrUpdateReactions = ((...args) => {
|
|
534
|
+
window.onReaction(args[0].map(reaction => {
|
|
535
|
+
const msgKey = window.Store.MsgKey.fromString(reaction.msgKey);
|
|
536
|
+
const parentMsgKey = window.Store.MsgKey.fromString(reaction.parentMsgKey);
|
|
537
|
+
const timestamp = reaction.timestamp / 1000;
|
|
538
|
+
|
|
539
|
+
return {...reaction, msgKey, parentMsgKey, timestamp };
|
|
540
|
+
}));
|
|
541
|
+
|
|
542
|
+
return ogMethod(...args);
|
|
543
|
+
}).bind(module);
|
|
544
|
+
}
|
|
456
545
|
});
|
|
457
546
|
|
|
458
547
|
/**
|
|
@@ -460,11 +549,13 @@ class Client extends EventEmitter {
|
|
|
460
549
|
* @event Client#ready
|
|
461
550
|
*/
|
|
462
551
|
this.emit(Events.READY);
|
|
552
|
+
this.authStrategy.afterAuthReady();
|
|
463
553
|
|
|
464
554
|
// Disconnect when navigating away when in PAIRING state (detect logout)
|
|
465
555
|
this.pupPage.on('framenavigated', async () => {
|
|
466
556
|
const appState = await this.getState();
|
|
467
557
|
if(!appState || appState === WAState.PAIRING) {
|
|
558
|
+
await this.authStrategy.disconnect();
|
|
468
559
|
this.emit(Events.DISCONNECTED, 'NAVIGATION');
|
|
469
560
|
await this.destroy();
|
|
470
561
|
}
|
|
@@ -476,6 +567,7 @@ class Client extends EventEmitter {
|
|
|
476
567
|
*/
|
|
477
568
|
async destroy() {
|
|
478
569
|
await this.pupBrowser.close();
|
|
570
|
+
await this.authStrategy.destroy();
|
|
479
571
|
}
|
|
480
572
|
|
|
481
573
|
/**
|
|
@@ -580,7 +672,7 @@ class Client extends EventEmitter {
|
|
|
580
672
|
internalOptions.list = content;
|
|
581
673
|
content = '';
|
|
582
674
|
}
|
|
583
|
-
|
|
675
|
+
|
|
584
676
|
if (internalOptions.sendMediaAsSticker && internalOptions.attachment) {
|
|
585
677
|
internalOptions.attachment = await Util.formatToWebpSticker(
|
|
586
678
|
internalOptions.attachment, {
|
|
@@ -744,7 +836,7 @@ class Client extends EventEmitter {
|
|
|
744
836
|
|
|
745
837
|
return couldSet;
|
|
746
838
|
}
|
|
747
|
-
|
|
839
|
+
|
|
748
840
|
/**
|
|
749
841
|
* Gets the current connection state for the client
|
|
750
842
|
* @returns {WAState}
|
|
@@ -944,7 +1036,8 @@ class Client extends EventEmitter {
|
|
|
944
1036
|
}
|
|
945
1037
|
|
|
946
1038
|
return await this.pupPage.evaluate(async number => {
|
|
947
|
-
const
|
|
1039
|
+
const wid = window.Store.WidFactory.createWid(number);
|
|
1040
|
+
const result = await window.Store.QueryExist(wid);
|
|
948
1041
|
if (!result || result.wid === undefined) return null;
|
|
949
1042
|
return result.wid;
|
|
950
1043
|
}, number);
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/* Require Optional Dependencies */
|
|
4
|
+
try {
|
|
5
|
+
var fs = require('fs-extra');
|
|
6
|
+
var unzipper = require('unzipper');
|
|
7
|
+
var archiver = require('archiver');
|
|
8
|
+
} catch {
|
|
9
|
+
fs = undefined;
|
|
10
|
+
unzipper = undefined;
|
|
11
|
+
archiver = undefined;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const { Events } = require('./../util/Constants');
|
|
16
|
+
const BaseAuthStrategy = require('./BaseAuthStrategy');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Remote-based authentication
|
|
20
|
+
* @param {object} options - options
|
|
21
|
+
* @param {object} options.store - Remote database store instance
|
|
22
|
+
* @param {string} options.clientId - Client id to distinguish instances if you are using multiple, otherwise keep null if you are using only one instance
|
|
23
|
+
* @param {string} options.dataPath - Change the default path for saving session files, default is: "./.wwebjs_auth/"
|
|
24
|
+
* @param {number} options.backupSyncIntervalMs - Sets the time interval for periodic session backups. Accepts values starting from 60000ms {1 minute}
|
|
25
|
+
*/
|
|
26
|
+
class RemoteAuth extends BaseAuthStrategy {
|
|
27
|
+
constructor({ clientId, dataPath, store, backupSyncIntervalMs } = {}) {
|
|
28
|
+
if (!fs && !unzipper && !archiver) throw new Error('Optional Dependencies [fs-extra, unzipper, archiver] are required to use RemoteAuth. Make sure to run npm install correctly and remove the --no-optional flag');
|
|
29
|
+
super();
|
|
30
|
+
|
|
31
|
+
const idRegex = /^[-_\w]+$/i;
|
|
32
|
+
if (clientId && !idRegex.test(clientId)) {
|
|
33
|
+
throw new Error('Invalid clientId. Only alphanumeric characters, underscores and hyphens are allowed.');
|
|
34
|
+
}
|
|
35
|
+
if (!backupSyncIntervalMs || backupSyncIntervalMs < 60000) {
|
|
36
|
+
throw new Error('Invalid backupSyncIntervalMs. Accepts values starting from 60000ms {1 minute}.');
|
|
37
|
+
}
|
|
38
|
+
if(!store) throw new Error('Remote database store is required.');
|
|
39
|
+
|
|
40
|
+
this.store = store;
|
|
41
|
+
this.clientId = clientId;
|
|
42
|
+
this.backupSyncIntervalMs = backupSyncIntervalMs;
|
|
43
|
+
this.dataPath = path.resolve(dataPath || './.wwebjs_auth/');
|
|
44
|
+
this.tempDir = `${this.dataPath}/wwebjs_temp_session`;
|
|
45
|
+
this.requiredDirs = ['Default', 'IndexedDB', 'Local Storage']; /* => Required Files & Dirs in WWebJS to restore session */
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async beforeBrowserInitialized() {
|
|
49
|
+
const puppeteerOpts = this.client.options.puppeteer;
|
|
50
|
+
const sessionDirName = this.clientId ? `RemoteAuth-${this.clientId}` : 'RemoteAuth';
|
|
51
|
+
const dirPath = path.join(this.dataPath, sessionDirName);
|
|
52
|
+
|
|
53
|
+
if (puppeteerOpts.userDataDir && puppeteerOpts.userDataDir !== dirPath) {
|
|
54
|
+
throw new Error('RemoteAuth is not compatible with a user-supplied userDataDir.');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
this.userDataDir = dirPath;
|
|
58
|
+
this.sessionName = sessionDirName;
|
|
59
|
+
|
|
60
|
+
await this.extractRemoteSession();
|
|
61
|
+
|
|
62
|
+
this.client.options.puppeteer = {
|
|
63
|
+
...puppeteerOpts,
|
|
64
|
+
userDataDir: dirPath
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async logout() {
|
|
69
|
+
await this.disconnect();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async destroy() {
|
|
73
|
+
clearInterval(this.backupSync);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async disconnect() {
|
|
77
|
+
await this.deleteRemoteSession();
|
|
78
|
+
|
|
79
|
+
let pathExists = await this.isValidPath(this.userDataDir);
|
|
80
|
+
if (pathExists) {
|
|
81
|
+
await fs.promises.rm(this.userDataDir, {
|
|
82
|
+
recursive: true,
|
|
83
|
+
force: true
|
|
84
|
+
}).catch(() => {});
|
|
85
|
+
}
|
|
86
|
+
clearInterval(this.backupSync);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async afterAuthReady() {
|
|
90
|
+
const sessionExists = await this.store.sessionExists({session: this.sessionName});
|
|
91
|
+
if(!sessionExists) {
|
|
92
|
+
await this.delay(60000); /* Initial delay sync required for session to be stable enough to recover */
|
|
93
|
+
await this.storeRemoteSession({emit: true});
|
|
94
|
+
}
|
|
95
|
+
var self = this;
|
|
96
|
+
this.backupSync = setInterval(async function () {
|
|
97
|
+
await self.storeRemoteSession();
|
|
98
|
+
}, this.backupSyncIntervalMs);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async storeRemoteSession(options) {
|
|
102
|
+
/* Compress & Store Session */
|
|
103
|
+
const pathExists = await this.isValidPath(this.userDataDir);
|
|
104
|
+
if (pathExists) {
|
|
105
|
+
await this.compressSession();
|
|
106
|
+
await this.store.save({session: this.sessionName});
|
|
107
|
+
await fs.promises.unlink(`${this.sessionName}.zip`);
|
|
108
|
+
await fs.promises.rm(`${this.tempDir}`, {
|
|
109
|
+
recursive: true,
|
|
110
|
+
force: true
|
|
111
|
+
}).catch(() => {});
|
|
112
|
+
if(options && options.emit) this.client.emit(Events.REMOTE_SESSION_SAVED);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async extractRemoteSession() {
|
|
117
|
+
const pathExists = await this.isValidPath(this.userDataDir);
|
|
118
|
+
const compressedSessionPath = `${this.sessionName}.zip`;
|
|
119
|
+
const sessionExists = await this.store.sessionExists({session: this.sessionName});
|
|
120
|
+
if (pathExists) {
|
|
121
|
+
await fs.promises.rm(this.userDataDir, {
|
|
122
|
+
recursive: true,
|
|
123
|
+
force: true
|
|
124
|
+
}).catch(() => {});
|
|
125
|
+
}
|
|
126
|
+
if (sessionExists) {
|
|
127
|
+
await this.store.extract({session: this.sessionName, path: compressedSessionPath});
|
|
128
|
+
await this.unCompressSession(compressedSessionPath);
|
|
129
|
+
} else {
|
|
130
|
+
fs.mkdirSync(this.userDataDir, { recursive: true });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async deleteRemoteSession() {
|
|
135
|
+
const sessionExists = await this.store.sessionExists({session: this.sessionName});
|
|
136
|
+
if (sessionExists) await this.store.delete({session: this.sessionName});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async compressSession() {
|
|
140
|
+
const archive = archiver('zip');
|
|
141
|
+
const stream = fs.createWriteStream(`${this.sessionName}.zip`);
|
|
142
|
+
|
|
143
|
+
await fs.copy(this.userDataDir, this.tempDir).catch(() => {});
|
|
144
|
+
await this.deleteMetadata();
|
|
145
|
+
return new Promise((resolve, reject) => {
|
|
146
|
+
archive
|
|
147
|
+
.directory(this.tempDir, false)
|
|
148
|
+
.on('error', err => reject(err))
|
|
149
|
+
.pipe(stream);
|
|
150
|
+
|
|
151
|
+
stream.on('close', () => resolve());
|
|
152
|
+
archive.finalize();
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async unCompressSession(compressedSessionPath) {
|
|
157
|
+
var stream = fs.createReadStream(compressedSessionPath);
|
|
158
|
+
await new Promise((resolve, reject) => {
|
|
159
|
+
stream.pipe(unzipper.Extract({
|
|
160
|
+
path: this.userDataDir
|
|
161
|
+
}))
|
|
162
|
+
.on('error', err => reject(err))
|
|
163
|
+
.on('finish', () => resolve());
|
|
164
|
+
});
|
|
165
|
+
await fs.promises.unlink(compressedSessionPath);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async deleteMetadata() {
|
|
169
|
+
const sessionDirs = [this.tempDir, path.join(this.tempDir, 'Default')];
|
|
170
|
+
for (const dir of sessionDirs) {
|
|
171
|
+
const sessionFiles = await fs.promises.readdir(dir);
|
|
172
|
+
for (const element of sessionFiles) {
|
|
173
|
+
if (!this.requiredDirs.includes(element)) {
|
|
174
|
+
const dirElement = path.join(dir, element);
|
|
175
|
+
const stats = await fs.promises.lstat(dirElement);
|
|
176
|
+
|
|
177
|
+
if (stats.isDirectory()) {
|
|
178
|
+
await fs.promises.rm(dirElement, {
|
|
179
|
+
recursive: true,
|
|
180
|
+
force: true
|
|
181
|
+
});
|
|
182
|
+
} else {
|
|
183
|
+
await fs.promises.unlink(dirElement);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async isValidPath(path) {
|
|
191
|
+
try {
|
|
192
|
+
await fs.promises.access(path);
|
|
193
|
+
return true;
|
|
194
|
+
} catch {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async delay(ms) {
|
|
200
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
module.exports = RemoteAuth;
|
package/src/structures/Chat.js
CHANGED
|
@@ -170,13 +170,22 @@ class Chat extends Base {
|
|
|
170
170
|
|
|
171
171
|
/**
|
|
172
172
|
* Loads chat messages, sorted from earliest to latest.
|
|
173
|
-
* @param {Object} searchOptions Options for searching messages. Right now only limit is supported.
|
|
173
|
+
* @param {Object} searchOptions Options for searching messages. Right now only limit and fromMe is supported.
|
|
174
174
|
* @param {Number} [searchOptions.limit] The amount of messages to return. If no limit is specified, the available messages will be returned. Note that the actual number of returned messages may be smaller if there aren't enough messages in the conversation. Set this to Infinity to load all messages.
|
|
175
|
+
* @param {Boolean} [searchOptions.fromMe] Return only messages from the bot number or vise versa. To get all messages, leave the option undefined.
|
|
175
176
|
* @returns {Promise<Array<Message>>}
|
|
176
177
|
*/
|
|
177
178
|
async fetchMessages(searchOptions) {
|
|
178
179
|
let messages = await this.client.pupPage.evaluate(async (chatId, searchOptions) => {
|
|
179
|
-
const msgFilter = m =>
|
|
180
|
+
const msgFilter = (m) => {
|
|
181
|
+
if (m.isNotification) {
|
|
182
|
+
return false; // dont include notification messages
|
|
183
|
+
}
|
|
184
|
+
if (searchOptions && searchOptions.fromMe && m.id.fromMe !== searchOptions.fromMe) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
return true;
|
|
188
|
+
};
|
|
180
189
|
|
|
181
190
|
const chat = window.Store.Chat.get(chatId);
|
|
182
191
|
let msgs = chat.msgs.getModelsArray().filter(msgFilter);
|
|
@@ -184,7 +193,7 @@ class Chat extends Base {
|
|
|
184
193
|
if (searchOptions && searchOptions.limit > 0) {
|
|
185
194
|
while (msgs.length < searchOptions.limit) {
|
|
186
195
|
const loadedMessages = await window.Store.ConversationMsgs.loadEarlierMsgs(chat);
|
|
187
|
-
if (!loadedMessages) break;
|
|
196
|
+
if (!loadedMessages || !loadedMessages.length) break;
|
|
188
197
|
msgs = [...loadedMessages.filter(msgFilter), ...msgs];
|
|
189
198
|
}
|
|
190
199
|
|
|
@@ -147,7 +147,7 @@ class GroupChat extends Chat {
|
|
|
147
147
|
this.groupMetadata.desc = description;
|
|
148
148
|
return true;
|
|
149
149
|
}
|
|
150
|
-
|
|
150
|
+
|
|
151
151
|
/**
|
|
152
152
|
* Updates the group settings to only allow admins to send messages.
|
|
153
153
|
* @param {boolean} [adminsOnly=true] Enable or disable this option
|
|
@@ -335,6 +335,20 @@ class Message extends Base {
|
|
|
335
335
|
return this.client.sendMessage(chatId, content, options);
|
|
336
336
|
}
|
|
337
337
|
|
|
338
|
+
/**
|
|
339
|
+
* React to this message with an emoji
|
|
340
|
+
* @param {string} reaction - Emoji to react with. Send an empty string to remove the reaction.
|
|
341
|
+
* @return {Promise}
|
|
342
|
+
*/
|
|
343
|
+
async react(reaction){
|
|
344
|
+
await this.client.pupPage.evaluate(async (messageId, reaction) => {
|
|
345
|
+
if (!messageId) { return undefined; }
|
|
346
|
+
|
|
347
|
+
const msg = await window.Store.Msg.get(messageId);
|
|
348
|
+
await window.Store.sendReactionToMsg(msg, reaction);
|
|
349
|
+
}, this.id._serialized, reaction);
|
|
350
|
+
}
|
|
351
|
+
|
|
338
352
|
/**
|
|
339
353
|
* Accept Group V4 Invite
|
|
340
354
|
* @returns {Promise<Object>}
|
|
@@ -344,7 +358,7 @@ class Message extends Base {
|
|
|
344
358
|
}
|
|
345
359
|
|
|
346
360
|
/**
|
|
347
|
-
* Forwards this message to another chat
|
|
361
|
+
* Forwards this message to another chat (that you chatted before, otherwise it will fail)
|
|
348
362
|
*
|
|
349
363
|
* @param {string|Chat} chat Chat model or chat ID to which the message will be forwarded
|
|
350
364
|
* @returns {Promise}
|
|
@@ -396,12 +410,13 @@ class Message extends Base {
|
|
|
396
410
|
signal: (new AbortController).signal
|
|
397
411
|
});
|
|
398
412
|
|
|
399
|
-
const data = window.WWebJS.
|
|
413
|
+
const data = await window.WWebJS.arrayBufferToBase64Async(decryptedMedia);
|
|
400
414
|
|
|
401
415
|
return {
|
|
402
416
|
data,
|
|
403
417
|
mimetype: msg.mimetype,
|
|
404
|
-
filename: msg.filename
|
|
418
|
+
filename: msg.filename,
|
|
419
|
+
filesize: msg.size
|
|
405
420
|
};
|
|
406
421
|
} catch (e) {
|
|
407
422
|
if(e.status && e.status === 404) return undefined;
|
|
@@ -410,7 +425,7 @@ class Message extends Base {
|
|
|
410
425
|
}, this.id._serialized);
|
|
411
426
|
|
|
412
427
|
if (!result) return undefined;
|
|
413
|
-
return new MessageMedia(result.mimetype, result.data, result.filename);
|
|
428
|
+
return new MessageMedia(result.mimetype, result.data, result.filename, result.filesize);
|
|
414
429
|
}
|
|
415
430
|
|
|
416
431
|
/**
|
|
@@ -437,7 +452,7 @@ class Message extends Base {
|
|
|
437
452
|
let msg = window.Store.Msg.get(msgId);
|
|
438
453
|
|
|
439
454
|
if (msg.canStar()) {
|
|
440
|
-
return
|
|
455
|
+
return window.Store.Cmd.sendStarMsgs(msg.chat, [msg], false);
|
|
441
456
|
}
|
|
442
457
|
}, this.id._serialized);
|
|
443
458
|
}
|
|
@@ -450,7 +465,7 @@ class Message extends Base {
|
|
|
450
465
|
let msg = window.Store.Msg.get(msgId);
|
|
451
466
|
|
|
452
467
|
if (msg.canStar()) {
|
|
453
|
-
return msg.chat
|
|
468
|
+
return window.Store.Cmd.sendUnstarMsgs(msg.chat, [msg], false);
|
|
454
469
|
}
|
|
455
470
|
}, this.id._serialized);
|
|
456
471
|
}
|
|
@@ -10,10 +10,11 @@ const { URL } = require('url');
|
|
|
10
10
|
* Media attached to a message
|
|
11
11
|
* @param {string} mimetype MIME type of the attachment
|
|
12
12
|
* @param {string} data Base64-encoded data of the file
|
|
13
|
-
* @param {?string} filename Document file name
|
|
13
|
+
* @param {?string} filename Document file name. Value can be null
|
|
14
|
+
* @param {?number} filesize Document file size in bytes. Value can be null
|
|
14
15
|
*/
|
|
15
16
|
class MessageMedia {
|
|
16
|
-
constructor(mimetype, data, filename) {
|
|
17
|
+
constructor(mimetype, data, filename, filesize) {
|
|
17
18
|
/**
|
|
18
19
|
* MIME type of the attachment
|
|
19
20
|
* @type {string}
|
|
@@ -27,10 +28,16 @@ class MessageMedia {
|
|
|
27
28
|
this.data = data;
|
|
28
29
|
|
|
29
30
|
/**
|
|
30
|
-
*
|
|
31
|
+
* Document file name. Value can be null
|
|
31
32
|
* @type {?string}
|
|
32
33
|
*/
|
|
33
34
|
this.filename = filename;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Document file size in bytes. Value can be null
|
|
38
|
+
* @type {?number}
|
|
39
|
+
*/
|
|
40
|
+
this.filesize = filesize;
|
|
34
41
|
}
|
|
35
42
|
|
|
36
43
|
/**
|
|
@@ -68,6 +75,7 @@ class MessageMedia {
|
|
|
68
75
|
const reqOptions = Object.assign({ headers: { accept: 'image/* video/* text/* audio/*' } }, options);
|
|
69
76
|
const response = await fetch(url, reqOptions);
|
|
70
77
|
const mime = response.headers.get('Content-Type');
|
|
78
|
+
const size = response.headers.get('Content-Length');
|
|
71
79
|
|
|
72
80
|
const contentDisposition = response.headers.get('Content-Disposition');
|
|
73
81
|
const name = contentDisposition ? contentDisposition.match(/((?<=filename=")(.*)(?="))/) : null;
|
|
@@ -83,7 +91,7 @@ class MessageMedia {
|
|
|
83
91
|
data = btoa(data);
|
|
84
92
|
}
|
|
85
93
|
|
|
86
|
-
return { data, mime, name };
|
|
94
|
+
return { data, mime, name, size };
|
|
87
95
|
}
|
|
88
96
|
|
|
89
97
|
const res = options.client
|
|
@@ -96,7 +104,7 @@ class MessageMedia {
|
|
|
96
104
|
if (!mimetype)
|
|
97
105
|
mimetype = res.mime;
|
|
98
106
|
|
|
99
|
-
return new MessageMedia(mimetype, res.data, filename);
|
|
107
|
+
return new MessageMedia(mimetype, res.data, filename, res.size || null);
|
|
100
108
|
}
|
|
101
109
|
}
|
|
102
110
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Base = require('./Base');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Represents a Reaction on WhatsApp
|
|
7
|
+
* @extends {Base}
|
|
8
|
+
*/
|
|
9
|
+
class Reaction extends Base {
|
|
10
|
+
constructor(client, data) {
|
|
11
|
+
super(client);
|
|
12
|
+
|
|
13
|
+
if (data) this._patch(data);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
_patch(data) {
|
|
17
|
+
/**
|
|
18
|
+
* Reaction ID
|
|
19
|
+
* @type {object}
|
|
20
|
+
*/
|
|
21
|
+
this.id = data.msgKey;
|
|
22
|
+
/**
|
|
23
|
+
* Orphan
|
|
24
|
+
* @type {number}
|
|
25
|
+
*/
|
|
26
|
+
this.orphan = data.orphan;
|
|
27
|
+
/**
|
|
28
|
+
* Orphan reason
|
|
29
|
+
* @type {?string}
|
|
30
|
+
*/
|
|
31
|
+
this.orphanReason = data.orphanReason;
|
|
32
|
+
/**
|
|
33
|
+
* Unix timestamp for when the reaction was created
|
|
34
|
+
* @type {number}
|
|
35
|
+
*/
|
|
36
|
+
this.timestamp = data.timestamp;
|
|
37
|
+
/**
|
|
38
|
+
* Reaction
|
|
39
|
+
* @type {string}
|
|
40
|
+
*/
|
|
41
|
+
this.reaction = data.reactionText;
|
|
42
|
+
/**
|
|
43
|
+
* Read
|
|
44
|
+
* @type {boolean}
|
|
45
|
+
*/
|
|
46
|
+
this.read = data.read;
|
|
47
|
+
/**
|
|
48
|
+
* Message ID
|
|
49
|
+
* @type {object}
|
|
50
|
+
*/
|
|
51
|
+
this.msgId = data.parentMsgKey;
|
|
52
|
+
/**
|
|
53
|
+
* Sender ID
|
|
54
|
+
* @type {string}
|
|
55
|
+
*/
|
|
56
|
+
this.senderId = data.senderUserJid;
|
|
57
|
+
/**
|
|
58
|
+
* ACK
|
|
59
|
+
* @type {?number}
|
|
60
|
+
*/
|
|
61
|
+
this.ack = data.ack;
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
return super._patch(data);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
module.exports = Reaction;
|
package/src/structures/index.js
CHANGED
package/src/util/Constants.js
CHANGED
|
@@ -11,7 +11,7 @@ exports.DefaultOptions = {
|
|
|
11
11
|
qrMaxRetries: 0,
|
|
12
12
|
takeoverOnConflict: false,
|
|
13
13
|
takeoverTimeoutMs: 0,
|
|
14
|
-
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
|
|
14
|
+
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36',
|
|
15
15
|
ffmpegPath: 'ffmpeg',
|
|
16
16
|
bypassCSP: false
|
|
17
17
|
};
|
|
@@ -41,15 +41,18 @@ exports.Events = {
|
|
|
41
41
|
MESSAGE_REVOKED_EVERYONE: 'message_revoke_everyone',
|
|
42
42
|
MESSAGE_REVOKED_ME: 'message_revoke_me',
|
|
43
43
|
MESSAGE_ACK: 'message_ack',
|
|
44
|
+
MESSAGE_REACTION: 'message_reaction',
|
|
44
45
|
MEDIA_UPLOADED: 'media_uploaded',
|
|
45
46
|
GROUP_JOIN: 'group_join',
|
|
46
47
|
GROUP_LEAVE: 'group_leave',
|
|
47
48
|
GROUP_UPDATE: 'group_update',
|
|
48
49
|
QR_RECEIVED: 'qr',
|
|
50
|
+
LOADING_SCREEN: 'loading_screen',
|
|
49
51
|
DISCONNECTED: 'disconnected',
|
|
50
52
|
STATE_CHANGED: 'change_state',
|
|
51
53
|
BATTERY_CHANGED: 'change_battery',
|
|
52
|
-
INCOMING_CALL: 'incoming_call'
|
|
54
|
+
INCOMING_CALL: 'incoming_call',
|
|
55
|
+
REMOTE_SESSION_SAVED: 'remote_session_saved'
|
|
53
56
|
};
|
|
54
57
|
|
|
55
58
|
/**
|
package/src/util/Injected.js
CHANGED
|
@@ -49,6 +49,8 @@ exports.ExposeStore = (moduleRaidStr) => {
|
|
|
49
49
|
window.Store.findCommonGroups = window.mR.findModule('findCommonGroups')[0].findCommonGroups;
|
|
50
50
|
window.Store.StatusUtils = window.mR.findModule('setMyStatus')[0];
|
|
51
51
|
window.Store.ConversationMsgs = window.mR.findModule('loadEarlierMsgs')[0];
|
|
52
|
+
window.Store.sendReactionToMsg = window.mR.findModule('sendReactionToMsg')[0].sendReactionToMsg;
|
|
53
|
+
window.Store.createOrUpdateReactionsModule = window.mR.findModule('createOrUpdateReactions')[0];
|
|
52
54
|
window.Store.StickerTools = {
|
|
53
55
|
...window.mR.findModule('toWebpSticker')[0],
|
|
54
56
|
...window.mR.findModule('addWebpMetadata')[0]
|
|
@@ -99,7 +101,6 @@ exports.LoadUtils = () => {
|
|
|
99
101
|
delete options.attachment;
|
|
100
102
|
delete options.sendMediaAsSticker;
|
|
101
103
|
}
|
|
102
|
-
|
|
103
104
|
let quotedMsgOptions = {};
|
|
104
105
|
if (options.quotedMessageId) {
|
|
105
106
|
let quotedMessage = window.Store.Msg.get(options.quotedMessageId);
|
|
@@ -479,6 +480,20 @@ exports.LoadUtils = () => {
|
|
|
479
480
|
return window.btoa(binary);
|
|
480
481
|
};
|
|
481
482
|
|
|
483
|
+
window.WWebJS.arrayBufferToBase64Async = (arrayBuffer) =>
|
|
484
|
+
new Promise((resolve, reject) => {
|
|
485
|
+
const blob = new Blob([arrayBuffer], {
|
|
486
|
+
type: 'application/octet-stream',
|
|
487
|
+
});
|
|
488
|
+
const fileReader = new FileReader();
|
|
489
|
+
fileReader.onload = () => {
|
|
490
|
+
const [, data] = fileReader.result.split(',');
|
|
491
|
+
resolve(data);
|
|
492
|
+
};
|
|
493
|
+
fileReader.onerror = (e) => reject(e);
|
|
494
|
+
fileReader.readAsDataURL(blob);
|
|
495
|
+
});
|
|
496
|
+
|
|
482
497
|
window.WWebJS.getFileHash = async (data) => {
|
|
483
498
|
let buffer = await data.arrayBuffer();
|
|
484
499
|
const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);
|