@another-trial/whatsapp-web.js 1.34.0 → 1.34.5-alpha.3
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 +2 -2
- package/CODE_OF_CONDUCT.md +133 -133
- package/LICENSE +201 -201
- package/README.md +155 -185
- package/example.js +706 -690
- package/index.d.ts +2259 -2202
- package/index.js +35 -35
- package/package.json +59 -59
- package/shell.js +36 -36
- package/src/Client.js +2533 -2361
- package/src/authStrategies/BaseAuthStrategy.js +26 -26
- package/src/authStrategies/LocalAuth.js +58 -58
- package/src/authStrategies/NoAuth.js +11 -11
- package/src/authStrategies/RemoteAuth.js +210 -210
- package/src/factories/ChatFactory.js +21 -21
- package/src/factories/ContactFactory.js +15 -15
- package/src/structures/Base.js +21 -21
- package/src/structures/Broadcast.js +69 -69
- package/src/structures/BusinessContact.js +20 -20
- package/src/structures/Buttons.js +81 -81
- package/src/structures/Call.js +75 -75
- package/src/structures/Channel.js +382 -382
- package/src/structures/Chat.js +329 -299
- package/src/structures/ClientInfo.js +70 -70
- package/src/structures/Contact.js +215 -208
- package/src/structures/GroupChat.js +485 -485
- package/src/structures/GroupNotification.js +104 -104
- package/src/structures/Label.js +49 -49
- package/src/structures/List.js +79 -79
- package/src/structures/Location.js +61 -61
- package/src/structures/Message.js +787 -747
- package/src/structures/MessageMedia.js +111 -111
- package/src/structures/Order.js +51 -51
- package/src/structures/Payment.js +79 -79
- package/src/structures/Poll.js +44 -44
- package/src/structures/PollVote.js +75 -75
- package/src/structures/PrivateChat.js +12 -12
- package/src/structures/PrivateContact.js +12 -12
- package/src/structures/Product.js +67 -67
- package/src/structures/ProductMetadata.js +24 -24
- package/src/structures/Reaction.js +68 -68
- package/src/structures/ScheduledEvent.js +71 -71
- package/src/structures/index.js +27 -27
- package/src/util/Constants.js +186 -183
- package/src/util/Injected/AuthStore/AuthStore.js +16 -16
- package/src/util/Injected/AuthStore/LegacyAuthStore.js +21 -21
- package/src/util/Injected/LegacyStore.js +145 -145
- package/src/util/Injected/Store.js +263 -231
- package/src/util/Injected/Utils.js +1221 -1169
- package/src/util/InterfaceController.js +126 -126
- package/src/util/Puppeteer.js +23 -23
- package/src/util/Util.js +186 -186
- package/src/webCache/LocalWebCache.js +40 -40
- package/src/webCache/RemoteWebCache.js +39 -39
- package/src/webCache/WebCache.js +13 -13
- package/src/webCache/WebCacheFactory.js +19 -19
|
@@ -1,126 +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;
|
|
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
|
+
return await this.pupPage.evaluate(async (chatId) => {
|
|
18
|
+
const chat = await window.WWebJS.getChat(chatId, { getAsModel: false });
|
|
19
|
+
return await window.Store.Cmd.openChatBottom({'chat':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;
|
package/src/util/Puppeteer.js
CHANGED
|
@@ -1,23 +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.
|
|
8
|
-
*
|
|
9
|
-
* @param {
|
|
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};
|
|
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 {object} page - Puppeteer Page instance
|
|
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};
|
package/src/util/Util.js
CHANGED
|
@@ -1,186 +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;
|
|
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;
|