feat: Channel (#2620)

* window.injectToFunction clarifications

* added createChannel method

* initial Channel class structure

* added getChannelById and getChannels methods

* added 4 methods to change channel metadata

* added deleteChannel method

* added subscribe to/unsubscribe from channel methods

* ecmaVersion updated to 2022

* added searchChannels method

* refactoring of: getChat, getChannel, getChatModel, getChannelModel

* removed Channel.canSend

* sendMessage updated to support channels [p1]

* createChannel: returns string if an error occupied

* added getSubscribers

* added mute/unmute methods

* searchChannels: minor update

* sendMessage updated to support channels [p2]

- added support to send image, sticker, gif and video

* [REFAC] refactored chat retrieving in getChatOrChannel

* [FEAT] added getChannelByInviteCode method

* [CHG] added check if channel creation enabled

* [FEAT] added sendChannelAdminInvitation method

* [WIP] preparations for sending polls [p1]

* [BUG] fixed some internal functions

* [BUG] fixed poll creation

* [COPY] renamed some internal functions

- window.WWebJS.getChatOrChannel renamed back to window.WWebJS.getChat
-window.WWebJS.getChatOrChannelModel renamed back to window.WWebJS.getChatModel

* fix: typo

* fix: send docs without caption

* fix: fixed some internal function

* fix: channel retrieving

* fix: fixed internal getChannelMetadata function

* fix: fixed channel admin can unsubscribe from channel

* feat: channel admin functionality

* feat: added fetchMessages method

Co-authored-by: tuke307 <58882912+tuke307@users.noreply.github.com>

* refactor: revert removing support for Buttons and Lists

Note: buttons and lists are deprecated

* refactor: revert parameter name from 'chatOrChannel' to 'chat'

* fix: support Node v12

* refactor: return 'undefined' in a case of falsy value

* refactor: use already created variable

* docs: fix 'countryCodes' property type

* fix: 'searchChannels' method fixed

* fix: add check in 'setReactionSetting' method

* sendNewsletterMessageJob requires a proper Msg Object in 2.24xx (#10)

Co-authored-by: Sven Neumann <s.neumann@websix.de>

* style: fix broken link in readme file

* fix: only directPath determines whether hasMedia or not

fixes #2952

* fix: get rid of unnecessary 'await'

* feat: add option to send polls in channels

* refactor: get channel instances with 'getChatById'

* chore: fix eslint

* fix: fatal error

* refactor: delete local models by default when unsubscribing

* fix: prevent TypeError if no channel models are returned

* fix: update 'searchChannels' function for WWeb > 2.3000.0

* fix: fix for 'getChannelByInviteCode' function

* fix: fix for 'sendChannelAdminInvite' function

* fix: update 'fetchMessages' for channels

* refactor: update 'setReactionSetting' function

* fix: update 'sendMessage' for channels

* feat: add 'transferChannelOwnership' function

* refactor: add 'deleteChannel' function to 'Client'

* style: fix project code style

* chore: eslint fix

* feat: add 'sendSeen' function

* fix: fix the function restoration after injection

* refactor: remove duplicate module

* fix: get isNewsletter properly

* fix: fix channels media - add mediaHandle

Co-authored-by: devlikepro <155617407+devlikepro@users.noreply.github.com>

---------

Co-authored-by: tuke307 <58882912+tuke307@users.noreply.github.com>
Co-authored-by: Sven <sven2neumann@gmail.com>
Co-authored-by: Sven Neumann <s.neumann@websix.de>
Co-authored-by: devlikepro <155617407+devlikepro@users.noreply.github.com>
This commit is contained in:
alechkos 2025-05-31 21:36:19 +03:00 • committed by GitHub
commit d0642b761c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 1241 additions and 234 deletions

View file

@ -65,7 +65,6 @@ exports.ExposeStore = () => {
window.Store.SendSeen = window.require('WAWebUpdateUnreadChatAction');
window.Store.User = window.require('WAWebUserPrefsMeUser');
window.Store.ContactMethods = window.require('WAWebContactGetters');
window.Store.UploadUtils = window.require('WAWebUploadManager');
window.Store.UserConstructor = window.require('WAWebWid');
window.Store.Validators = window.require('WALinkify');
window.Store.VCard = window.require('WAWebFrontendVcardUtils');
@ -95,22 +94,21 @@ exports.ExposeStore = () => {
window.Store.ReplyUtils = window.require('WAWebMsgReply');
window.Store.BotSecret = window.require('WAWebBotMessageSecret');
window.Store.BotProfiles = window.require('WAWebBotProfileCollection');
window.Store.ContactCollection = window.require('WAWebContactCollection').ContactCollection;
window.Store.DeviceList = window.require('WAWebApiDeviceList');
window.Store.HistorySync = window.require('WAWebSendNonMessageDataRequest');
if (window.compareWwebVersions(window.Debug.VERSION, '>=', '2.3000.1014111620'))
window.Store.AddonReactionTable = window.require('WAWebAddonReactionTableMode').reactionTableMode;
window.Store.AddonReactionTable = window.require('WAWebAddonReactionTableMode').reactionTableMode;
window.Store.ChatGetters = window.require('WAWebChatGetters');
window.Store.Settings = {
...window.require('WAWebUserPrefsGeneral'),
...window.require('WAWebUserPrefsNotifications'),
setPushname: window.require('WAWebSetPushnameConnAction').setPushname
};
window.Store.NumberInfo = {
...window.require('WAPhoneUtils'),
...window.require('WAPhoneFindCC')
};
window.Store.ForwardUtils = {
...window.require('WAWebForwardMessagesToChat')
};
@ -141,6 +139,37 @@ exports.ExposeStore = () => {
...window.require('WAWebApiMembershipApprovalRequestStore'),
...window.require('WASmaxGroupsMembershipRequestsActionRPC')
};
window.Store.ChannelUtils = {
...window.require('WAWebLoadNewsletterPreviewChatAction'),
...window.require('WAWebNewsletterMetadataQueryJob'),
...window.require('WAWebNewsletterCreateQueryJob'),
...window.require('WAWebEditNewsletterMetadataAction'),
...window.require('WAWebNewsletterDeleteAction'),
...window.require('WAWebNewsletterSubscribeAction'),
...window.require('WAWebNewsletterUnsubscribeAction'),
...window.require('WAWebNewsletterDirectorySearchAction'),
...window.require('WAWebNewsletterToggleMuteStateJob'),
...window.require('WAWebNewsletterGatingUtils'),
...window.require('WAWebNewsletterModelUtils'),
...window.require('WAWebMexAcceptNewsletterAdminInviteJob'),
...window.require('WAWebMexRevokeNewsletterAdminInviteJob'),
...window.require('WAWebChangeNewsletterOwnerAction'),
...window.require('WAWebDemoteNewsletterAdminAction'),
...window.require('WAWebNewsletterDemoteAdminJob'),
countryCodesIso: window.require('WAWebCountriesNativeCountryNames'),
currentRegion: window.require('WAWebL10N').getRegion(),
};
window.Store.SendChannelMessage = {
...window.require('WAWebNewsletterUpdateMsgsRecordsJob'),
...window.require('WAWebMsgDataFromModel'),
...window.require('WAWebNewsletterSendMessageJob'),
...window.require('WAWebNewsletterSendMsgAction'),
...window.require('WAMediaCalculateFilehash')
};
window.Store.ChannelSubscribers = {
...window.require('WAWebMexFetchNewsletterSubscribersJob'),
...window.require('WAWebNewsletterSubscriberListAction')
};
window.Store.AddressbookContactUtils = {
...window.require('WAWebSaveContactAction'),
...window.require('WAWebDeleteContactAction')

View file

@ -5,7 +5,7 @@ exports.LoadUtils = () => {
window.WWebJS.forwardMessage = async (chatId, msgId) => {
const msg = window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
let chat = window.Store.Chat.get(chatId);
const chat = await window.WWebJS.getChat(chatId, { getAsModel: false });
if (window.compareWwebVersions(window.Debug.VERSION, '>', '2.3000.0')) {
return window.Store.ForwardUtils.forwardMessagesToChats([msg], [chat], true);
@ -15,50 +15,51 @@ exports.LoadUtils = () => {
};
window.WWebJS.sendSeen = async (chatId) => {
let chat = window.Store.Chat.get(chatId);
if (chat !== undefined) {
await window.Store.SendSeen.sendSeen(chat, false);
const chat = await window.WWebJS.getChat(chatId, { getAsModel: false });
if (chat) {
await window.Store.SendSeen.sendSeen(chat);
return true;
}
return false;
};
window.WWebJS.sendMessage = async (chat, content, options = {}) => {
let attOptions = {};
if (options.attachment) {
attOptions = options.sendMediaAsSticker
? await window.WWebJS.processStickerData(options.attachment)
: await window.WWebJS.processMediaData(options.attachment, {
const isChannel = window.Store.ChatGetters.getIsNewsletter(chat);
let mediaOptions = {};
if (options.media) {
mediaOptions = await window.WWebJS.processMediaData(
options.media, {
forceSticker: options.sendMediaAsSticker,
forceGif: options.sendVideoAsGif,
forceVoice: options.sendAudioAsVoice,
forceDocument: options.sendMediaAsDocument,
forceGif: options.sendVideoAsGif
sendToChannel: isChannel
});
attOptions.caption = options.caption;
content = options.sendMediaAsSticker ? undefined : attOptions.preview;
attOptions.isViewOnce = options.isViewOnce;
delete options.attachment;
mediaOptions.caption = options.caption;
content = options.sendMediaAsSticker ? undefined : mediaOptions.preview;
mediaOptions.isViewOnce = options.isViewOnce;
delete options.media;
delete options.sendMediaAsSticker;
}
let quotedMsgOptions = {};
if (options.quotedMessageId) {
let quotedMessage = await window.Store.Msg.getMessagesById([options.quotedMessageId]);
let quotedMessage = window.Store.Msg.get(options.quotedMessageId);
!quotedMessage && (quotedMessage = (await window.Store.Msg.getMessagesById([options.quotedMessageId]))?.messages?.[0]);
if (quotedMessage['messages'].length == 1) {
quotedMessage = quotedMessage['messages'][0];
// TODO remove .canReply() once all clients are updated to >= v2.2241.6
const canReply = window.Store.ReplyUtils ?
window.Store.ReplyUtils.canReplyMsg(quotedMessage.unsafe()) :
quotedMessage.canReply();
const canReply = window.Store.ReplyUtils
? window.Store.ReplyUtils.canReplyMsg(quotedMessage.unsafe())
: quotedMessage.canReply();
if (canReply) {
quotedMsgOptions = quotedMessage.msgContextInfo(chat);
}
}else{
if(!options.ignoreQuoteErrors) {
} else {
if (!options.ignoreQuoteErrors) {
throw new Error('Could not get the quoted message.');
}
}
@ -106,14 +107,15 @@ exports.LoadUtils = () => {
const { pollName, pollOptions } = options.poll;
const { allowMultipleAnswers, messageSecret } = options.poll.options;
_pollOptions = {
kind: 'pollCreation',
type: 'poll_creation',
pollName: pollName,
pollOptions: pollOptions,
pollSelectableOptionsCount: allowMultipleAnswers ? 0 : 1,
messageSecret:
Array.isArray(messageSecret) && messageSecret.length === 32
? new Uint8Array(messageSecret)
: window.crypto.getRandomValues(new Uint8Array(32))
Array.isArray(messageSecret) && messageSecret.length === 32
? new Uint8Array(messageSecret)
: window.crypto.getRandomValues(new Uint8Array(32))
};
delete options.poll;
}
@ -164,9 +166,9 @@ exports.LoadUtils = () => {
}
}
}
let buttonOptions = {};
if(options.buttons){
if (options.buttons) {
let caption;
if (options.buttons.type === 'chat') {
content = options.buttons.body;
@ -214,18 +216,22 @@ exports.LoadUtils = () => {
delete options.invokedBotWid;
}
let meUser = window.Store.User.getMaybeMeUser();
const lidUser = window.Store.User.getMaybeMeLidUser();
const meUser = window.Store.User.getMaybeMeUser();
const newId = await window.Store.MsgKey.newId();
let from = chat.id.isLid() ? lidUser : meUser;
let participant;
if (chat.id.isGroup() && chat.groupMetadata.isLidAddressingMode) {
meUser = window.Store.User.getMaybeMeLidUser();
if (chat.isGroup) {
from = chat.groupMetadata && chat.groupMetadata.isLidAddressingMode ? lidUser : meUser;
participant = window.Store.WidFactory.toUserWid(from);
}
const newMsgId = new window.Store.MsgKey({
from: meUser,
const newMsgKey = new window.Store.MsgKey({
from: from,
to: chat.id,
id: newId,
participant: chat.id.isGroup() ? meUser : undefined,
participant: participant,
selfDir: 'out',
});
@ -236,7 +242,7 @@ exports.LoadUtils = () => {
const message = {
...options,
id: newMsgId,
id: newMsgKey,
ack: 0,
body: content,
from: meUser,
@ -247,11 +253,11 @@ exports.LoadUtils = () => {
isNewMsg: true,
type: 'chat',
...ephemeralFields,
...mediaOptions,
...(mediaOptions.toJSON ? mediaOptions.toJSON() : {}),
...quotedMsgOptions,
...locationOptions,
..._pollOptions,
...attOptions,
...(attOptions.toJSON ? attOptions.toJSON() : {}),
...quotedMsgOptions,
...vcardOptions,
...buttonOptions,
...listOptions,
@ -264,12 +270,41 @@ exports.LoadUtils = () => {
delete message.canonicalUrl;
}
if (isChannel) {
const msg = new window.Store.Msg.modelClass(message);
const msgDataFromMsgModel = window.Store.SendChannelMessage.msgDataFromMsgModel(msg);
const isMedia = Object.keys(mediaOptions).length > 0;
await window.Store.SendChannelMessage.addNewsletterMsgsRecords([msgDataFromMsgModel]);
chat.msgs.add(msg);
chat.t = msg.t;
const sendChannelMsgResponse = await window.Store.SendChannelMessage.sendNewsletterMessageJob({
msg: msg,
type: message.type === 'chat' ? 'text' : isMedia ? 'media' : 'pollCreation',
newsletterJid: chat.id.toJid(),
...(isMedia
? {
mediaMetadata: msg.avParams(),
mediaHandle: isMedia ? mediaOptions.mediaHandle : null,
}
: {}
)
});
if (sendChannelMsgResponse.success) {
msg.t = sendChannelMsgResponse.ack.t;
msg.serverId = sendChannelMsgResponse.serverId;
}
msg.updateAck(1, true);
await window.Store.SendChannelMessage.updateNewsletterMsgRecord(msg);
return msg;
}
await window.Store.SendMessage.addAndSendMsgToChat(chat, message);
return window.Store.Msg.get(newMsgId._serialized);
return window.Store.Msg.get(newMsgKey._serialized);
};
window.WWebJS.editMessage = async (msg, content, options = {}) => {
const extraOptions = options.extraOptions || {};
delete options.extraOptions;
@ -327,74 +362,51 @@ exports.LoadUtils = () => {
};
};
window.WWebJS.processStickerData = async (mediaInfo) => {
if (mediaInfo.mimetype !== 'image/webp') throw new Error('Invalid media type');
window.WWebJS.processMediaData = async (mediaInfo, { forceSticker, forceGif, forceVoice, forceDocument, sendToChannel }) => {
const file = window.WWebJS.mediaInfoToFile(mediaInfo);
let filehash = await window.WWebJS.getFileHash(file);
let mediaKey = await window.WWebJS.generateHash(32);
const controller = new AbortController();
const uploadedInfo = await window.Store.UploadUtils.encryptAndUpload({
blob: file,
type: 'sticker',
signal: controller.signal,
mediaKey
});
const stickerInfo = {
...uploadedInfo,
clientUrl: uploadedInfo.url,
deprecatedMms3Url: uploadedInfo.url,
uploadhash: uploadedInfo.encFilehash,
size: file.size,
type: 'sticker',
filehash
};
return stickerInfo;
};
window.WWebJS.processMediaData = async (mediaInfo, { forceVoice, forceDocument, forceGif }) => {
const file = window.WWebJS.mediaInfoToFile(mediaInfo);
const mData = await window.Store.OpaqueData.createFromData(file, file.type);
const mediaPrep = window.Store.MediaPrep.prepRawMedia(mData, { asDocument: forceDocument });
const opaqueData = await window.Store.OpaqueData.createFromData(file, file.type);
const mediaPrep = window.Store.MediaPrep.prepRawMedia(
opaqueData, {
asSticker: forceSticker,
asGif: forceGif,
isPtt: forceVoice,
asDocument: forceDocument
});
const mediaData = await mediaPrep.waitForPrep();
const mediaObject = window.Store.MediaObject.getOrCreateMediaObject(mediaData.filehash);
const mediaType = window.Store.MediaTypes.msgToMediaType({
type: mediaData.type,
isGif: mediaData.isGif
isGif: mediaData.isGif,
isNewsletter: sendToChannel,
});
if (forceVoice && mediaData.type === 'audio') {
mediaData.type = 'ptt';
if (forceVoice && mediaData.type === 'ptt') {
const waveform = mediaObject.contentInfo.waveform;
mediaData.waveform =
waveform ?? await window.WWebJS.generateWaveform(file);
}
if (forceGif && mediaData.type === 'video') {
mediaData.isGif = true;
}
if (forceDocument) {
mediaData.type = 'document';
waveform || await window.WWebJS.generateWaveform(file);
}
if (!(mediaData.mediaBlob instanceof window.Store.OpaqueData)) {
mediaData.mediaBlob = await window.Store.OpaqueData.createFromData(mediaData.mediaBlob, mediaData.mediaBlob.type);
mediaData.mediaBlob = await window.Store.OpaqueData.createFromData(
mediaData.mediaBlob,
mediaData.mediaBlob.type
);
}
mediaData.renderableUrl = mediaData.mediaBlob.url();
mediaObject.consolidate(mediaData.toJSON());
mediaData.mediaBlob.autorelease();
const uploadedMedia = await window.Store.MediaUpload.uploadMedia({
const dataToUpload = {
mimetype: mediaData.mimetype,
mediaObject,
mediaType
});
mediaType,
...(sendToChannel ? { calculateToken: window.Store.SendChannelMessage.getRandomFilehash } : {})
};
const uploadedMedia = !sendToChannel
? await window.Store.MediaUpload.uploadMedia(dataToUpload)
: await window.Store.MediaUpload.uploadUnencryptedMedia(dataToUpload);
const mediaEntry = uploadedMedia.mediaEntry;
if (!mediaEntry) {
@ -412,13 +424,14 @@ exports.LoadUtils = () => {
uploadhash: mediaEntry.uploadHash,
size: mediaObject.size,
streamingSidecar: mediaEntry.sidecar,
firstFrameSidecar: mediaEntry.firstFrameSidecar
firstFrameSidecar: mediaEntry.firstFrameSidecar,
mediaHandle: sendToChannel ? mediaEntry.handle : null,
});
return mediaData;
};
window.WWebJS.getMessageModel = message => {
window.WWebJS.getMessageModel = (message) => {
const msg = message.serialize();
msg.isEphemeral = message.isEphemeral;
@ -456,54 +469,115 @@ exports.LoadUtils = () => {
return _vote;
};
window.WWebJS.getChatModel = async chat => {
window.WWebJS.getChat = async (chatId, { getAsModel = true } = {}) => {
const isChannel = /@\w*newsletter\b/.test(chatId);
const chatWid = window.Store.WidFactory.createWid(chatId);
let chat;
let res = chat.serialize();
res.isGroup = false;
res.formattedTitle = chat.formattedTitle;
res.isMuted = chat.mute?.expiration !== 0;
if (chat.groupMetadata) {
res.isGroup = true;
const chatWid = window.Store.WidFactory.createWid((chat.id._serialized));
await window.Store.GroupMetadata.update(chatWid);
chat.groupMetadata.participants._models
.filter(x => x.id._serialized.endsWith('@lid'))
.forEach(x => { x.id = x.contact.phoneNumber; });
res.groupMetadata = chat.groupMetadata.serialize();
res.isReadOnly = chat.groupMetadata.announce;
}
res.lastMessage = null;
if (res.msgs && res.msgs.length) {
const lastMessage = chat.lastReceivedKey
? window.Store.Msg.get(chat.lastReceivedKey._serialized) || (await window.Store.Msg.getMessagesById([chat.lastReceivedKey._serialized]))?.messages?.[0]
: null;
if (lastMessage) {
res.lastMessage = window.WWebJS.getMessageModel(lastMessage);
if (isChannel) {
try {
chat = window.Store.NewsletterCollection.get(chatId);
if (!chat) {
await window.Store.ChannelUtils.loadNewsletterPreviewChat(chatId);
chat = await window.Store.NewsletterCollection.find(chatWid);
}
} catch (err) {
chat = null;
}
} else {
chat = window.Store.Chat.get(chatWid) || (await window.Store.Chat.find(chatWid));
}
delete res.msgs;
delete res.msgUnsyncedButtonReplyMsgs;
delete res.unsyncedButtonReplies;
return res;
return getAsModel && chat
? await window.WWebJS.getChatModel(chat, { isChannel: isChannel })
: chat;
};
window.WWebJS.getChat = async chatId => {
const chatWid = window.Store.WidFactory.createWid(chatId);
const chat = await window.Store.Chat.find(chatWid);
return await window.WWebJS.getChatModel(chat);
window.WWebJS.getChannelMetadata = async (inviteCode) => {
const response =
await window.Store.ChannelUtils.queryNewsletterMetadataByInviteCode(
inviteCode,
window.Store.ChannelUtils.getRoleByIdentifier(inviteCode)
);
const picUrl = response.newsletterPictureMetadataMixin?.picture[0]?.queryPictureDirectPathOrEmptyResponseMixinGroup.value.directPath;
return {
id: response.idJid,
createdAtTs: response.newsletterCreationTimeMetadataMixin.creationTimeValue,
titleMetadata: {
title: response.newsletterNameMetadataMixin.nameElementValue,
updatedAtTs: response.newsletterNameMetadataMixin.nameUpdateTime
},
descriptionMetadata: {
description: response.newsletterDescriptionMetadataMixin.descriptionQueryDescriptionResponseMixin.elementValue,
updatedAtTs: response.newsletterDescriptionMetadataMixin.descriptionQueryDescriptionResponseMixin.updateTime
},
inviteLink: `https://whatsapp.com/channel/${response.newsletterInviteLinkMetadataMixin.inviteCode}`,
membershipType: window.Store.ChannelUtils.getRoleByIdentifier(inviteCode),
stateType: response.newsletterStateMetadataMixin.stateType,
pictureUrl: picUrl ? `https://pps.whatsapp.net${picUrl}` : null,
subscribersCount: response.newsletterSubscribersMetadataMixin.subscribersCount,
isVerified: response.newsletterVerificationMetadataMixin.verificationState === 'verified'
};
};
window.WWebJS.getChats = async () => {
const chats = window.Store.Chat.getModelsArray();
const chatPromises = chats.map(chat => window.WWebJS.getChatModel(chat));
return await Promise.all(chatPromises);
};
window.WWebJS.getChannels = async () => {
const channels = window.Store.NewsletterCollection.getModelsArray();
const channelPromises = channels?.map((channel) => window.WWebJS.getChatModel(channel, { isChannel: true }));
return await Promise.all(channelPromises);
};
window.WWebJS.getChatModel = async (chat, { isChannel = false } = {}) => {
if (!chat) return null;
const model = chat.serialize();
model.isGroup = false;
model.isMuted = chat.mute?.expiration !== 0;
if (isChannel) {
model.isChannel = window.Store.ChatGetters.getIsNewsletter(chat);
} else {
model.formattedTitle = chat.formattedTitle;
}
if (chat.groupMetadata) {
model.isGroup = true;
const chatWid = window.Store.WidFactory.createWid(chat.id._serialized);
await window.Store.GroupMetadata.update(chatWid);
model.groupMetadata.participants._models
.filter(x => x.id._serialized.endsWith('@lid'))
.forEach(x => { x.id = x.contact.phoneNumber; });
model.groupMetadata = chat.groupMetadata.serialize();
model.isReadOnly = chat.groupMetadata.announce;
}
if (chat.newsletterMetadata) {
await window.Store.NewsletterMetadataCollection.update(chat.id);
model.channelMetadata = chat.newsletterMetadata.serialize();
model.channelMetadata.createdAtTs = chat.newsletterMetadata.creationTime;
}
model.lastMessage = null;
if (model.msgs && model.msgs.length) {
const lastMessage = chat.lastReceivedKey
? window.Store.Msg.get(chat.lastReceivedKey._serialized) || (await window.Store.Msg.getMessagesById([chat.lastReceivedKey._serialized]))?.messages?.[0]
: null;
lastMessage && (model.lastMessage = window.WWebJS.getMessageModel(lastMessage));
}
delete model.msgs;
delete model.msgUnsyncedButtonReplyMsgs;
delete model.unsyncedButtonReplies;
return model;
};
window.WWebJS.getContactModel = contact => {
let res = contact.serialize();
res.isBusiness = contact.isBusiness === undefined ? false : contact.isBusiness;
@ -512,50 +586,20 @@ exports.LoadUtils = () => {
res.businessProfile = contact.businessProfile.serialize();
}
// TODO: remove useOldImplementation and its checks once all clients are updated to >= v2.2327.4
const useOldImplementation
= window.compareWwebVersions(window.Debug.VERSION, '<', '2.2327.4');
res.isMe = useOldImplementation
? contact.isMe
: window.Store.ContactMethods.getIsMe(contact);
res.isUser = useOldImplementation
? contact.isUser
: window.Store.ContactMethods.getIsUser(contact);
res.isGroup = useOldImplementation
? contact.isGroup
: window.Store.ContactMethods.getIsGroup(contact);
res.isWAContact = useOldImplementation
? contact.isWAContact
: window.Store.ContactMethods.getIsWAContact(contact);
res.isMyContact = useOldImplementation
? contact.isMyContact
: window.Store.ContactMethods.getIsMyContact(contact);
res.isMe = window.Store.ContactMethods.getIsMe(contact);
res.isUser = window.Store.ContactMethods.getIsUser(contact);
res.isGroup = window.Store.ContactMethods.getIsGroup(contact);
res.isWAContact = window.Store.ContactMethods.getIsWAContact(contact);
res.isMyContact = window.Store.ContactMethods.getIsMyContact(contact);
res.isBlocked = contact.isContactBlocked;
res.userid = useOldImplementation
? contact.userid
: window.Store.ContactMethods.getUserid(contact);
res.isEnterprise = useOldImplementation
? contact.isEnterprise
: window.Store.ContactMethods.getIsEnterprise(contact);
res.verifiedName = useOldImplementation
? contact.verifiedName
: window.Store.ContactMethods.getVerifiedName(contact);
res.verifiedLevel = useOldImplementation
? contact.verifiedLevel
: window.Store.ContactMethods.getVerifiedLevel(contact);
res.statusMute = useOldImplementation
? contact.statusMute
: window.Store.ContactMethods.getStatusMute(contact);
res.name = useOldImplementation
? contact.name
: window.Store.ContactMethods.getName(contact);
res.shortName = useOldImplementation
? contact.shortName
: window.Store.ContactMethods.getShortName(contact);
res.pushname = useOldImplementation
? contact.pushname
: window.Store.ContactMethods.getPushname(contact);
res.userid = window.Store.ContactMethods.getUserid(contact);
res.isEnterprise = window.Store.ContactMethods.getIsEnterprise(contact);
res.verifiedName = window.Store.ContactMethods.getVerifiedName(contact);
res.verifiedLevel = window.Store.ContactMethods.getVerifiedLevel(contact);
res.statusMute = window.Store.ContactMethods.getStatusMute(contact);
res.name = window.Store.ContactMethods.getName(contact);
res.shortName = window.Store.ContactMethods.getShortName(contact);
res.pushname = window.Store.ContactMethods.getPushname(contact);
return res;
};
@ -632,10 +676,6 @@ exports.LoadUtils = () => {
return result;
};
/**
* Referenced from and modified:
* @see https://github.com/wppconnect-team/wa-js/commit/290ebfefe6021b3d17f7fdfdda5545bb0473b26f
*/
window.WWebJS.generateWaveform = async (audioFile) => {
try {
const audioData = await audioFile.arrayBuffer();
@ -669,7 +709,7 @@ exports.LoadUtils = () => {
};
window.WWebJS.sendClearChat = async (chatId) => {
let chat = window.Store.Chat.get(chatId);
let chat = await window.WWebJS.getChat(chatId, { getAsModel: false });
if (chat !== undefined) {
await window.Store.SendClear.sendClear(chat, false);
return true;
@ -678,7 +718,7 @@ exports.LoadUtils = () => {
};
window.WWebJS.sendDeleteChat = async (chatId) => {
let chat = window.Store.Chat.get(chatId);
let chat = await window.WWebJS.getChat(chatId, { getAsModel: false });
if (chat !== undefined) {
await window.Store.SendDelete.sendDelete(chat);
return true;
@ -908,7 +948,7 @@ exports.LoadUtils = () => {
}
if (rpcResult.name === 'AddParticipantsResponseSuccess') {
const code = resultArgs?.value.error ?? '200';
const code = resultArgs?.value.error || '200';
data.name = resultArgs?.name;
data.code = +code;
data.inviteV4Code = resultArgs?.value.addRequestCode;
@ -1023,6 +1063,25 @@ exports.LoadUtils = () => {
}
};
window.WWebJS.subscribeToUnsubscribeFromChannel = async (channelId, action, options = {}) => {
const channel = await window.WWebJS.getChat(channelId, { getAsModel: false });
if (!channel || channel.newsletterMetadata.membershipType === 'owner') return false;
options = { eventSurface: 3, deleteLocalModels: options.deleteLocalModels ?? true };
try {
if (action === 'Subscribe') {
await window.Store.ChannelUtils.subscribeToNewsletterAction(channel, options);
} else if (action === 'Unsubscribe') {
await window.Store.ChannelUtils.unsubscribeFromNewsletterAction(channel, options);
} else return false;
return true;
} catch (err) {
if (err.name === 'ServerStatusCodeError') return false;
throw err;
}
};
window.WWebJS.pinUnpinMsgAction = async (msgId, action, duration) => {
const message = window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
if (!message) return false;

View file

@ -14,9 +14,8 @@ class InterfaceController {
* @param {string} chatId ID of the chat window that will be opened
*/
async openChatWindow(chatId) {
await this.pupPage.evaluate(async chatId => {
const chatWid = window.Store.WidFactory.createWid(chatId);
const chat = window.Store.Chat.get(chatWid) || await window.Store.Chat.find(chatWid);
await this.pupPage.evaluate(async (chatId) => {
const chat = await window.WWebJS.getChat(chatId, { getAsModel: false });
await window.Store.Cmd.openChatBottom(chat);
}, chatId);
}
@ -27,7 +26,7 @@ class InterfaceController {
*/
async openChatDrawer(chatId) {
await this.pupPage.evaluate(async chatId => {
let chat = await window.Store.Chat.get(chatId);
let chat = await window.WWebJS.getChat(chatId, { getAsModel: false });
await window.Store.Cmd.openDrawerMid(chat);
}, chatId);
}
@ -38,7 +37,7 @@ class InterfaceController {
*/
async openChatSearch(chatId) {
await this.pupPage.evaluate(async chatId => {
let chat = await window.Store.Chat.get(chatId);
let chat = await window.WWebJS.getChat(chatId, { getAsModel: false });
await window.Store.Cmd.chatSearch(chat);
}, chatId);
}