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.
- package/.env.example +3 -0
- package/CODE_OF_CONDUCT.md +133 -0
- package/LICENSE +201 -0
- package/README.md +185 -0
- package/example.js +634 -0
- package/index.d.ts +1842 -0
- package/index.js +32 -0
- package/package.json +55 -0
- package/shell.js +36 -0
- package/src/Client.js +1747 -0
- package/src/authStrategies/BaseAuthStrategy.js +27 -0
- package/src/authStrategies/LocalAuth.js +56 -0
- package/src/authStrategies/NoAuth.js +12 -0
- package/src/authStrategies/RemoteAuth.js +204 -0
- package/src/factories/ChatFactory.js +16 -0
- package/src/factories/ContactFactory.js +16 -0
- package/src/structures/Base.js +22 -0
- package/src/structures/BusinessContact.js +21 -0
- package/src/structures/Buttons.js +82 -0
- package/src/structures/Call.js +76 -0
- package/src/structures/Chat.js +275 -0
- package/src/structures/ClientInfo.js +71 -0
- package/src/structures/Contact.js +208 -0
- package/src/structures/GroupChat.js +475 -0
- package/src/structures/GroupNotification.js +104 -0
- package/src/structures/Label.js +50 -0
- package/src/structures/List.js +79 -0
- package/src/structures/Location.js +61 -0
- package/src/structures/Message.js +711 -0
- package/src/structures/MessageMedia.js +111 -0
- package/src/structures/Order.js +52 -0
- package/src/structures/Payment.js +79 -0
- package/src/structures/Poll.js +44 -0
- package/src/structures/PollVote.js +61 -0
- package/src/structures/PrivateChat.js +13 -0
- package/src/structures/PrivateContact.js +13 -0
- package/src/structures/Product.js +68 -0
- package/src/structures/ProductMetadata.js +25 -0
- package/src/structures/Reaction.js +69 -0
- package/src/structures/index.js +24 -0
- package/src/util/Constants.js +176 -0
- package/src/util/Injected/AuthStore/AuthStore.js +17 -0
- package/src/util/Injected/AuthStore/LegacyAuthStore.js +22 -0
- package/src/util/Injected/LegacyStore.js +146 -0
- package/src/util/Injected/Store.js +167 -0
- package/src/util/Injected/Utils.js +1017 -0
- package/src/util/InterfaceController.js +127 -0
- package/src/util/Util.js +186 -0
- package/src/webCache/LocalWebCache.js +40 -0
- package/src/webCache/RemoteWebCache.js +40 -0
- package/src/webCache/WebCache.js +14 -0
- 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;
|
package/src/util/Util.js
ADDED
@@ -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
|
+
};
|