feat: whatsapp stories implementation (#3994)

* support sending message from status broadcast

* add to index.d.ts

* add method to contact

* new modules

* Update Utils.js

* add for the examples

* add color assertion function

* finishing..

* added waveforms for audio messages and minor fixes

* fix eslint

* refactor

reorganize the store to include additional modules.

* adapt

* the last one.

---------

Co-authored-by: Rajeh Taher <rajeh@reforward.dev>
This commit is contained in:
RendyCraft047 2025-12-13 11:34:55 +07:00 • committed by GitHub
commit 6aa1bd85ab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -22,10 +22,11 @@ exports.LoadUtils = () => {
window.WWebJS.sendMessage = async (chat, content, options = {}) => {
const isChannel = window.Store.ChatGetters.getIsNewsletter(chat);
const isStatus = window.Store.ChatGetters.getIsBroadcast(chat);
let mediaOptions = {};
if (options.media) {
mediaOptions = options.sendMediaAsSticker && !isChannel
mediaOptions = options.sendMediaAsSticker && !isChannel && !isStatus
? await window.WWebJS.processStickerData(options.media)
: await window.WWebJS.processMediaData(options.media, {
forceSticker: options.sendMediaAsSticker,
@ -33,7 +34,8 @@ exports.LoadUtils = () => {
forceVoice: options.sendAudioAsVoice,
forceDocument: options.sendMediaAsDocument,
forceMediaHd: options.sendMediaAsHd,
sendToChannel: isChannel
sendToChannel: isChannel,
sendToStatus: isStatus
});
mediaOptions.caption = options.caption;
content = options.sendMediaAsSticker ? undefined : mediaOptions.preview;
@ -246,6 +248,10 @@ exports.LoadUtils = () => {
participant = window.Store.WidFactory.asUserWidOrThrow(from);
}
if (typeof chat.id?.isStatus === 'function' && chat.id.isStatus()) {
participant = window.Store.WidFactory.asUserWidOrThrow(from);
}
const newMsgKey = new window.Store.MsgKey({
from: from,
to: chat.id,
@ -320,6 +326,37 @@ exports.LoadUtils = () => {
return msg;
}
if (isStatus) {
const { backgroundColor, fontStyle } = extraOptions;
const isMedia = Object.keys(mediaOptions).length > 0;
const mediaUpdate = data => window.Store.MediaUpdate(data, mediaOptions);
const msg = new window.Store.Msg.modelClass({
...message,
author: participant ? participant : null,
messageSecret: window.crypto.getRandomValues(new Uint8Array(32)),
cannotBeRanked: window.Store.StatusUtils.canCheckStatusRankingPosterGating()
});
// for text only
const statusOptions = {
color: backgroundColor && window.WWebJS.assertColor(backgroundColor) || 0xff7acca5,
font: fontStyle >= 0 && fontStyle <= 7 && fontStyle || 0,
text: msg.body
};
await window.Store.StatusUtils[
isMedia ?
'sendStatusMediaMsgAction' : 'sendStatusTextMsgAction'
](
...(
isMedia ?
[msg, mediaUpdate] : [statusOptions]
)
);
return msg;
}
const [msgPromise, sendMsgResultPromise] = window.Store.SendMessage.addAndSendMsgToChat(chat, message);
await msgPromise;
@ -407,7 +444,7 @@ exports.LoadUtils = () => {
return stickerInfo;
};
window.WWebJS.processMediaData = async (mediaInfo, { forceSticker, forceGif, forceVoice, forceDocument, forceMediaHd, sendToChannel }) => {
window.WWebJS.processMediaData = async (mediaInfo, { forceSticker, forceGif, forceVoice, forceDocument, forceMediaHd, sendToChannel, sendToStatus }) => {
const file = window.WWebJS.mediaInfoToFile(mediaInfo);
const opaqueData = await window.Store.OpaqueData.createFromData(file, file.type);
const mediaParams = {
@ -434,7 +471,7 @@ exports.LoadUtils = () => {
throw new Error('media-fault: sendToChat filehash undefined');
}
if (forceVoice && mediaData.type === 'ptt') {
if ((forceVoice && mediaData.type === 'ptt') || (sendToStatus && mediaData.type === 'audio')) {
const waveform = mediaObject.contentInfo.waveform;
mediaData.waveform =
waveform || await window.WWebJS.generateWaveform(file);
@ -463,7 +500,7 @@ exports.LoadUtils = () => {
mimetype: mediaData.mimetype,
mediaObject,
mediaType,
...(sendToChannel ? { calculateToken: window.Store.SendChannelMessage.getRandomFilehash } : {})
...(sendToChannel ? { calculateToken: window.Store.SendChannelMessage.getRandomFilehash() } : {})
};
const uploadedMedia = !sendToChannel
@ -1165,4 +1202,20 @@ exports.LoadUtils = () => {
return { lid, phone };
};
window.WWebJS.assertColor = (hex) => {
let color;
if (typeof hex === 'number') {
color = hex > 0 ? hex : 0xffffffff + parseInt(hex) + 1;
} else if (typeof hex === 'string') {
let number = hex.trim().replace('#', '');
if (number.length <= 6) {
number = 'FF' + number.padStart(6, '0');
}
color = parseInt(number, 16);
} else {
throw 'Invalid hex color';
}
return color;
};
};