whatsapp-web-sj.js 1.26.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 (52) 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 +634 -0
  6. package/index.d.ts +1842 -0
  7. package/index.js +32 -0
  8. package/package.json +55 -0
  9. package/shell.js +36 -0
  10. package/src/Client.js +1747 -0
  11. package/src/authStrategies/BaseAuthStrategy.js +27 -0
  12. package/src/authStrategies/LocalAuth.js +56 -0
  13. package/src/authStrategies/NoAuth.js +12 -0
  14. package/src/authStrategies/RemoteAuth.js +204 -0
  15. package/src/factories/ChatFactory.js +16 -0
  16. package/src/factories/ContactFactory.js +16 -0
  17. package/src/structures/Base.js +22 -0
  18. package/src/structures/BusinessContact.js +21 -0
  19. package/src/structures/Buttons.js +82 -0
  20. package/src/structures/Call.js +76 -0
  21. package/src/structures/Chat.js +275 -0
  22. package/src/structures/ClientInfo.js +71 -0
  23. package/src/structures/Contact.js +208 -0
  24. package/src/structures/GroupChat.js +475 -0
  25. package/src/structures/GroupNotification.js +104 -0
  26. package/src/structures/Label.js +50 -0
  27. package/src/structures/List.js +79 -0
  28. package/src/structures/Location.js +61 -0
  29. package/src/structures/Message.js +711 -0
  30. package/src/structures/MessageMedia.js +111 -0
  31. package/src/structures/Order.js +52 -0
  32. package/src/structures/Payment.js +79 -0
  33. package/src/structures/Poll.js +44 -0
  34. package/src/structures/PollVote.js +61 -0
  35. package/src/structures/PrivateChat.js +13 -0
  36. package/src/structures/PrivateContact.js +13 -0
  37. package/src/structures/Product.js +68 -0
  38. package/src/structures/ProductMetadata.js +25 -0
  39. package/src/structures/Reaction.js +69 -0
  40. package/src/structures/index.js +24 -0
  41. package/src/util/Constants.js +176 -0
  42. package/src/util/Injected/AuthStore/AuthStore.js +17 -0
  43. package/src/util/Injected/AuthStore/LegacyAuthStore.js +22 -0
  44. package/src/util/Injected/LegacyStore.js +146 -0
  45. package/src/util/Injected/Store.js +167 -0
  46. package/src/util/Injected/Utils.js +1017 -0
  47. package/src/util/InterfaceController.js +127 -0
  48. package/src/util/Util.js +186 -0
  49. package/src/webCache/LocalWebCache.js +40 -0
  50. package/src/webCache/RemoteWebCache.js +40 -0
  51. package/src/webCache/WebCache.js +14 -0
  52. package/src/webCache/WebCacheFactory.js +20 -0
@@ -0,0 +1,127 @@
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
+ let chatWid = window.Store.WidFactory.createWid(chatId);
19
+ let chat = await window.Store.Chat.find(chatWid);
20
+ await window.Store.Cmd.openChatAt(chat);
21
+ }, chatId);
22
+ }
23
+
24
+ /**
25
+ * Opens the Chat Drawer
26
+ * @param {string} chatId ID of the chat drawer that will be opened
27
+ */
28
+ async openChatDrawer(chatId) {
29
+ await this.pupPage.evaluate(async chatId => {
30
+ let chat = await window.Store.Chat.get(chatId);
31
+ await window.Store.Cmd.openDrawerMid(chat);
32
+ }, chatId);
33
+ }
34
+
35
+ /**
36
+ * Opens the Chat Search
37
+ * @param {string} chatId ID of the chat search that will be opened
38
+ */
39
+ async openChatSearch(chatId) {
40
+ await this.pupPage.evaluate(async chatId => {
41
+ let chat = await window.Store.Chat.get(chatId);
42
+ await window.Store.Cmd.chatSearch(chat);
43
+ }, chatId);
44
+ }
45
+
46
+ /**
47
+ * Opens or Scrolls the Chat Window to the position of the message
48
+ * @param {string} msgId ID of the message that will be scrolled to
49
+ */
50
+ async openChatWindowAt(msgId) {
51
+ await this.pupPage.evaluate(async msgId => {
52
+ const msg = window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
53
+ let chat = await window.Store.Chat.find(msg.id.remote);
54
+ let searchContext = await window.Store.SearchContext(chat,msg);
55
+ await window.Store.Cmd.openChatAt(chat, searchContext);
56
+ }, msgId);
57
+ }
58
+
59
+ /**
60
+ * Opens the Message Drawer
61
+ * @param {string} msgId ID of the message drawer that will be opened
62
+ */
63
+ async openMessageDrawer(msgId) {
64
+ await this.pupPage.evaluate(async msgId => {
65
+ const msg = window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
66
+ await window.Store.Cmd.msgInfoDrawer(msg);
67
+ }, msgId);
68
+ }
69
+
70
+ /**
71
+ * Closes the Right Drawer
72
+ */
73
+ async closeRightDrawer() {
74
+ await this.pupPage.evaluate(async () => {
75
+ await window.Store.DrawerManager.closeDrawerRight();
76
+ });
77
+ }
78
+
79
+ /**
80
+ * Get all Features
81
+ */
82
+ async getFeatures() {
83
+ return await this.pupPage.evaluate(() => {
84
+ if(!window.Store.Features) throw new Error('This version of Whatsapp Web does not support features');
85
+ return window.Store.Features.F;
86
+ });
87
+ }
88
+
89
+ /**
90
+ * Check if Feature is enabled
91
+ * @param {string} feature status to check
92
+ */
93
+ async checkFeatureStatus(feature) {
94
+ return await this.pupPage.evaluate((feature) => {
95
+ if(!window.Store.Features) throw new Error('This version of Whatsapp Web does not support features');
96
+ return window.Store.Features.supportsFeature(feature);
97
+ }, feature);
98
+ }
99
+
100
+ /**
101
+ * Enable Features
102
+ * @param {string[]} features to be enabled
103
+ */
104
+ async enableFeatures(features) {
105
+ await this.pupPage.evaluate((features) => {
106
+ if(!window.Store.Features) throw new Error('This version of Whatsapp Web does not support features');
107
+ for (const feature in features) {
108
+ window.Store.Features.setFeature(features[feature], true);
109
+ }
110
+ }, features);
111
+ }
112
+
113
+ /**
114
+ * Disable Features
115
+ * @param {string[]} features to be disabled
116
+ */
117
+ async disableFeatures(features) {
118
+ await this.pupPage.evaluate((features) => {
119
+ if(!window.Store.Features) throw new Error('This version of Whatsapp Web does not support features');
120
+ for (const feature in features) {
121
+ window.Store.Features.setFeature(features[feature], false);
122
+ }
123
+ }, features);
124
+ }
125
+ }
126
+
127
+ module.exports = InterfaceController;
@@ -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
+ };