mirror of
https://github.com/cheveguerra/whatsapp-web.js.git
synced 2026-04-18 03:29:14 +00:00
* Fix get order. * Fix types. * Add set picture for profile and for groups. * Fix bug. * Fix * Fix types * Fix eslint * Add send reaction feature. * Add send reaction feature. * Add set picture for profile and for groups. * Add send reaction feature. * Add send reaction feature. * Add send reaction feature. * Add send reaction feature. * Add send reaction feature. * Add send reaction feature. * Update src/structures/Reaction.js Co-authored-by: Noah van der Aa <ndvdaa@gmail.com> * Bug fixes. * Bug fixes. * Bug fixes. * Fix * Fix * Fix example * Fix conflict * Fix conflict * Fix conflict * Fix conflict * Fix conflict * Fix conflict * move implementation to message model Co-authored-by: Noah van der Aa <ndvdaa@gmail.com> Co-authored-by: Pedro Lopez <pedroslopez@me.com>
187 lines
5.9 KiB
JavaScript
187 lines
5.9 KiB
JavaScript
'use strict';
|
|
|
|
const path = require('path');
|
|
const Crypto = require('crypto');
|
|
const { tmpdir } = require('os');
|
|
const ffmpeg = require('fluent-ffmpeg');
|
|
const webp = require('node-webpmux');
|
|
const fs = require('fs').promises;
|
|
const has = (o, k) => Object.prototype.hasOwnProperty.call(o, k);
|
|
|
|
/**
|
|
* Utility methods
|
|
*/
|
|
class Util {
|
|
constructor() {
|
|
throw new Error(`The ${this.constructor.name} class may not be instantiated.`);
|
|
}
|
|
|
|
static generateHash(length) {
|
|
var result = '';
|
|
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
var charactersLength = characters.length;
|
|
for (var i = 0; i < length; i++) {
|
|
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Sets default properties on an object that aren't already specified.
|
|
* @param {Object} def Default properties
|
|
* @param {Object} given Object to assign defaults to
|
|
* @returns {Object}
|
|
* @private
|
|
*/
|
|
static mergeDefault(def, given) {
|
|
if (!given) return def;
|
|
for (const key in def) {
|
|
if (!has(given, key) || given[key] === undefined) {
|
|
given[key] = def[key];
|
|
} else if (given[key] === Object(given[key])) {
|
|
given[key] = Util.mergeDefault(def[key], given[key]);
|
|
}
|
|
}
|
|
|
|
return given;
|
|
}
|
|
|
|
/**
|
|
* Formats a image to webp
|
|
* @param {MessageMedia} media
|
|
*
|
|
* @returns {Promise<MessageMedia>} media in webp format
|
|
*/
|
|
static async formatImageToWebpSticker(media, pupPage) {
|
|
if (!media.mimetype.includes('image'))
|
|
throw new Error('media is not a image');
|
|
|
|
if (media.mimetype.includes('webp')) {
|
|
return media;
|
|
}
|
|
|
|
return pupPage.evaluate((media) => {
|
|
return window.WWebJS.toStickerData(media);
|
|
}, media);
|
|
}
|
|
|
|
/**
|
|
* Formats a video to webp
|
|
* @param {MessageMedia} media
|
|
*
|
|
* @returns {Promise<MessageMedia>} media in webp format
|
|
*/
|
|
static async formatVideoToWebpSticker(media) {
|
|
if (!media.mimetype.includes('video'))
|
|
throw new Error('media is not a video');
|
|
|
|
const videoType = media.mimetype.split('/')[1];
|
|
|
|
const tempFile = path.join(
|
|
tmpdir(),
|
|
`${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.webp`
|
|
);
|
|
|
|
const stream = new (require('stream').Readable)();
|
|
const buffer = Buffer.from(
|
|
media.data.replace(`data:${media.mimetype};base64,`, ''),
|
|
'base64'
|
|
);
|
|
stream.push(buffer);
|
|
stream.push(null);
|
|
|
|
await new Promise((resolve, reject) => {
|
|
ffmpeg(stream)
|
|
.inputFormat(videoType)
|
|
.on('error', reject)
|
|
.on('end', () => resolve(true))
|
|
.addOutputOptions([
|
|
'-vcodec',
|
|
'libwebp',
|
|
'-vf',
|
|
// eslint-disable-next-line no-useless-escape
|
|
'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',
|
|
'-loop',
|
|
'0',
|
|
'-ss',
|
|
'00:00:00.0',
|
|
'-t',
|
|
'00:00:05.0',
|
|
'-preset',
|
|
'default',
|
|
'-an',
|
|
'-vsync',
|
|
'0',
|
|
'-s',
|
|
'512:512',
|
|
])
|
|
.toFormat('webp')
|
|
.save(tempFile);
|
|
});
|
|
|
|
const data = await fs.readFile(tempFile, 'base64');
|
|
await fs.unlink(tempFile);
|
|
|
|
return {
|
|
mimetype: 'image/webp',
|
|
data: data,
|
|
filename: media.filename,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Sticker metadata.
|
|
* @typedef {Object} StickerMetadata
|
|
* @property {string} [name]
|
|
* @property {string} [author]
|
|
* @property {string[]} [categories]
|
|
*/
|
|
|
|
/**
|
|
* Formats a media to webp
|
|
* @param {MessageMedia} media
|
|
* @param {StickerMetadata} metadata
|
|
*
|
|
* @returns {Promise<MessageMedia>} media in webp format
|
|
*/
|
|
static async formatToWebpSticker(media, metadata, pupPage) {
|
|
let webpMedia;
|
|
|
|
if (media.mimetype.includes('image'))
|
|
webpMedia = await this.formatImageToWebpSticker(media, pupPage);
|
|
else if (media.mimetype.includes('video'))
|
|
webpMedia = await this.formatVideoToWebpSticker(media);
|
|
else
|
|
throw new Error('Invalid media format');
|
|
|
|
if (metadata.name || metadata.author) {
|
|
const img = new webp.Image();
|
|
const hash = this.generateHash(32);
|
|
const stickerPackId = hash;
|
|
const packname = metadata.name;
|
|
const author = metadata.author;
|
|
const categories = metadata.categories || [''];
|
|
const json = { 'sticker-pack-id': stickerPackId, 'sticker-pack-name': packname, 'sticker-pack-publisher': author, 'emojis': categories };
|
|
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]);
|
|
let jsonBuffer = Buffer.from(JSON.stringify(json), 'utf8');
|
|
let exif = Buffer.concat([exifAttr, jsonBuffer]);
|
|
exif.writeUIntLE(jsonBuffer.length, 14, 4);
|
|
await img.load(Buffer.from(webpMedia.data, 'base64'));
|
|
img.exif = exif;
|
|
webpMedia.data = (await img.save(null)).toString('base64');
|
|
}
|
|
|
|
return webpMedia;
|
|
}
|
|
|
|
/**
|
|
* Configure ffmpeg path
|
|
* @param {string} path
|
|
*/
|
|
static setFfmpegPath(path) {
|
|
ffmpeg.setFfmpegPath(path);
|
|
}
|
|
}
|
|
|
|
module.exports = Util;
|