From b938792ca8e30ca4780284d68abef34da9557774 Mon Sep 17 00:00:00 2001 From: devlikepro Date: Mon, 15 Jul 2024 16:01:48 +0300 Subject: [PATCH] [core] channels --- Makefile | 4 +- package.json | 4 +- src/api/channels.controller.ts | 119 +++++++++++++++++++ src/core/SwaggerConfiguratorCore.ts | 1 + src/core/abc/session.abc.ts | 54 +++++++++ src/core/app.module.core.ts | 2 + src/core/engines/noweb/session.noweb.core.ts | 106 ++++++++++++++++- src/core/engines/webjs/session.webjs.core.ts | 118 +++++++++++++++++- src/structures/channels.dto.ts | 94 +++++++++++++++ yarn.lock | 18 +-- 10 files changed, 500 insertions(+), 20 deletions(-) create mode 100644 src/api/channels.controller.ts create mode 100644 src/structures/channels.dto.ts diff --git a/Makefile b/Makefile index 51d32d92..1bbcb3c3 100644 --- a/Makefile +++ b/Makefile @@ -32,8 +32,8 @@ for-swagger: WHATSAPP_SWAGGER_CONFIG_ADVANCED=true . ${NVM_DIR}/nvm.sh && nvm exec yarn start update-dependencies: - . ${NVM_DIR}/nvm.sh && nvm exec yarn up whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js#fork-webpack-exodus - . ${NVM_DIR}/nvm.sh && nvm exec yarn up @adiwajshing/baileys@github:devlikeapro/Baileys#fork-master + . ${NVM_DIR}/nvm.sh && nvm exec yarn up whatsapp-web.js@github:devlikeapro/whatsapp-web.js#fork-webpack-exodus-channels + . ${NVM_DIR}/nvm.sh && nvm exec yarn up @adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-channels . ${NVM_DIR}/nvm.sh && nvm exec yarn up venom-bot start-proxy: diff --git a/package.json b/package.json index 3a6c063b..9be41487 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "test:e2e": "jest --config ./test/jest-e2e.json" }, "dependencies": { - "@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master", + "@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master-channels", "@adiwajshing/keyed-db": "^0.2.4", "@nestjs/axios": "^3.0.2", "@nestjs/common": "^9.0.9", @@ -70,7 +70,7 @@ "sharp": "^0.33.4", "swagger-ui-express": "^4.1.4", "venom-bot": "5.0.1", - "whatsapp-web.js": "https://github.com/devlikeapro/whatsapp-web.js#fork-webpack-exodus" + "whatsapp-web.js": "github:devlikeapro/whatsapp-web.js#fork-webpack-exodus-channels" }, "devDependencies": { "@nestjs/cli": "^9.0.0", diff --git a/src/api/channels.controller.ts b/src/api/channels.controller.ts new file mode 100644 index 00000000..75ec1d18 --- /dev/null +++ b/src/api/channels.controller.ts @@ -0,0 +1,119 @@ +import { + Body, + Controller, + Delete, + Get, + Param, + Post, + Query, +} from '@nestjs/common'; +import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger'; +import { + Channel, + CreateChannelRequest, + ListChannelsQuery, + NewsletterIdApiParam, + NewsletterIdOrInviteCodeApiParam, +} from '@waha/structures/channels.dto'; + +import { SessionManager } from '../core/abc/manager.abc'; +import { isNewsletter, WhatsappSession } from '../core/abc/session.abc'; +import { SessionApiParam, SessionParam } from './helpers'; + +@ApiSecurity('api_key') +@Controller('api/:session/channels') +@ApiTags('channels') +export class ChannelsController { + constructor(private manager: SessionManager) {} + + @Get('') + @SessionApiParam + @ApiOperation({ summary: 'Get list of know channels' }) + async list( + @SessionParam session: WhatsappSession, + @Query() query: ListChannelsQuery, + ): Promise { + return session.channelsList(query); + } + + @Post('') + @SessionApiParam + @ApiOperation({ summary: 'Create a new channel.' }) + create( + @SessionParam session: WhatsappSession, + @Body() request: CreateChannelRequest, + ): Promise { + return session.channelsCreateChannel(request); + } + + @Delete(':id') + @SessionApiParam + @NewsletterIdApiParam + @ApiOperation({ summary: 'Delete the channel.' }) + delete(@SessionParam session: WhatsappSession, @Param('id') id: string) { + return session.channelsDeleteChannel(id); + } + + @Get(':id') + @SessionApiParam + @NewsletterIdOrInviteCodeApiParam + @ApiOperation({ + summary: 'Get the channel info - either by id @newsletter OR invite code.', + }) + get( + @SessionParam session: WhatsappSession, + @Param('id') id: string, + ): Promise { + if (isNewsletter(id)) { + return session.channelsGetChannel(id); + } else { + // https://www.whatsapp.com/channel/123 => 123 + const inviteCode = id.split('/').pop(); + return session.channelsGetChannelByInviteCode(inviteCode); + } + } + + @Post('follow') + @SessionApiParam + @NewsletterIdApiParam + @ApiOperation({ summary: 'Follow the channel.' }) + follow( + @SessionParam session: WhatsappSession, + @Param('id') id: string, + ): Promise { + return session.channelsFollowChannel(id); + } + + @Post('unfollow') + @SessionApiParam + @NewsletterIdApiParam + @ApiOperation({ summary: 'Unfollow the channel.' }) + unfollow( + @SessionParam session: WhatsappSession, + @Param('id') id: string, + ): Promise { + return session.channelsUnfollowChannel(id); + } + + @Post('mute') + @SessionApiParam + @NewsletterIdApiParam + @ApiOperation({ summary: 'Mute the channel.' }) + mute( + @SessionParam session: WhatsappSession, + @Param('id') id: string, + ): Promise { + return session.channelsMuteChannel(id); + } + + @Post('unmute') + @SessionApiParam + @NewsletterIdApiParam + @ApiOperation({ summary: 'Unmute the channel.' }) + unmute( + @SessionParam session: WhatsappSession, + @Param('id') id: string, + ): Promise { + return session.channelsUnmuteChannel(id); + } +} diff --git a/src/core/SwaggerConfiguratorCore.ts b/src/core/SwaggerConfiguratorCore.ts index 1906cfd0..d73edb07 100644 --- a/src/core/SwaggerConfiguratorCore.ts +++ b/src/core/SwaggerConfiguratorCore.ts @@ -60,6 +60,7 @@ export class SwaggerConfiguratorCore { .addTag('auth', 'Authentication') .addTag('screenshot', 'Get screenshot of WhatsApp and show QR code') .addTag('chatting', 'Chatting methods') + .addTag('channels', 'Channels (newsletters) methods') .addTag( 'status', 'Status (aka stories) methods. NOWEB engine only!', diff --git a/src/core/abc/session.abc.ts b/src/core/abc/session.abc.ts index b4ba999b..f929279f 100644 --- a/src/core/abc/session.abc.ts +++ b/src/core/abc/session.abc.ts @@ -1,3 +1,8 @@ +import { + Channel, + CreateChannelRequest, + ListChannelsQuery, +} from '@waha/structures/channels.dto'; import { GetChatsQuery } from '@waha/structures/chats.dto'; import { LoggerBuilder } from '@waha/utils/logging'; import { EventEmitter } from 'events'; @@ -437,6 +442,47 @@ export abstract class WhatsappSession { throw new NotImplementedByEngineError(); } + /** + * Channels methods + */ + public channelsList(query: ListChannelsQuery): Promise { + throw new NotImplementedByEngineError(); + } + + public channelsCreateChannel( + request: CreateChannelRequest, + ): Promise { + throw new NotImplementedByEngineError(); + } + + public channelsGetChannel(id: string): Promise { + throw new NotImplementedByEngineError(); + } + + public channelsGetChannelByInviteCode(inviteCode: string): Promise { + throw new NotImplementedByEngineError(); + } + + public channelsDeleteChannel(id: string): Promise { + throw new NotImplementedByEngineError(); + } + + public channelsFollowChannel(id: string): Promise { + throw new NotImplementedByEngineError(); + } + + public channelsUnfollowChannel(id: string): Promise { + throw new NotImplementedByEngineError(); + } + + public channelsMuteChannel(id: string): Promise { + throw new NotImplementedByEngineError(); + } + + public channelsUnmuteChannel(id: string): Promise { + throw new NotImplementedByEngineError(); + } + /** * Status methods */ @@ -502,3 +548,11 @@ export abstract class WhatsappSession { qrcode.generate(qr.raw, { small: true }); } } + +export function isNewsletter(jid: string) { + return jid.endsWith('@newsletter'); +} + +export function getChannelInviteLink(code: string) { + return `https://whatsapp.com/channel/${code}`; +} diff --git a/src/core/app.module.core.ts b/src/core/app.module.core.ts index 221c2529..622cc595 100644 --- a/src/core/app.module.core.ts +++ b/src/core/app.module.core.ts @@ -5,6 +5,7 @@ import { PassportModule } from '@nestjs/passport'; import { ServeStaticModule } from '@nestjs/serve-static'; import { TerminusModule } from '@nestjs/terminus'; import { BufferJsonReplacerInterceptor } from '@waha/api/BufferJsonReplacerInterceptor'; +import { ChannelsController } from '@waha/api/channels.controller'; import { WebsocketGatewayCore } from '@waha/core/api/websocket.gateway.core'; import { getPinoLogLevel, getPinoTransport } from '@waha/utils/logging'; import { LoggerModule } from 'nestjs-pino'; @@ -79,6 +80,7 @@ export const CONTROLLERS = [ SessionController, ChattingController, ChatsController, + ChannelsController, StatusController, ContactsController, GroupsController, diff --git a/src/core/engines/noweb/session.noweb.core.ts b/src/core/engines/noweb/session.noweb.core.ts index 3b666aa6..a2598b34 100644 --- a/src/core/engines/noweb/session.noweb.core.ts +++ b/src/core/engines/noweb/session.noweb.core.ts @@ -5,11 +5,13 @@ import makeWASocket, { extractMessageContent, getAggregateVotesInPollMessage, getKeyAuthor, + getUrlFromDirectPath, isJidGroup, isJidStatusBroadcast, isRealMessage, jidNormalizedUser, makeCacheableSignalKeyStore, + NewsletterMetadata, normalizeMessageContent, PresenceData, proto, @@ -22,6 +24,12 @@ import { UnprocessableEntityException } from '@nestjs/common'; import { NowebInMemoryStore } from '@waha/core/engines/noweb/store/NowebInMemoryStore'; import { flipObject, parseBool, splitAt } from '@waha/helpers'; import { PairingCodeResponse } from '@waha/structures/auth.dto'; +import { + Channel, + ChannelRole, + CreateChannelRequest, + ListChannelsQuery, +} from '@waha/structures/channels.dto'; import { GetChatsQuery } from '@waha/structures/chats.dto'; import { ContactQuery, ContactRequest } from '@waha/structures/contacts.dto'; import { @@ -85,6 +93,8 @@ import { import { IEngineMediaProcessor } from '../../abc/media.abc'; import { ensureSuffix, + getChannelInviteLink, + isNewsletter, WAHAInternalEvent, WhatsappSession, } from '../../abc/session.abc'; @@ -133,6 +143,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession { private startTimeoutId: null | ReturnType = null; private autoRestartTimeoutId: null | ReturnType = null; private msgRetryCounterCache: NodeCache; + protected engineLogger: BaileysLogger; get listenConnectionEventsFromTheStart() { return true; @@ -151,6 +162,10 @@ export class WhatsappSessionNoWebCore extends WhatsappSession { stdTTL: 60 * 60, // 1 hour useClones: false, }); + + this.engineLogger = this.loggerBuilder.child({ + name: 'NOWEBEngine', + }) as unknown as BaileysLogger; } start() { @@ -169,14 +184,11 @@ export class WhatsappSessionNoWebCore extends WhatsappSession { auth: { creds: state.creds, /** caching makes the store faster to send/recv messages */ - keys: makeCacheableSignalKeyStore( - state.keys, - this.logger as unknown as BaileysLogger, - ), + keys: makeCacheableSignalKeyStore(state.keys, this.engineLogger), }, printQRInTerminal: false, browser: browser, - logger: this.logger, + logger: this.engineLogger, mobile: false, defaultQueryTimeoutMs: 120_000, keepAliveIntervalMs: 30_000, @@ -863,6 +875,81 @@ export class WhatsappSessionNoWebCore extends WhatsappSession { } } + /** + * Channels methods + */ + protected toChannel(newsletter: NewsletterMetadata): Channel { + const role = + // @ts-ignore + newsletter.viewer_metadata?.role || + (newsletter.viewer_metadata?.view_role as ChannelRole) || + ChannelRole.GUEST; + const preview = newsletter.preview + ? getUrlFromDirectPath(newsletter.preview) + : null; + const picture = newsletter.picture + ? getUrlFromDirectPath(newsletter.picture) + : null; + return { + id: newsletter.id, + name: newsletter.name, + description: newsletter.description, + invite: getChannelInviteLink(newsletter.invite), + preview: preview, + picture: picture, + verified: newsletter.verification === 'VERIFIED', + role: role, + }; + } + + public async channelsList(query: ListChannelsQuery): Promise { + const newsletters = await this.sock.newsletterSubscribed(); + let channels = newsletters.map(this.toChannel); + if (query.role) { + // @ts-ignore + channels = channels.filter((channel) => channel.role === query.role); + } + return channels; + } + + public async channelsCreateChannel(request: CreateChannelRequest) { + const newsletter = await this.sock.newsletterCreate( + request.name, + request.description, + ); + return this.toChannel(newsletter); + } + + public async channelsGetChannel(id: string) { + const newsletter = await this.sock.newsletterMetadata('jid', id); + return this.toChannel(newsletter); + } + + public async channelsGetChannelByInviteCode(inviteCode: string) { + const newsletter = await this.sock.newsletterMetadata('invite', inviteCode); + return this.toChannel(newsletter); + } + + public async channelsDeleteChannel(id: string) { + return await this.sock.newsletterDelete(id); + } + + public async channelsFollowChannel(id: string): Promise { + return await this.sock.newsletterFollow(id); + } + + public async channelsUnfollowChannel(id: string): Promise { + return await this.sock.newsletterUnfollow(id); + } + + public async channelsMuteChannel(id: string): Promise { + return await this.sock.newsletterMute(id); + } + + public async channelsUnmuteChannel(id: string): Promise { + return await this.sock.newsletterUnmute(id); + } + /** * END - Methods for API */ @@ -1222,6 +1309,9 @@ export class EngineMediaProcessor implements IEngineMediaProcessor { * Convert from 11111111111@s.whatsapp.net to 11111111111@c.us */ function toCusFormat(remoteJid) { + if (!remoteJid) { + return remoteJid; + } if (isJidGroup(remoteJid)) { return remoteJid; } @@ -1231,6 +1321,9 @@ function toCusFormat(remoteJid) { if (isLidUser(remoteJid)) { return remoteJid; } + if (isNewsletter(remoteJid)) { + return remoteJid; + } if (!remoteJid) { return; } @@ -1252,6 +1345,9 @@ export function toJID(chatId) { if (isJidStatusBroadcast(chatId)) { return chatId; } + if (isNewsletter(chatId)) { + return chatId; + } const number = chatId.split('@')[0]; return number + '@s.whatsapp.net'; } diff --git a/src/core/engines/webjs/session.webjs.core.ts b/src/core/engines/webjs/session.webjs.core.ts index 574b557e..21b637ea 100644 --- a/src/core/engines/webjs/session.webjs.core.ts +++ b/src/core/engines/webjs/session.webjs.core.ts @@ -1,6 +1,10 @@ import { UnprocessableEntityException } from '@nestjs/common/exceptions/unprocessable-entity.exception'; import { IEngineMediaProcessor } from '@waha/core/abc/media.abc'; -import { WAHAInternalEvent, WhatsappSession } from '@waha/core/abc/session.abc'; +import { + getChannelInviteLink, + WAHAInternalEvent, + WhatsappSession, +} from '@waha/core/abc/session.abc'; import { WebjsClient } from '@waha/core/engines/webjs/WebjsClient'; import { AvailableInPlusVersion, @@ -8,6 +12,12 @@ import { } from '@waha/core/exceptions'; import { QR } from '@waha/core/QR'; import { parseBool } from '@waha/helpers'; +import { + Channel, + ChannelRole, + CreateChannelRequest, + ListChannelsQuery, +} from '@waha/structures/channels.dto'; import { GetChatsQuery } from '@waha/structures/chats.dto'; import { ChatRequest, @@ -44,6 +54,7 @@ import { WAMessage, WAMessageReaction } from '@waha/structures/responses.dto'; import { MeInfo } from '@waha/structures/sessions.dto'; import { WAMessageRevokedBody } from '@waha/structures/webhooks.dto'; import { + Channel as WEBJSChannel, Chat, ClientOptions, Contact, @@ -260,7 +271,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession { this.whatsapp.on(Events.READY, () => { this.status = WAHASessionStatus.WORKING; this.qr.save(''); - this.logger.info(`Session '${this.name}' has been authenticated!`); + this.logger.info(`Session '${this.name}' is ready!`); }); this.whatsapp.on(Events.AUTHENTICATED, () => { @@ -617,6 +628,109 @@ export class WhatsappSessionWebJSCore extends WhatsappSession { return groupChat.demoteParticipants(participantIds); } + /** + * Channels methods + */ + protected ChatToChannel(chat: WEBJSChannel): Channel { + // @ts-ignore + const metadata = chat.channelMetadata; + let role = metadata.membershipType.toUpperCase(); + if (role === 'VIEWER') { + role = ChannelRole.GUEST; + } + return { + id: chat.id._serialized, + name: chat.name, + description: chat.description, + invite: getChannelInviteLink(metadata.inviteCode), + preview: null, + picture: null, + verified: metadata.verified, + role: role, + }; + } + + protected ChannelMetadataToChannel(metadata: any): Channel { + let role = metadata.membershipType.toUpperCase(); + if (role === 'VIEWER') { + role = ChannelRole.GUEST; + } + return { + id: metadata.id, + name: metadata.titleMetadata.title, + description: metadata.descriptionMetadata.description, + invite: metadata.inviteLink, + preview: metadata.pictureUrl, + picture: metadata.pictureUrl, + verified: metadata.isVerified, + role: role, + }; + } + + public async channelsList(query: ListChannelsQuery): Promise { + const data = await this.whatsapp.getChannels(); + let channels = data.map(this.ChatToChannel); + if (query.role) { + // @ts-ignore + channels = channels.filter((channel) => channel.role === query.role); + } + + const promises = channels.map(async (channel) => + this.whatsapp.getProfilePicUrl(channel.id), + ); + const pictures = await Promise.all(promises); + channels = channels.map((channel, index) => { + channel.picture = pictures[index] || null; + channel.preview = channel.picture; + return channel; + }); + return channels; + } + + public channelsCreateChannel( + request: CreateChannelRequest, + ): Promise { + throw new NotImplementedByEngineError(); + } + + public async channelsGetChannel(id: string): Promise { + return await this.channelsGetChannelByInviteCode(id); + } + + public async channelsGetChannelByInviteCode( + inviteCode: string, + ): Promise { + const metadata = await this.whatsapp.getChannelByInviteCode(inviteCode); + const channel = this.ChannelMetadataToChannel(metadata); + channel.preview = + (await this.whatsapp.getProfilePicUrl(channel.id)) || null; + channel.picture = channel.preview; + return channel; + } + + public channelsDeleteChannel(id: string): Promise { + throw new NotImplementedByEngineError(); + } + + public channelsFollowChannel(id: string): Promise { + throw new NotImplementedByEngineError(); + } + + public channelsUnfollowChannel(id: string): Promise { + throw new NotImplementedByEngineError(); + } + + public channelsMuteChannel(id: string): Promise { + throw new NotImplementedByEngineError(); + } + + public channelsUnmuteChannel(id: string): Promise { + throw new NotImplementedByEngineError(); + } + + /** + * Presences methods + */ public async setPresence(presence: WAHAPresenceStatus, chatId?: string) { let chat: Chat; switch (presence) { diff --git a/src/structures/channels.dto.ts b/src/structures/channels.dto.ts new file mode 100644 index 00000000..e2adfffc --- /dev/null +++ b/src/structures/channels.dto.ts @@ -0,0 +1,94 @@ +import { ApiParam, ApiProperty, getSchemaPath } from '@nestjs/swagger'; +import { BinaryFile, RemoteFile } from '@waha/structures/files.dto'; + +export class CreateChannelRequest { + @ApiProperty({ + example: 'Channel Name', + }) + name: string; + + @ApiProperty({ + example: 'Channel Description', + }) + description?: string; + + @ApiProperty({ + oneOf: [ + { $ref: getSchemaPath(RemoteFile) }, + { $ref: getSchemaPath(BinaryFile) }, + ], + }) + picture?: RemoteFile | BinaryFile; +} + +export enum ChannelRole { + OWNER = 'OWNER', + ADMIN = 'ADMIN', + SUBSCRIBER = 'SUBSCRIBER', + GUEST = 'GUEST', +} + +export enum ChannelRoleFilter { + OWNER = ChannelRole.OWNER, + ADMIN = ChannelRole.ADMIN, + SUBSCRIBER = ChannelRole.SUBSCRIBER, +} + +export class ListChannelsQuery { + role?: ChannelRoleFilter; +} + +export class Channel { + @ApiProperty({ + description: 'Newsletter id', + example: '123123123123@newsletter', + }) + id: string; + + name: string; + + description?: string; + + @ApiProperty({ + description: 'Invite link', + example: 'https://www.whatsapp.com/channel/111111111111111111111111', + }) + invite: string; + + @ApiProperty({ + description: "Preview for channel's picture", + example: 'https://mmg.whatsapp.net/m1/v/t24/An&_nc_cat=10', + }) + preview?: string; + + @ApiProperty({ + description: "Channel's picture", + example: 'https://mmg.whatsapp.net/m1/v/t24/An&_nc_cat=10', + }) + picture?: string; + + verified: boolean; + + role: ChannelRole; +} + +export const NewsletterIdApiParam = ApiParam({ + name: 'id', + required: true, + type: 'string', + schema: { + default: '123123123@newsletter', + }, + description: 'WhatsApp Channel ID', +}); + +export const NewsletterIdOrInviteCodeApiParam = ApiParam({ + name: 'id', + required: true, + type: 'string', + schema: { + default: '123123123@newsletter', + }, + description: + 'WhatsApp Channel ID or invite code from invite link https://www.whatsapp.com/channel/11111', +}); diff --git a/yarn.lock b/yarn.lock index 39f2241c..dec1c328 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,9 +5,9 @@ __metadata: version: 6 cacheKey: 8 -"@adiwajshing/baileys@github:devlikeapro/Baileys#fork-master": +"@adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-channels": version: 6.7.5 - resolution: "@adiwajshing/baileys@https://github.com/devlikeapro/Baileys.git#commit=7da6b067b37bb2dec388bf43f1517ed98e05ee45" + resolution: "@adiwajshing/baileys@https://github.com/devlikeapro/Baileys.git#commit=66fde3f3e5df76b52c6a01b75c301d63e2b6a75f" dependencies: "@adiwajshing/keyed-db": ^0.2.4 "@hapi/boom": ^9.1.3 @@ -39,7 +39,7 @@ __metadata: optional: true sharp: optional: true - checksum: d16ada049d4d74e08f4be479a5a871d96e75e91f5bf614468bd282475662ab4efa208e82e53339cd28e0f4afbbc3add269a491886ba033e6ba8fe838983fe1b5 + checksum: b4f3bc47c559641800e220bddfd06f86848c74dd0c5ea85aba4d1033606a3f90a2d2e488df33da191de75be7357b10df041b2b6d76ee5b7bda1bd37d2586b45c languageName: node linkType: hard @@ -12861,7 +12861,7 @@ __metadata: version: 0.0.0-use.local resolution: "waha@workspace:." dependencies: - "@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master" + "@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master-channels" "@adiwajshing/keyed-db": ^0.2.4 "@nestjs/axios": ^3.0.2 "@nestjs/cli": ^9.0.0 @@ -12930,7 +12930,7 @@ __metadata: tsconfig-paths: ^4.1.0 typescript: ^4.8.4 venom-bot: 5.0.1 - whatsapp-web.js: "https://github.com/devlikeapro/whatsapp-web.js#fork-webpack-exodus" + whatsapp-web.js: "github:devlikeapro/whatsapp-web.js#fork-webpack-exodus-channels" languageName: unknown linkType: soft @@ -13027,9 +13027,9 @@ __metadata: languageName: node linkType: hard -"whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js#fork-webpack-exodus": - version: 1.23.1-alpha.5 - resolution: "whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js.git#commit=72b2a360c1eb5aaa1b7a2d9a2d742d44ba6c0fd5" +"whatsapp-web.js@github:devlikeapro/whatsapp-web.js#fork-webpack-exodus-channels": + version: 1.24.0 + resolution: "whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js.git#commit=035245d3216ff98f231468d9be87253600194d42" dependencies: "@pedroslopez/moduleraid": ^5.0.2 adm-zip: ^0.5.10 @@ -13048,7 +13048,7 @@ __metadata: optional: true unzipper: optional: true - checksum: 6cdd9fb648e2523aea5bca21e10a4133db9faf3b64d96d20fec381275b48cac3729115db442bab20591f7f65b163ae853d569a4fb6c9266db9f388eb1a5a3527 + checksum: 32048fdf11284ca516e2a09801f74ba1e5676cbdfbe4ac7aee7b167e26173aa250cd2593288573e490b474b3c5ebdc20933bd57b3b0b42c1c9299d9fda563923 languageName: node linkType: hard