whatsapp-web-jf.js 1.31.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.
Files changed (55) hide show
  1. package/.env.example +3 -0
  2. package/CODE_OF_CONDUCT.md +133 -0
  3. package/LICENSE +201 -0
  4. package/README.md +185 -0
  5. package/example.js +680 -0
  6. package/index.d.ts +2069 -0
  7. package/index.js +34 -0
  8. package/package.json +55 -0
  9. package/shell.js +36 -0
  10. package/src/Client.js +2188 -0
  11. package/src/authStrategies/BaseAuthStrategy.js +27 -0
  12. package/src/authStrategies/LocalAuth.js +58 -0
  13. package/src/authStrategies/NoAuth.js +12 -0
  14. package/src/authStrategies/RemoteAuth.js +210 -0
  15. package/src/factories/ChatFactory.js +21 -0
  16. package/src/factories/ContactFactory.js +16 -0
  17. package/src/structures/Base.js +22 -0
  18. package/src/structures/Broadcast.js +69 -0
  19. package/src/structures/BusinessContact.js +21 -0
  20. package/src/structures/Buttons.js +82 -0
  21. package/src/structures/Call.js +76 -0
  22. package/src/structures/Channel.js +382 -0
  23. package/src/structures/Chat.js +291 -0
  24. package/src/structures/ClientInfo.js +71 -0
  25. package/src/structures/Contact.js +208 -0
  26. package/src/structures/GroupChat.js +473 -0
  27. package/src/structures/GroupNotification.js +104 -0
  28. package/src/structures/Label.js +50 -0
  29. package/src/structures/List.js +79 -0
  30. package/src/structures/Location.js +62 -0
  31. package/src/structures/Message.js +704 -0
  32. package/src/structures/MessageMedia.js +111 -0
  33. package/src/structures/Order.js +52 -0
  34. package/src/structures/Payment.js +79 -0
  35. package/src/structures/Poll.js +44 -0
  36. package/src/structures/PollVote.js +61 -0
  37. package/src/structures/PrivateChat.js +13 -0
  38. package/src/structures/PrivateContact.js +13 -0
  39. package/src/structures/Product.js +68 -0
  40. package/src/structures/ProductMetadata.js +25 -0
  41. package/src/structures/Reaction.js +69 -0
  42. package/src/structures/index.js +26 -0
  43. package/src/util/Constants.js +176 -0
  44. package/src/util/Injected/AuthStore/AuthStore.js +17 -0
  45. package/src/util/Injected/AuthStore/LegacyAuthStore.js +22 -0
  46. package/src/util/Injected/LegacyStore.js +146 -0
  47. package/src/util/Injected/Store.js +215 -0
  48. package/src/util/Injected/Utils.js +1139 -0
  49. package/src/util/InterfaceController.js +126 -0
  50. package/src/util/Puppeteer.js +23 -0
  51. package/src/util/Util.js +186 -0
  52. package/src/webCache/LocalWebCache.js +40 -0
  53. package/src/webCache/RemoteWebCache.js +40 -0
  54. package/src/webCache/WebCache.js +14 -0
  55. package/src/webCache/WebCacheFactory.js +20 -0
@@ -0,0 +1,126 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Interface Controller
5
+ */
6
+ class InterfaceController {
7
+
8
+ constructor(props) {
9
+ this.pupPage = props.pupPage;
10
+ }
11
+
12
+ /**
13
+ * Opens the Chat Window
14
+ * @param {string} chatId ID of the chat window that will be opened
15
+ */
16
+ async openChatWindow(chatId) {
17
+ await this.pupPage.evaluate(async (chatId) => {
18
+ const chat = await window.WWebJS.getChat(chatId, { getAsModel: false });
19
+ await window.Store.Cmd.openChatBottom(chat);
20
+ }, chatId);
21
+ }
22
+
23
+ /**
24
+ * Opens the Chat Drawer
25
+ * @param {string} chatId ID of the chat drawer that will be opened
26
+ */
27
+ async openChatDrawer(chatId) {
28
+ await this.pupPage.evaluate(async chatId => {
29
+ let chat = await window.WWebJS.getChat(chatId, { getAsModel: false });
30
+ await window.Store.Cmd.openDrawerMid(chat);
31
+ }, chatId);
32
+ }
33
+
34
+ /**
35
+ * Opens the Chat Search
36
+ * @param {string} chatId ID of the chat search that will be opened
37
+ */
38
+ async openChatSearch(chatId) {
39
+ await this.pupPage.evaluate(async chatId => {
40
+ let chat = await window.WWebJS.getChat(chatId, { getAsModel: false });
41
+ await window.Store.Cmd.chatSearch(chat);
42
+ }, chatId);
43
+ }
44
+
45
+ /**
46
+ * Opens or Scrolls the Chat Window to the position of the message
47
+ * @param {string} msgId ID of the message that will be scrolled to
48
+ */
49
+ async openChatWindowAt(msgId) {
50
+ await this.pupPage.evaluate(async (msgId) => {
51
+ const msg = window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
52
+ const chat = window.Store.Chat.get(msg.id.remote) ?? await window.Store.Chat.find(msg.id.remote);
53
+ const searchContext = await window.Store.SearchContext.getSearchContext(chat, msg.id);
54
+ await window.Store.Cmd.openChatAt({ chat: chat, msgContext: searchContext });
55
+ }, msgId);
56
+ }
57
+
58
+ /**
59
+ * Opens the Message Drawer
60
+ * @param {string} msgId ID of the message drawer that will be opened
61
+ */
62
+ async openMessageDrawer(msgId) {
63
+ await this.pupPage.evaluate(async msgId => {
64
+ const msg = window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
65
+ await window.Store.Cmd.msgInfoDrawer(msg);
66
+ }, msgId);
67
+ }
68
+
69
+ /**
70
+ * Closes the Right Drawer
71
+ */
72
+ async closeRightDrawer() {
73
+ await this.pupPage.evaluate(async () => {
74
+ await window.Store.DrawerManager.closeDrawerRight();
75
+ });
76
+ }
77
+
78
+ /**
79
+ * Get all Features
80
+ */
81
+ async getFeatures() {
82
+ return await this.pupPage.evaluate(() => {
83
+ if(!window.Store.Features) throw new Error('This version of Whatsapp Web does not support features');
84
+ return window.Store.Features.F;
85
+ });
86
+ }
87
+
88
+ /**
89
+ * Check if Feature is enabled
90
+ * @param {string} feature status to check
91
+ */
92
+ async checkFeatureStatus(feature) {
93
+ return await this.pupPage.evaluate((feature) => {
94
+ if(!window.Store.Features) throw new Error('This version of Whatsapp Web does not support features');
95
+ return window.Store.Features.supportsFeature(feature);
96
+ }, feature);
97
+ }
98
+
99
+ /**
100
+ * Enable Features
101
+ * @param {string[]} features to be enabled
102
+ */
103
+ async enableFeatures(features) {
104
+ await this.pupPage.evaluate((features) => {
105
+ if(!window.Store.Features) throw new Error('This version of Whatsapp Web does not support features');
106
+ for (const feature in features) {
107
+ window.Store.Features.setFeature(features[feature], true);
108
+ }
109
+ }, features);
110
+ }
111
+
112
+ /**
113
+ * Disable Features
114
+ * @param {string[]} features to be disabled
115
+ */
116
+ async disableFeatures(features) {
117
+ await this.pupPage.evaluate((features) => {
118
+ if(!window.Store.Features) throw new Error('This version of Whatsapp Web does not support features');
119
+ for (const feature in features) {
120
+ window.Store.Features.setFeature(features[feature], false);
121
+ }
122
+ }, features);
123
+ }
124
+ }
125
+
126
+ module.exports = InterfaceController;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Expose a function to the page if it does not exist
3
+ *
4
+ * NOTE:
5
+ * Rewrite it to 'upsertFunction' after updating Puppeteer to 20.6 or higher
6
+ * using page.removeExposedFunction
7
+ * https://pptr.dev/api/puppeteer.page.removeExposedFunction
8
+ *
9
+ * @param {import(puppeteer).Page} page
10
+ * @param {string} name
11
+ * @param {Function} fn
12
+ */
13
+ async function exposeFunctionIfAbsent(page, name, fn) {
14
+ const exist = await page.evaluate((name) => {
15
+ return !!window[name];
16
+ }, name);
17
+ if (exist) {
18
+ return;
19
+ }
20
+ await page.exposeFunction(name, fn);
21
+ }
22
+
23
+ module.exports = {exposeFunctionIfAbsent};
@@ -0,0 +1,186 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const Crypto = require('crypto');
5
+ const { tmpdir } = require('os');
6
+ const ffmpeg = require('fluent-ffmpeg');
7
+ const webp = require('node-webpmux');
8
+ const fs = require('fs').promises;
9
+ const has = (o, k) => Object.prototype.hasOwnProperty.call(o, k);
10
+
11
+ /**
12
+ * Utility methods
13
+ */
14
+ class Util {
15
+ constructor() {
16
+ throw new Error(`The ${this.constructor.name} class may not be instantiated.`);
17
+ }
18
+
19
+ static generateHash(length) {
20
+ var result = '';
21
+ var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
22
+ var charactersLength = characters.length;
23
+ for (var i = 0; i < length; i++) {
24
+ result += characters.charAt(Math.floor(Math.random() * charactersLength));
25
+ }
26
+ return result;
27
+ }
28
+
29
+ /**
30
+ * Sets default properties on an object that aren't already specified.
31
+ * @param {Object} def Default properties
32
+ * @param {Object} given Object to assign defaults to
33
+ * @returns {Object}
34
+ * @private
35
+ */
36
+ static mergeDefault(def, given) {
37
+ if (!given) return def;
38
+ for (const key in def) {
39
+ if (!has(given, key) || given[key] === undefined) {
40
+ given[key] = def[key];
41
+ } else if (given[key] === Object(given[key])) {
42
+ given[key] = Util.mergeDefault(def[key], given[key]);
43
+ }
44
+ }
45
+
46
+ return given;
47
+ }
48
+
49
+ /**
50
+ * Formats a image to webp
51
+ * @param {MessageMedia} media
52
+ *
53
+ * @returns {Promise<MessageMedia>} media in webp format
54
+ */
55
+ static async formatImageToWebpSticker(media, pupPage) {
56
+ if (!media.mimetype.includes('image'))
57
+ throw new Error('media is not a image');
58
+
59
+ if (media.mimetype.includes('webp')) {
60
+ return media;
61
+ }
62
+
63
+ return pupPage.evaluate((media) => {
64
+ return window.WWebJS.toStickerData(media);
65
+ }, media);
66
+ }
67
+
68
+ /**
69
+ * Formats a video to webp
70
+ * @param {MessageMedia} media
71
+ *
72
+ * @returns {Promise<MessageMedia>} media in webp format
73
+ */
74
+ static async formatVideoToWebpSticker(media) {
75
+ if (!media.mimetype.includes('video'))
76
+ throw new Error('media is not a video');
77
+
78
+ const videoType = media.mimetype.split('/')[1];
79
+
80
+ const tempFile = path.join(
81
+ tmpdir(),
82
+ `${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.webp`
83
+ );
84
+
85
+ const stream = new (require('stream').Readable)();
86
+ const buffer = Buffer.from(
87
+ media.data.replace(`data:${media.mimetype};base64,`, ''),
88
+ 'base64'
89
+ );
90
+ stream.push(buffer);
91
+ stream.push(null);
92
+
93
+ await new Promise((resolve, reject) => {
94
+ ffmpeg(stream)
95
+ .inputFormat(videoType)
96
+ .on('error', reject)
97
+ .on('end', () => resolve(true))
98
+ .addOutputOptions([
99
+ '-vcodec',
100
+ 'libwebp',
101
+ '-vf',
102
+ // eslint-disable-next-line no-useless-escape
103
+ 'scale=\'iw*min(300/iw\,300/ih)\':\'ih*min(300/iw\,300/ih)\',format=rgba,pad=300:300:\'(300-iw)/2\':\'(300-ih)/2\':\'#00000000\',setsar=1,fps=10',
104
+ '-loop',
105
+ '0',
106
+ '-ss',
107
+ '00:00:00.0',
108
+ '-t',
109
+ '00:00:05.0',
110
+ '-preset',
111
+ 'default',
112
+ '-an',
113
+ '-vsync',
114
+ '0',
115
+ '-s',
116
+ '512:512',
117
+ ])
118
+ .toFormat('webp')
119
+ .save(tempFile);
120
+ });
121
+
122
+ const data = await fs.readFile(tempFile, 'base64');
123
+ await fs.unlink(tempFile);
124
+
125
+ return {
126
+ mimetype: 'image/webp',
127
+ data: data,
128
+ filename: media.filename,
129
+ };
130
+ }
131
+
132
+ /**
133
+ * Sticker metadata.
134
+ * @typedef {Object} StickerMetadata
135
+ * @property {string} [name]
136
+ * @property {string} [author]
137
+ * @property {string[]} [categories]
138
+ */
139
+
140
+ /**
141
+ * Formats a media to webp
142
+ * @param {MessageMedia} media
143
+ * @param {StickerMetadata} metadata
144
+ *
145
+ * @returns {Promise<MessageMedia>} media in webp format
146
+ */
147
+ static async formatToWebpSticker(media, metadata, pupPage) {
148
+ let webpMedia;
149
+
150
+ if (media.mimetype.includes('image'))
151
+ webpMedia = await this.formatImageToWebpSticker(media, pupPage);
152
+ else if (media.mimetype.includes('video'))
153
+ webpMedia = await this.formatVideoToWebpSticker(media);
154
+ else
155
+ throw new Error('Invalid media format');
156
+
157
+ if (metadata.name || metadata.author) {
158
+ const img = new webp.Image();
159
+ const hash = this.generateHash(32);
160
+ const stickerPackId = hash;
161
+ const packname = metadata.name;
162
+ const author = metadata.author;
163
+ const categories = metadata.categories || [''];
164
+ const json = { 'sticker-pack-id': stickerPackId, 'sticker-pack-name': packname, 'sticker-pack-publisher': author, 'emojis': categories };
165
+ let exifAttr = Buffer.from([0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x41, 0x57, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00]);
166
+ let jsonBuffer = Buffer.from(JSON.stringify(json), 'utf8');
167
+ let exif = Buffer.concat([exifAttr, jsonBuffer]);
168
+ exif.writeUIntLE(jsonBuffer.length, 14, 4);
169
+ await img.load(Buffer.from(webpMedia.data, 'base64'));
170
+ img.exif = exif;
171
+ webpMedia.data = (await img.save(null)).toString('base64');
172
+ }
173
+
174
+ return webpMedia;
175
+ }
176
+
177
+ /**
178
+ * Configure ffmpeg path
179
+ * @param {string} path
180
+ */
181
+ static setFfmpegPath(path) {
182
+ ffmpeg.setFfmpegPath(path);
183
+ }
184
+ }
185
+
186
+ module.exports = Util;
@@ -0,0 +1,40 @@
1
+ const path = require('path');
2
+ const fs = require('fs');
3
+
4
+ const { WebCache, VersionResolveError } = require('./WebCache');
5
+
6
+ /**
7
+ * LocalWebCache - Fetches a WhatsApp Web version from a local file store
8
+ * @param {object} options - options
9
+ * @param {string} options.path - Path to the directory where cached versions are saved, default is: "./.wwebjs_cache/"
10
+ * @param {boolean} options.strict - If true, will throw an error if the requested version can't be fetched. If false, will resolve to the latest version.
11
+ */
12
+ class LocalWebCache extends WebCache {
13
+ constructor(options = {}) {
14
+ super();
15
+
16
+ this.path = options.path || './.wwebjs_cache/';
17
+ this.strict = options.strict || false;
18
+ }
19
+
20
+ async resolve(version) {
21
+ const filePath = path.join(this.path, `${version}.html`);
22
+
23
+ try {
24
+ return fs.readFileSync(filePath, 'utf-8');
25
+ }
26
+ catch (err) {
27
+ if (this.strict) throw new VersionResolveError(`Couldn't load version ${version} from the cache`);
28
+ return null;
29
+ }
30
+ }
31
+
32
+ async persist(indexHtml, version) {
33
+ // version = (version+'').replace(/[^0-9.]/g,'');
34
+ const filePath = path.join(this.path, `${version}.html`);
35
+ fs.mkdirSync(this.path, { recursive: true });
36
+ fs.writeFileSync(filePath, indexHtml);
37
+ }
38
+ }
39
+
40
+ module.exports = LocalWebCache;
@@ -0,0 +1,40 @@
1
+ const fetch = require('node-fetch');
2
+ const { WebCache, VersionResolveError } = require('./WebCache');
3
+
4
+ /**
5
+ * RemoteWebCache - Fetches a WhatsApp Web version index from a remote server
6
+ * @param {object} options - options
7
+ * @param {string} options.remotePath - Endpoint that should be used to fetch the version index. Use {version} as a placeholder for the version number.
8
+ * @param {boolean} options.strict - If true, will throw an error if the requested version can't be fetched. If false, will resolve to the latest version. Defaults to false.
9
+ */
10
+ class RemoteWebCache extends WebCache {
11
+ constructor(options = {}) {
12
+ super();
13
+
14
+ if (!options.remotePath) throw new Error('webVersionCache.remotePath is required when using the remote cache');
15
+ this.remotePath = options.remotePath;
16
+ this.strict = options.strict || false;
17
+ }
18
+
19
+ async resolve(version) {
20
+ const remotePath = this.remotePath.replace('{version}', version);
21
+
22
+ try {
23
+ const cachedRes = await fetch(remotePath);
24
+ if (cachedRes.ok) {
25
+ return cachedRes.text();
26
+ }
27
+ } catch (err) {
28
+ console.error(`Error fetching version ${version} from remote`, err);
29
+ }
30
+
31
+ if (this.strict) throw new VersionResolveError(`Couldn't load version ${version} from the archive`);
32
+ return null;
33
+ }
34
+
35
+ async persist() {
36
+ // Nothing to do here
37
+ }
38
+ }
39
+
40
+ module.exports = RemoteWebCache;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Default implementation of a web version cache that does nothing.
3
+ */
4
+ class WebCache {
5
+ async resolve() { return null; }
6
+ async persist() { }
7
+ }
8
+
9
+ class VersionResolveError extends Error { }
10
+
11
+ module.exports = {
12
+ WebCache,
13
+ VersionResolveError
14
+ };
@@ -0,0 +1,20 @@
1
+ const RemoteWebCache = require('./RemoteWebCache');
2
+ const LocalWebCache = require('./LocalWebCache');
3
+ const { WebCache } = require('./WebCache');
4
+
5
+ const createWebCache = (type, options) => {
6
+ switch (type) {
7
+ case 'remote':
8
+ return new RemoteWebCache(options);
9
+ case 'local':
10
+ return new LocalWebCache(options);
11
+ case 'none':
12
+ return new WebCache();
13
+ default:
14
+ throw new Error(`Invalid WebCache type ${type}`);
15
+ }
16
+ };
17
+
18
+ module.exports = {
19
+ createWebCache,
20
+ };