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:
parent
dd257b2039
commit
d0642b761c
13 changed files with 1241 additions and 234 deletions
413
src/Client.js
413
src/Client.js
|
|
@ -15,7 +15,7 @@ const { LoadUtils } = require('./util/Injected/Utils');
|
|||
const ChatFactory = require('./factories/ChatFactory');
|
||||
const ContactFactory = require('./factories/ContactFactory');
|
||||
const WebCacheFactory = require('./webCache/WebCacheFactory');
|
||||
const { ClientInfo, Message, MessageMedia, Contact, Location, Poll, PollVote, GroupNotification, Label, Call, Buttons, List, Reaction, Broadcast} = require('./structures');
|
||||
const { Broadcast, Buttons, Call, ClientInfo, Contact, GroupNotification, Label, List, Location, Message, MessageMedia, Poll, PollVote, Reaction } = require('./structures');
|
||||
const NoAuth = require('./authStrategies/NoAuth');
|
||||
const {exposeFunctionIfAbsent} = require('./util/Puppeteer');
|
||||
|
||||
|
|
@ -820,11 +820,9 @@ class Client extends EventEmitter {
|
|||
*
|
||||
*/
|
||||
async sendSeen(chatId) {
|
||||
const result = await this.pupPage.evaluate(async (chatId) => {
|
||||
return await this.pupPage.evaluate(async (chatId) => {
|
||||
return window.WWebJS.sendSeen(chatId);
|
||||
|
||||
}, chatId);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -854,6 +852,7 @@ class Client extends EventEmitter {
|
|||
* @property {string} [stickerName=undefined] - Sets the name of the sticker, (if sendMediaAsSticker is true).
|
||||
* @property {string[]} [stickerCategories=undefined] - Sets the categories of the sticker, (if sendMediaAsSticker is true). Provide emoji char array, can be null.
|
||||
* @property {MessageMedia} [media] - Media to be sent
|
||||
* @property {any} [extra] - Extra options
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -865,6 +864,19 @@ class Client extends EventEmitter {
|
|||
* @returns {Promise<Message>} Message that was just sent
|
||||
*/
|
||||
async sendMessage(chatId, content, options = {}) {
|
||||
const isChannel = /@\w*newsletter\b/.test(chatId);
|
||||
|
||||
if (isChannel && [
|
||||
options.sendMediaAsDocument, options.quotedMessageId,
|
||||
options.parseVCards, options.isViewOnce,
|
||||
content instanceof Location, content instanceof Contact,
|
||||
content instanceof Buttons, content instanceof List,
|
||||
Array.isArray(content) && content.length > 0 && content[0] instanceof Contact
|
||||
].includes(true)) {
|
||||
console.warn('The message type is currently not supported for sending in channels,\nthe supported message types are: text, image, sticker, gif, video, voice and poll.');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (options.mentions) {
|
||||
!Array.isArray(options.mentions) && (options.mentions = [options.mentions]);
|
||||
if (options.mentions.some((possiblyContact) => possiblyContact instanceof Contact)) {
|
||||
|
|
@ -874,7 +886,7 @@ class Client extends EventEmitter {
|
|||
}
|
||||
|
||||
options.groupMentions && !Array.isArray(options.groupMentions) && (options.groupMentions = [options.groupMentions]);
|
||||
|
||||
|
||||
let internalOptions = {
|
||||
linkPreview: options.linkPreview === false ? undefined : true,
|
||||
sendAudioAsVoice: options.sendAudioAsVoice,
|
||||
|
|
@ -890,14 +902,14 @@ class Client extends EventEmitter {
|
|||
extraOptions: options.extra
|
||||
};
|
||||
|
||||
const sendSeen = typeof options.sendSeen === 'undefined' ? true : options.sendSeen;
|
||||
const sendSeen = options.sendSeen !== false;
|
||||
|
||||
if (content instanceof MessageMedia) {
|
||||
internalOptions.attachment = content;
|
||||
internalOptions.media = content;
|
||||
internalOptions.isViewOnce = options.isViewOnce,
|
||||
content = '';
|
||||
} else if (options.media instanceof MessageMedia) {
|
||||
internalOptions.attachment = options.media;
|
||||
internalOptions.media = options.media;
|
||||
internalOptions.caption = content;
|
||||
internalOptions.isViewOnce = options.isViewOnce,
|
||||
content = '';
|
||||
|
|
@ -914,17 +926,19 @@ class Client extends EventEmitter {
|
|||
internalOptions.contactCardList = content.map(contact => contact.id._serialized);
|
||||
content = '';
|
||||
} else if (content instanceof Buttons) {
|
||||
console.warn('Buttons are now deprecated. See more at https://www.youtube.com/watch?v=hv1R1rLeVVE.');
|
||||
if (content.type !== 'chat') { internalOptions.attachment = content.body; }
|
||||
internalOptions.buttons = content;
|
||||
content = '';
|
||||
} else if (content instanceof List) {
|
||||
console.warn('Lists are now deprecated. See more at https://www.youtube.com/watch?v=hv1R1rLeVVE.');
|
||||
internalOptions.list = content;
|
||||
content = '';
|
||||
}
|
||||
|
||||
if (internalOptions.sendMediaAsSticker && internalOptions.attachment) {
|
||||
internalOptions.attachment = await Util.formatToWebpSticker(
|
||||
internalOptions.attachment, {
|
||||
if (internalOptions.sendMediaAsSticker && internalOptions.media) {
|
||||
internalOptions.media = await Util.formatToWebpSticker(
|
||||
internalOptions.media, {
|
||||
name: options.stickerName,
|
||||
author: options.stickerAuthor,
|
||||
categories: options.stickerCategories
|
||||
|
|
@ -932,20 +946,60 @@ class Client extends EventEmitter {
|
|||
);
|
||||
}
|
||||
|
||||
const newMessage = await this.pupPage.evaluate(async (chatId, message, options, sendSeen) => {
|
||||
const chatWid = window.Store.WidFactory.createWid(chatId);
|
||||
const chat = await window.Store.Chat.find(chatWid);
|
||||
const sentMsg = await this.pupPage.evaluate(async (chatId, content, options, sendSeen) => {
|
||||
const chat = await window.WWebJS.getChat(chatId, { getAsModel: false });
|
||||
|
||||
if (!chat) return null;
|
||||
|
||||
if (sendSeen) {
|
||||
await window.WWebJS.sendSeen(chatId);
|
||||
}
|
||||
|
||||
const msg = await window.WWebJS.sendMessage(chat, message, options, sendSeen);
|
||||
return window.WWebJS.getMessageModel(msg);
|
||||
const msg = await window.WWebJS.sendMessage(chat, content, options);
|
||||
return msg
|
||||
? window.WWebJS.getMessageModel(msg)
|
||||
: undefined;
|
||||
}, chatId, content, internalOptions, sendSeen);
|
||||
|
||||
return new Message(this, newMessage);
|
||||
return sentMsg
|
||||
? new Message(this, sentMsg)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} SendChannelAdminInviteOptions
|
||||
* @property {?string} comment The comment to be added to an invitation
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sends a channel admin invitation to a user, allowing them to become an admin of the channel
|
||||
* @param {string} chatId The ID of a user to send the channel admin invitation to
|
||||
* @param {string} channelId The ID of a channel for which the invitation is being sent
|
||||
* @param {SendChannelAdminInviteOptions} options
|
||||
* @returns {Promise<boolean>} Returns true if an invitation was sent successfully, false otherwise
|
||||
*/
|
||||
async sendChannelAdminInvite(chatId, channelId, options = {}) {
|
||||
const response = await this.pupPage.evaluate(async (chatId, channelId, options) => {
|
||||
const channelWid = window.Store.WidFactory.createWid(channelId);
|
||||
const chatWid = window.Store.WidFactory.createWid(chatId);
|
||||
const chat = window.Store.Chat.get(chatWid) || (await window.Store.Chat.find(chatWid));
|
||||
|
||||
if (!chatWid.isUser()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return await window.Store.SendChannelMessage.sendNewsletterAdminInviteMessage(
|
||||
chat,
|
||||
{
|
||||
newsletterWid: channelWid,
|
||||
invitee: chatWid,
|
||||
inviteMessage: options.comment,
|
||||
base64Thumb: await window.WWebJS.getProfilePicThumbToBase64(channelWid)
|
||||
}
|
||||
);
|
||||
}, chatId, channelId, options);
|
||||
|
||||
return response.messageSendResult === 'OK';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -971,7 +1025,7 @@ class Client extends EventEmitter {
|
|||
* @returns {Promise<Array<Chat>>}
|
||||
*/
|
||||
async getChats() {
|
||||
let chats = await this.pupPage.evaluate(async () => {
|
||||
const chats = await this.pupPage.evaluate(async () => {
|
||||
return await window.WWebJS.getChats();
|
||||
});
|
||||
|
||||
|
|
@ -979,16 +1033,51 @@ class Client extends EventEmitter {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get chat instance by ID
|
||||
* Gets all cached {@link Channel} instance
|
||||
* @returns {Promise<Array<Channel>>}
|
||||
*/
|
||||
async getChannels() {
|
||||
const channels = await this.pupPage.evaluate(async () => {
|
||||
return await window.WWebJS.getChannels();
|
||||
});
|
||||
|
||||
return channels.map((channel) => ChatFactory.create(this, channel));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets chat or channel instance by ID
|
||||
* @param {string} chatId
|
||||
* @returns {Promise<Chat>}
|
||||
* @returns {Promise<Chat|Channel>}
|
||||
*/
|
||||
async getChatById(chatId) {
|
||||
let chat = await this.pupPage.evaluate(async chatId => {
|
||||
const chat = await this.pupPage.evaluate(async chatId => {
|
||||
return await window.WWebJS.getChat(chatId);
|
||||
}, chatId);
|
||||
return chat
|
||||
? ChatFactory.create(this, chat)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
return ChatFactory.create(this, chat);
|
||||
/**
|
||||
* Gets a {@link Channel} instance by invite code
|
||||
* @param {string} inviteCode The code that comes after the 'https://whatsapp.com/channel/'
|
||||
* @returns {Promise<Channel>}
|
||||
*/
|
||||
async getChannelByInviteCode(inviteCode) {
|
||||
const channel = await this.pupPage.evaluate(async (inviteCode) => {
|
||||
let channelMetadata;
|
||||
try {
|
||||
channelMetadata = await window.WWebJS.getChannelMetadata(inviteCode);
|
||||
} catch (err) {
|
||||
if (err.name === 'ServerStatusCodeError') return null;
|
||||
throw err;
|
||||
}
|
||||
return await window.WWebJS.getChat(channelMetadata.id);
|
||||
}, inviteCode);
|
||||
|
||||
return channel
|
||||
? ChatFactory.create(this, channel)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1058,6 +1147,61 @@ class Client extends EventEmitter {
|
|||
return res.gid._serialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts a channel admin invitation and promotes the current user to a channel admin
|
||||
* @param {string} channelId The channel ID to accept the admin invitation from
|
||||
* @returns {Promise<boolean>} Returns true if the operation completed successfully, false otherwise
|
||||
*/
|
||||
async acceptChannelAdminInvite(channelId) {
|
||||
return await this.pupPage.evaluate(async (channelId) => {
|
||||
try {
|
||||
await window.Store.ChannelUtils.acceptNewsletterAdminInvite(channelId);
|
||||
return true;
|
||||
} catch (err) {
|
||||
if (err.name === 'ServerStatusCodeError') return false;
|
||||
throw err;
|
||||
}
|
||||
}, channelId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Revokes a channel admin invitation sent to a user by a channel owner
|
||||
* @param {string} channelId The channel ID an invitation belongs to
|
||||
* @param {string} userId The user ID the invitation was sent to
|
||||
* @returns {Promise<boolean>} Returns true if the operation completed successfully, false otherwise
|
||||
*/
|
||||
async revokeChannelAdminInvite(channelId, userId) {
|
||||
return await this.pupPage.evaluate(async (channelId, userId) => {
|
||||
try {
|
||||
const userWid = window.Store.WidFactory.createWid(userId);
|
||||
await window.Store.ChannelUtils.revokeNewsletterAdminInvite(channelId, userWid);
|
||||
return true;
|
||||
} catch (err) {
|
||||
if (err.name === 'ServerStatusCodeError') return false;
|
||||
throw err;
|
||||
}
|
||||
}, channelId, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Demotes a channel admin to a regular subscriber (can be used also for self-demotion)
|
||||
* @param {string} channelId The channel ID to demote an admin in
|
||||
* @param {string} userId The user ID to demote
|
||||
* @returns {Promise<boolean>} Returns true if the operation completed successfully, false otherwise
|
||||
*/
|
||||
async demoteChannelAdmin(channelId, userId) {
|
||||
return await this.pupPage.evaluate(async (channelId, userId) => {
|
||||
try {
|
||||
const userWid = window.Store.WidFactory.createWid(userId);
|
||||
await window.Store.ChannelUtils.demoteNewsletterAdmin(channelId, userWid);
|
||||
return true;
|
||||
} catch (err) {
|
||||
if (err.name === 'ServerStatusCodeError') return false;
|
||||
throw err;
|
||||
}
|
||||
}, channelId, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts a private invitation to join a group
|
||||
* @param {object} inviteInfo Invite V4 Info
|
||||
|
|
@ -1134,7 +1278,7 @@ class Client extends EventEmitter {
|
|||
*/
|
||||
async archiveChat(chatId) {
|
||||
return 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.archiveChat(chat, true);
|
||||
return true;
|
||||
}, chatId);
|
||||
|
|
@ -1146,7 +1290,7 @@ class Client extends EventEmitter {
|
|||
*/
|
||||
async unarchiveChat(chatId) {
|
||||
return 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.archiveChat(chat, false);
|
||||
return false;
|
||||
}, chatId);
|
||||
|
|
@ -1158,7 +1302,7 @@ class Client extends EventEmitter {
|
|||
*/
|
||||
async pinChat(chatId) {
|
||||
return this.pupPage.evaluate(async chatId => {
|
||||
let chat = window.Store.Chat.get(chatId);
|
||||
let chat = await window.WWebJS.getChat(chatId, { getAsModel: false });
|
||||
if (chat.pin) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1181,7 +1325,7 @@ class Client extends EventEmitter {
|
|||
*/
|
||||
async unpinChat(chatId) {
|
||||
return this.pupPage.evaluate(async chatId => {
|
||||
let chat = window.Store.Chat.get(chatId);
|
||||
let chat = await window.WWebJS.getChat(chatId, { getAsModel: false });
|
||||
if (!chat.pin) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1233,7 +1377,7 @@ class Client extends EventEmitter {
|
|||
*/
|
||||
async markChatUnread(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.markChatUnread(chat, true);
|
||||
}, chatId);
|
||||
}
|
||||
|
|
@ -1435,7 +1579,7 @@ class Client extends EventEmitter {
|
|||
for (const participant of createGroupResult.participants) {
|
||||
let isInviteV4Sent = false;
|
||||
const participantId = participant.wid._serialized;
|
||||
const statusCode = participant.error ?? 200;
|
||||
const statusCode = participant.error || 200;
|
||||
|
||||
if (autoSendInviteV4 && statusCode === 403) {
|
||||
window.Store.Contact.gadd(participant.wid, { silent: true });
|
||||
|
|
@ -1474,6 +1618,219 @@ class Client extends EventEmitter {
|
|||
}, title, participants, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* An object that handles the result for {@link createChannel} method
|
||||
* @typedef {Object} CreateChannelResult
|
||||
* @property {string} title A channel title
|
||||
* @property {ChatId} nid An object that handels the newly created channel ID
|
||||
* @property {string} nid.server 'newsletter'
|
||||
* @property {string} nid.user 'XXXXXXXXXX'
|
||||
* @property {string} nid._serialized 'XXXXXXXXXX@newsletter'
|
||||
* @property {string} inviteLink The channel invite link, starts with 'https://whatsapp.com/channel/'
|
||||
* @property {number} createdAtTs The timestamp the channel was created at
|
||||
*/
|
||||
|
||||
/**
|
||||
* Options for the channel creation
|
||||
* @typedef {Object} CreateChannelOptions
|
||||
* @property {?string} description The channel description
|
||||
* @property {?MessageMedia} picture The channel profile picture
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates a new channel
|
||||
* @param {string} title The channel name
|
||||
* @param {CreateChannelOptions} options
|
||||
* @returns {Promise<CreateChannelResult|string>} Returns an object that handles the result for the channel creation or an error message as a string
|
||||
*/
|
||||
async createChannel(title, options = {}) {
|
||||
return await this.pupPage.evaluate(async (title, options) => {
|
||||
let response, { description = null, picture = null } = options;
|
||||
|
||||
if (!window.Store.ChannelUtils.isNewsletterCreationEnabled()) {
|
||||
return 'CreateChannelError: A channel creation is not enabled';
|
||||
}
|
||||
|
||||
if (picture) {
|
||||
picture = await window.WWebJS.cropAndResizeImage(picture, {
|
||||
asDataUrl: true,
|
||||
mimetype: 'image/jpeg',
|
||||
size: 640,
|
||||
quality: 1
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
response = await window.Store.ChannelUtils.createNewsletterQuery({
|
||||
name: title,
|
||||
description: description,
|
||||
picture: picture,
|
||||
});
|
||||
} catch (err) {
|
||||
if (err.name === 'ServerStatusCodeError') {
|
||||
return 'CreateChannelError: An error occupied while creating a channel';
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
|
||||
return {
|
||||
title: title,
|
||||
nid: window.Store.JidToWid.newsletterJidToWid(response.idJid),
|
||||
inviteLink: `https://whatsapp.com/channel/${response.newsletterInviteLinkMetadataMixin.inviteCode}`,
|
||||
createdAtTs: response.newsletterCreationTimeMetadataMixin.creationTimeValue
|
||||
};
|
||||
}, title, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to channel
|
||||
* @param {string} channelId The channel ID
|
||||
* @returns {Promise<boolean>} Returns true if the operation completed successfully, false otherwise
|
||||
*/
|
||||
async subscribeToChannel(channelId) {
|
||||
return await this.pupPage.evaluate(async (channelId) => {
|
||||
return await window.WWebJS.subscribeToUnsubscribeFromChannel(channelId, 'Subscribe');
|
||||
}, channelId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for unsubscribe from a channel
|
||||
* @typedef {Object} UnsubscribeOptions
|
||||
* @property {boolean} [deleteLocalModels = false] If true, after an unsubscription, it will completely remove a channel from the channel collection making it seem like the current user have never interacted with it. Otherwise it will only remove a channel from the list of channels the current user is subscribed to and will set the membership type for that channel to GUEST
|
||||
*/
|
||||
|
||||
/**
|
||||
* Unsubscribe from channel
|
||||
* @param {string} channelId The channel ID
|
||||
* @param {UnsubscribeOptions} options
|
||||
* @returns {Promise<boolean>} Returns true if the operation completed successfully, false otherwise
|
||||
*/
|
||||
async unsubscribeFromChannel(channelId, options) {
|
||||
return await this.pupPage.evaluate(async (channelId, options) => {
|
||||
return await window.WWebJS.subscribeToUnsubscribeFromChannel(channelId, 'Unsubscribe', options);
|
||||
}, channelId, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for transferring a channel ownership to another user
|
||||
* @typedef {Object} TransferChannelOwnershipOptions
|
||||
* @property {boolean} [shouldDismissSelfAsAdmin = false] If true, after the channel ownership is being transferred to another user, the current user will be dismissed as a channel admin and will become to a channel subscriber.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Transfers a channel ownership to another user.
|
||||
* Note: the user you are transferring the channel ownership to must be a channel admin.
|
||||
* @param {string} channelId
|
||||
* @param {string} newOwnerId
|
||||
* @param {TransferChannelOwnershipOptions} options
|
||||
* @returns {Promise<boolean>} Returns true if the operation completed successfully, false otherwise
|
||||
*/
|
||||
async transferChannelOwnership(channelId, newOwnerId, options = {}) {
|
||||
return await this.pupPage.evaluate(async (channelId, newOwnerId, options) => {
|
||||
const channel = await window.WWebJS.getChat(channelId, { getAsModel: false });
|
||||
const newOwner = window.Store.Contact.get(newOwnerId) || (await window.Store.Contact.find(newOwnerId));
|
||||
if (!channel.newsletterMetadata) {
|
||||
await window.Store.NewsletterMetadataCollection.update(channel.id);
|
||||
}
|
||||
|
||||
try {
|
||||
await window.Store.ChannelUtils.changeNewsletterOwnerAction(channel, newOwner);
|
||||
|
||||
if (options.shouldDismissSelfAsAdmin) {
|
||||
const meContact = window.Store.ContactCollection.getMeContact();
|
||||
meContact && (await window.Store.ChannelUtils.demoteNewsletterAdminAction(channel, meContact));
|
||||
}
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}, channelId, newOwnerId, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for channels based on search criteria, there are some notes:
|
||||
* 1. The method finds only channels you are not subscribed to currently
|
||||
* 2. If you have never been subscribed to a found channel
|
||||
* or you have unsubscribed from it with {@link UnsubscribeOptions.deleteLocalModels} set to 'true',
|
||||
* the lastMessage property of a found channel will be 'null'
|
||||
*
|
||||
* @param {Object} searchOptions Search options
|
||||
* @param {string} [searchOptions.searchText = ''] Text to search
|
||||
* @param {Array<string>} [searchOptions.countryCodes = [your local region]] Array of country codes in 'ISO 3166-1 alpha-2' standart (@see https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) to search for channels created in these countries
|
||||
* @param {boolean} [searchOptions.skipSubscribedNewsletters = false] If true, channels that user is subscribed to won't appear in found channels
|
||||
* @param {number} [searchOptions.view = 0] View type, makes sense only when the searchText is empty. Valid values to provide are:
|
||||
* 0 for RECOMMENDED channels
|
||||
* 1 for TRENDING channels
|
||||
* 2 for POPULAR channels
|
||||
* 3 for NEW channels
|
||||
* @param {number} [searchOptions.limit = 50] The limit of found channels to be appear in the returnig result
|
||||
* @returns {Promise<Array<Channel>|[]>} Returns an array of Channel objects or an empty array if no channels were found
|
||||
*/
|
||||
async searchChannels(searchOptions = {}) {
|
||||
return await this.pupPage.evaluate(async ({
|
||||
searchText = '',
|
||||
countryCodes = [window.Store.ChannelUtils.currentRegion],
|
||||
skipSubscribedNewsletters = false,
|
||||
view = 0,
|
||||
limit = 50
|
||||
}) => {
|
||||
searchText = searchText.trim();
|
||||
const currentRegion = window.Store.ChannelUtils.currentRegion;
|
||||
if (![0, 1, 2, 3].includes(view)) view = 0;
|
||||
|
||||
countryCodes = countryCodes.length === 1 && countryCodes[0] === currentRegion
|
||||
? countryCodes
|
||||
: countryCodes.filter((code) => Object.keys(window.Store.ChannelUtils.countryCodesIso).includes(code));
|
||||
|
||||
const viewTypeMapping = {
|
||||
0: 'RECOMMENDED',
|
||||
1: 'TRENDING',
|
||||
2: 'POPULAR',
|
||||
3: 'NEW'
|
||||
};
|
||||
|
||||
searchOptions = {
|
||||
searchText: searchText,
|
||||
countryCodes: countryCodes,
|
||||
skipSubscribedNewsletters: skipSubscribedNewsletters,
|
||||
view: viewTypeMapping[view],
|
||||
categories: [],
|
||||
cursorToken: ''
|
||||
};
|
||||
|
||||
const originalFunction = window.Store.ChannelUtils.getNewsletterDirectoryPageSize;
|
||||
limit !== 50 && (window.Store.ChannelUtils.getNewsletterDirectoryPageSize = () => limit);
|
||||
|
||||
const channels = (await window.Store.ChannelUtils.fetchNewsletterDirectories(searchOptions)).newsletters;
|
||||
|
||||
limit !== 50 && (window.Store.ChannelUtils.getNewsletterDirectoryPageSize = originalFunction);
|
||||
|
||||
return channels
|
||||
? await Promise.all(channels.map((channel) => window.WWebJS.getChatModel(channel, { isChannel: true })))
|
||||
: [];
|
||||
}, searchOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the channel you created
|
||||
* @param {string} channelId The ID of a channel to delete
|
||||
* @returns {Promise<boolean>} Returns true if the operation completed successfully, false otherwise
|
||||
*/
|
||||
async deleteChannel(channelId) {
|
||||
return await this.client.pupPage.evaluate(async (channelId) => {
|
||||
const channel = await window.WWebJS.getChat(channelId, { getAsModel: false });
|
||||
if (!channel) return false;
|
||||
try {
|
||||
await window.Store.ChannelUtils.deleteNewsletterAction(channel);
|
||||
return true;
|
||||
} catch (err) {
|
||||
if (err.name === 'ServerStatusCodeError') return false;
|
||||
throw err;
|
||||
}
|
||||
}, channelId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all current Labels
|
||||
* @returns {Promise<Array<Label>>}
|
||||
|
|
|
|||
Loading…
Reference in a new issue