From 72985aff53f88d01579433e6cb648da514679dfc Mon Sep 17 00:00:00 2001 From: Luiz Felipe Stangarlin Date: Mon, 31 Jul 2023 16:17:34 -0500 Subject: [PATCH 1/2] rename --- src/api/class/{instance.js => instance.ts} | 0 src/api/class/{session.js => session.ts} | 0 src/api/controllers/{group.controller.js => group.controller.ts} | 0 .../{instance.controller.js => instance.controller.ts} | 0 .../controllers/{message.controller.js => message.controller.ts} | 0 src/api/controllers/{misc.controller.js => misc.controller.ts} | 0 src/api/errors/{api.error.js => api.error.ts} | 0 src/api/errors/{extendable.error.js => extendable.error.ts} | 0 src/api/helper/{connectMongoClient.js => connectMongoClient.ts} | 0 src/api/helper/{downloadMsg.js => downloadMsg.ts} | 0 src/api/helper/{genVc.js => genVc.ts} | 0 src/api/helper/{mongoAuthState.js => mongoAuthState.ts} | 0 src/api/helper/{processbtn.js => processbtn.ts} | 0 src/api/helper/{sleep.js => sleep.ts} | 0 src/api/middlewares/{error.js => error.ts} | 0 src/api/middlewares/{keyCheck.js => keyCheck.ts} | 0 src/api/middlewares/{loginCheck.js => loginCheck.ts} | 0 src/api/middlewares/{tokenCheck.js => tokenCheck.ts} | 0 src/api/models/{chat.model.js => chat.model.ts} | 0 src/api/routes/{group.route.js => group.route.ts} | 0 src/api/routes/{index.js => index.ts} | 0 src/api/routes/{instance.route.js => instance.route.ts} | 0 src/api/routes/{message.route.js => message.route.ts} | 0 src/api/routes/{misc.route.js => misc.route.ts} | 0 src/config/{config.js => config.ts} | 0 src/config/{express.js => express.ts} | 0 src/{server.js => server.ts} | 0 27 files changed, 0 insertions(+), 0 deletions(-) rename src/api/class/{instance.js => instance.ts} (100%) rename src/api/class/{session.js => session.ts} (100%) rename src/api/controllers/{group.controller.js => group.controller.ts} (100%) rename src/api/controllers/{instance.controller.js => instance.controller.ts} (100%) rename src/api/controllers/{message.controller.js => message.controller.ts} (100%) rename src/api/controllers/{misc.controller.js => misc.controller.ts} (100%) rename src/api/errors/{api.error.js => api.error.ts} (100%) rename src/api/errors/{extendable.error.js => extendable.error.ts} (100%) rename src/api/helper/{connectMongoClient.js => connectMongoClient.ts} (100%) rename src/api/helper/{downloadMsg.js => downloadMsg.ts} (100%) rename src/api/helper/{genVc.js => genVc.ts} (100%) rename src/api/helper/{mongoAuthState.js => mongoAuthState.ts} (100%) rename src/api/helper/{processbtn.js => processbtn.ts} (100%) rename src/api/helper/{sleep.js => sleep.ts} (100%) rename src/api/middlewares/{error.js => error.ts} (100%) rename src/api/middlewares/{keyCheck.js => keyCheck.ts} (100%) rename src/api/middlewares/{loginCheck.js => loginCheck.ts} (100%) rename src/api/middlewares/{tokenCheck.js => tokenCheck.ts} (100%) rename src/api/models/{chat.model.js => chat.model.ts} (100%) rename src/api/routes/{group.route.js => group.route.ts} (100%) rename src/api/routes/{index.js => index.ts} (100%) rename src/api/routes/{instance.route.js => instance.route.ts} (100%) rename src/api/routes/{message.route.js => message.route.ts} (100%) rename src/api/routes/{misc.route.js => misc.route.ts} (100%) rename src/config/{config.js => config.ts} (100%) rename src/config/{express.js => express.ts} (100%) rename src/{server.js => server.ts} (100%) diff --git a/src/api/class/instance.js b/src/api/class/instance.ts similarity index 100% rename from src/api/class/instance.js rename to src/api/class/instance.ts diff --git a/src/api/class/session.js b/src/api/class/session.ts similarity index 100% rename from src/api/class/session.js rename to src/api/class/session.ts diff --git a/src/api/controllers/group.controller.js b/src/api/controllers/group.controller.ts similarity index 100% rename from src/api/controllers/group.controller.js rename to src/api/controllers/group.controller.ts diff --git a/src/api/controllers/instance.controller.js b/src/api/controllers/instance.controller.ts similarity index 100% rename from src/api/controllers/instance.controller.js rename to src/api/controllers/instance.controller.ts diff --git a/src/api/controllers/message.controller.js b/src/api/controllers/message.controller.ts similarity index 100% rename from src/api/controllers/message.controller.js rename to src/api/controllers/message.controller.ts diff --git a/src/api/controllers/misc.controller.js b/src/api/controllers/misc.controller.ts similarity index 100% rename from src/api/controllers/misc.controller.js rename to src/api/controllers/misc.controller.ts diff --git a/src/api/errors/api.error.js b/src/api/errors/api.error.ts similarity index 100% rename from src/api/errors/api.error.js rename to src/api/errors/api.error.ts diff --git a/src/api/errors/extendable.error.js b/src/api/errors/extendable.error.ts similarity index 100% rename from src/api/errors/extendable.error.js rename to src/api/errors/extendable.error.ts diff --git a/src/api/helper/connectMongoClient.js b/src/api/helper/connectMongoClient.ts similarity index 100% rename from src/api/helper/connectMongoClient.js rename to src/api/helper/connectMongoClient.ts diff --git a/src/api/helper/downloadMsg.js b/src/api/helper/downloadMsg.ts similarity index 100% rename from src/api/helper/downloadMsg.js rename to src/api/helper/downloadMsg.ts diff --git a/src/api/helper/genVc.js b/src/api/helper/genVc.ts similarity index 100% rename from src/api/helper/genVc.js rename to src/api/helper/genVc.ts diff --git a/src/api/helper/mongoAuthState.js b/src/api/helper/mongoAuthState.ts similarity index 100% rename from src/api/helper/mongoAuthState.js rename to src/api/helper/mongoAuthState.ts diff --git a/src/api/helper/processbtn.js b/src/api/helper/processbtn.ts similarity index 100% rename from src/api/helper/processbtn.js rename to src/api/helper/processbtn.ts diff --git a/src/api/helper/sleep.js b/src/api/helper/sleep.ts similarity index 100% rename from src/api/helper/sleep.js rename to src/api/helper/sleep.ts diff --git a/src/api/middlewares/error.js b/src/api/middlewares/error.ts similarity index 100% rename from src/api/middlewares/error.js rename to src/api/middlewares/error.ts diff --git a/src/api/middlewares/keyCheck.js b/src/api/middlewares/keyCheck.ts similarity index 100% rename from src/api/middlewares/keyCheck.js rename to src/api/middlewares/keyCheck.ts diff --git a/src/api/middlewares/loginCheck.js b/src/api/middlewares/loginCheck.ts similarity index 100% rename from src/api/middlewares/loginCheck.js rename to src/api/middlewares/loginCheck.ts diff --git a/src/api/middlewares/tokenCheck.js b/src/api/middlewares/tokenCheck.ts similarity index 100% rename from src/api/middlewares/tokenCheck.js rename to src/api/middlewares/tokenCheck.ts diff --git a/src/api/models/chat.model.js b/src/api/models/chat.model.ts similarity index 100% rename from src/api/models/chat.model.js rename to src/api/models/chat.model.ts diff --git a/src/api/routes/group.route.js b/src/api/routes/group.route.ts similarity index 100% rename from src/api/routes/group.route.js rename to src/api/routes/group.route.ts diff --git a/src/api/routes/index.js b/src/api/routes/index.ts similarity index 100% rename from src/api/routes/index.js rename to src/api/routes/index.ts diff --git a/src/api/routes/instance.route.js b/src/api/routes/instance.route.ts similarity index 100% rename from src/api/routes/instance.route.js rename to src/api/routes/instance.route.ts diff --git a/src/api/routes/message.route.js b/src/api/routes/message.route.ts similarity index 100% rename from src/api/routes/message.route.js rename to src/api/routes/message.route.ts diff --git a/src/api/routes/misc.route.js b/src/api/routes/misc.route.ts similarity index 100% rename from src/api/routes/misc.route.js rename to src/api/routes/misc.route.ts diff --git a/src/config/config.js b/src/config/config.ts similarity index 100% rename from src/config/config.js rename to src/config/config.ts diff --git a/src/config/express.js b/src/config/express.ts similarity index 100% rename from src/config/express.js rename to src/config/express.ts diff --git a/src/server.js b/src/server.ts similarity index 100% rename from src/server.js rename to src/server.ts From c72fb38a827b16cf7b160ea18937acfea2d0d154 Mon Sep 17 00:00:00 2001 From: Luiz Felipe Stangarlin Date: Mon, 31 Jul 2023 15:42:00 -0500 Subject: [PATCH 2/2] typescript support --- .eslintrc.json | 2 +- package.json | 11 +- src/api/class/instance.ts | 381 +++++++++++++-------- src/api/class/session.ts | 33 +- src/api/controllers/group.controller.ts | 67 ++-- src/api/controllers/instance.controller.ts | 59 ++-- src/api/controllers/message.controller.ts | 55 +-- src/api/controllers/misc.controller.ts | 39 ++- src/api/errors/api.error.ts | 6 +- src/api/errors/extendable.error.ts | 7 +- src/api/helper/connectMongoClient.ts | 22 +- src/api/helper/downloadMsg.ts | 7 +- src/api/helper/genVc.ts | 9 +- src/api/helper/mongoAuthState.ts | 38 +- src/api/helper/processbtn.ts | 19 +- src/api/helper/processmessage.ts | 45 +++ src/api/helper/sleep.ts | 3 +- src/api/helper/types.ts | 11 + src/api/middlewares/error.ts | 13 +- src/api/middlewares/keyCheck.ts | 9 +- src/api/middlewares/loginCheck.ts | 9 +- src/api/middlewares/tokenCheck.ts | 7 +- src/api/models/chat.model.ts | 41 ++- src/api/routes/group.route.ts | 52 +-- src/api/routes/index.ts | 14 +- src/api/routes/instance.route.ts | 13 +- src/api/routes/message.route.ts | 16 +- src/api/routes/misc.route.ts | 19 +- src/api/service/database.ts | 23 ++ src/api/service/instance.ts | 20 ++ src/config/config.ts | 7 +- src/config/express-exception-handler.d.ts | 2 + src/config/express.ts | 25 +- src/server.ts | 29 +- tsconfig.json | 17 + yarn.lock | 299 ++++++++++++++-- 36 files changed, 989 insertions(+), 440 deletions(-) create mode 100644 src/api/helper/processmessage.ts create mode 100644 src/api/helper/types.ts create mode 100644 src/api/service/database.ts create mode 100644 src/api/service/instance.ts create mode 100644 src/config/express-exception-handler.d.ts create mode 100644 tsconfig.json diff --git a/.eslintrc.json b/.eslintrc.json index 60c32107..ed4b4276 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -18,5 +18,5 @@ "WhatsAppInstances": true, "describe": true, "it": true - } + } } diff --git a/package.json b/package.json index ee1b67dc..566be569 100644 --- a/package.json +++ b/package.json @@ -32,8 +32,9 @@ "author": "Mohd Salman Ansari ", "license": "MIT", "dependencies": { - "@whiskeysockets/baileys": "^6.1.0", "@adiwajshing/keyed-db": "^0.2.4", + "@hapi/boom": "^10.0.1", + "@whiskeysockets/baileys": "^6.4.0", "axios": "^1.1.3", "dotenv": "^16.0.3", "ejs": "^3.1.7", @@ -49,6 +50,11 @@ "uuid": "^9.0.0" }, "devDependencies": { + "@types/express": "^4.17.17", + "@types/multer": "^1.4.7", + "@types/qrcode": "^1.5.1", + "@types/uuid": "^9.0.2", + "@types/ws": "^8.5.5", "eslint": "^8.28.0", "eslint-config-prettier": "^8.5.0", "husky": "^8.0.2", @@ -56,6 +62,7 @@ "mocha": "^10.1.0", "nodemon": "^2.0.20", "prettier": "^2.8.0", - "supertest": "^6.3.1" + "supertest": "^6.3.1", + "typescript": "^5.1.6" } } diff --git a/src/api/class/instance.ts b/src/api/class/instance.ts index af957ba4..e2b579d0 100644 --- a/src/api/class/instance.ts +++ b/src/api/class/instance.ts @@ -1,23 +1,67 @@ /* eslint-disable no-unsafe-optional-chaining */ -const QRCode = require('qrcode') -const pino = require('pino') -const { - default: makeWASocket, - DisconnectReason, -} = require('@whiskeysockets/baileys') -const { unlinkSync } = require('fs') -const { v4: uuidv4 } = require('uuid') -const path = require('path') -const processButton = require('../helper/processbtn') -const generateVC = require('../helper/genVc') -const Chat = require('../models/chat.model') -const axios = require('axios') -const config = require('../../config/config') -const downloadMessage = require('../helper/downloadMsg') -const logger = require('pino')() -const useMongoDBAuthState = require('../helper/mongoAuthState') +import QRCode from 'qrcode' +import pino from 'pino' +import { Boom } from '@hapi/boom' +import { DisconnectReason, GroupMetadata, GroupParticipant, WAMessage, default as makeWASocket, proto } from '@whiskeysockets/baileys' +import { v4 as uuidv4 } from 'uuid' +import processButton, { ButtonDef } from '../helper/processbtn' +import generateVC, { VCardData } from '../helper/genVc' +import Chat, { ChatType } from '../models/chat.model' +import axios from 'axios' +import config from '../../config/config' +import downloadMessage from '../helper/downloadMsg' +import useMongoDBAuthState, { AuthState } from '../helper/mongoAuthState' +import { AppType, FileType, TypeOfPromise } from '../helper/types' +import getDatabase, { CollectionType } from '../service/database' +import processMessage, { MediaType } from '../helper/processmessage' + +const logger = pino() + +type WASocket = ReturnType + +// The following types are used instead of the ones from whiskeysockets because that's the public interface. +// except on the ByApp calls that are `private`, supposedly. + +export type Lock = 'block' | 'unblock' + +export type Status = 'unavailable' | 'available' | 'composing' | 'recording' | 'paused' + +export type ParticipantAction = 'add' | 'remove' | 'promote' | 'demote' + +export type GroupAction = 'announcement' | 'locked' | 'not_announcement' | 'unlocked' + +export interface ButtonMessage { + buttons: ButtonDef[] + text?: string + footerText?: string +} + +export interface ListMessage { + text: string + sections?: any + buttonText?: string + description?: string + title?: string +} + +export interface MediaButtonMessage { + mediaType: MediaType + image?: string + footerText?: string + text?: string + buttons?: ButtonDef[] + mimeType: string +} + +export interface MessageKey { + remoteJid?: (string|null); + fromMe?: (boolean|null); + id?: (string|null); + participant?: (string|null); +} class WhatsAppInstance { + app: AppType socketConfig = { defaultQueryTimeoutMs: undefined, printQRInTerminal: false, @@ -26,29 +70,34 @@ class WhatsAppInstance { }), } key = '' - authState - allowWebhook = undefined + authState: AuthState | null = null + collection: CollectionType | null = null + allowWebhook: boolean | null = null webhook = undefined instance = { key: this.key, - chats: [], + chats: [], qr: '', - messages: [], + messages: [], qrRetry: 0, customWebhook: '', + sock: null, + online: false, } axiosInstance = axios.create({ baseURL: config.webhookUrl, }) - constructor(key, allowWebhook, webhook) { + + constructor(app: AppType, key?: string, allowWebhook?: boolean, webhook?: any) { + this.app = app; this.key = key ? key : uuidv4() this.instance.customWebhook = this.webhook ? this.webhook : webhook this.allowWebhook = config.webhookEnabled ? config.webhookEnabled - : allowWebhook + : (allowWebhook ?? null) if (this.allowWebhook && this.instance.customWebhook !== null) { this.allowWebhook = true this.instance.customWebhook = webhook @@ -58,7 +107,7 @@ class WhatsAppInstance { } } - async SendWebhook(type, body, key) { + async SendWebhook(type: string, body: any, key: string) { if (!this.allowWebhook) return this.axiosInstance .post('', { @@ -70,20 +119,57 @@ class WhatsAppInstance { } async init() { - this.collection = mongoClient.db('whatsapp-api').collection(this.key) + const db = getDatabase(this.app) + this.collection = db.collection(this.key) const { state, saveCreds } = await useMongoDBAuthState(this.collection) this.authState = { state: state, saveCreds: saveCreds } - this.socketConfig.auth = this.authState.state - this.socketConfig.browser = Object.values(config.browser) - this.instance.sock = makeWASocket(this.socketConfig) + const socketConfig = { + auth: this.authState.state, + browser: <[string, string, string]> Object.values(config.browser), + ...this.socketConfig + } + this.instance.sock = makeWASocket(socketConfig) this.setHandler() return this } setHandler() { const sock = this.instance.sock + + const baileysEvents = [ + 'creds.update', + 'messaging-history.set', + 'chats.upsert', + 'chats.update', + 'chats.delete', + 'presence.update', + // 'contacts.upsert', + // 'contacts.update', + 'messages.delete', + // 'messages.update', + // 'messages.media-update', + 'messages.upsert', + // 'messages.reaction', + // 'message-receipt.update', + 'groups.upsert', + 'groups.update', + 'group-participants.update', + // 'blocklist.set', + // 'blocklist.update', + // 'call', + // 'labels.edit', + // 'labels.association', + ]; + // on credentials update save state - sock?.ev.on('creds.update', this.authState.saveCreds) + sock?.ev.on('creds.update', async (creds) => { + if (this.authState?.state) + this.authState.state.creds = { + ...this.authState.state.creds, + ...creds + } + this.authState?.saveCreds() + }) // on socket closed, opened, connecting sock?.ev.on('connection.update', async (update) => { @@ -94,12 +180,11 @@ class WhatsAppInstance { if (connection === 'close') { // reconnect if not logged out if ( - lastDisconnect?.error?.output?.statusCode !== - DisconnectReason.loggedOut + (lastDisconnect?.error as Boom)?.output?.statusCode !== DisconnectReason.loggedOut ) { await this.init() } else { - await this.collection.drop().then((r) => { + await this.collection?.drop().then((r: any) => { logger.info('STATE: Droped collection') }) this.instance.online = false @@ -152,11 +237,12 @@ class WhatsAppInstance { QRCode.toDataURL(qr).then((url) => { this.instance.qr = url this.instance.qrRetry++ - if (this.instance.qrRetry >= config.instance.maxRetryQr) { + if (this.instance.qrRetry >= Number(config.instance.maxRetryQr)) { // close WebSocket connection - this.instance.sock.ws.close() + this.instance.sock?.ws.close() // remove all events - this.instance.sock.ev.removeAllListeners() + baileysEvents.forEach(ev => this.instance.sock?.ev.removeAllListeners( ev)) + // terminated this.instance.qr = ' ' logger.info('socket connection terminated') } @@ -175,20 +261,20 @@ class WhatsAppInstance { }) // on receive all chats - sock?.ev.on('chats.set', async ({ chats }) => { + sock?.ev.on('messaging-history.set', async ({ chats }) => { this.instance.chats = [] - const recivedChats = chats.map((chat) => { + const receivedChats = chats.map((chat: any) => { return { ...chat, messages: [], } }) - this.instance.chats.push(...recivedChats) + this.instance.chats.push(...receivedChats) await this.updateDb(this.instance.chats) await this.updateDbGroupsParticipants() }) - // on recive new chat + // on receive new chat sock?.ev.on('chats.upsert', (newChat) => { //console.log('chats.upsert') //console.log(newChat) @@ -196,6 +282,8 @@ class WhatsAppInstance { return { ...chat, messages: [], + name: chat.name ?? '', + participant: this.toProtoParticipants(chat.participant) } }) this.instance.chats.push(...chats) @@ -213,6 +301,9 @@ class WhatsAppInstance { this.instance.chats[index] = { ...PrevChat, ...chat, + messages: chat.messages ?? [], + name: chat.name ?? '', + participant: this.toProtoParticipants(chat.participant) } }) }) @@ -233,8 +324,9 @@ class WhatsAppInstance { sock?.ev.on('messages.upsert', async (m) => { //console.log('messages.upsert') //console.log(m) - if (m.type === 'prepend') - this.instance.messages.unshift(...m.messages) + if (m.type === 'append') + this.instance.messages.push(...m.messages) + if (m.type !== 'notify') return // https://adiwajshing.github.io/Baileys/#reading-messages @@ -249,7 +341,7 @@ class WhatsAppInstance { await sock.readMessages(unreadMessages) } - this.instance.messages.unshift(...m.messages) + this.instance.messages.push(...m.messages) m.messages.map(async (msg) => { if (!msg.message) return @@ -264,35 +356,39 @@ class WhatsAppInstance { return const webhookData = { - key: this.key, - ...msg, + ...{ key: this.key }, + ...msg, + messageKey: msg.key, + instanceKey: this.key, + text: null, + msgContent: null, } if (messageType === 'conversation') { - webhookData['text'] = m + webhookData.text = m } if (config.webhookBase64) { switch (messageType) { case 'imageMessage': - webhookData['msgContent'] = await downloadMessage( - msg.message.imageMessage, + webhookData.msgContent = await downloadMessage( + msg.message.imageMessage!, 'image' ) break case 'videoMessage': - webhookData['msgContent'] = await downloadMessage( - msg.message.videoMessage, + webhookData.msgContent = await downloadMessage( + msg.message.videoMessage!, 'video' ) break case 'audioMessage': - webhookData['msgContent'] = await downloadMessage( - msg.message.audioMessage, + webhookData.msgContent = await downloadMessage( + msg.message.audioMessage!, 'audio' ) break default: - webhookData['msgContent'] = '' + webhookData.msgContent = '' break } } @@ -304,15 +400,16 @@ class WhatsAppInstance { await this.SendWebhook('message', webhookData, this.key) }) }) - + sock?.ev.on('messages.update', async (messages) => { //console.log('messages.update') //console.dir(messages); }) + sock?.ws.on('CB:call', async (data) => { if (data.content) { - if (data.content.find((e) => e.tag === 'offer')) { - const content = data.content.find((e) => e.tag === 'offer') + if (data.content.find((e: { tag: string }) => e.tag === 'offer')) { + const content = data.content.find((e: { tag: string }) => e.tag === 'offer') if ( ['all', 'call', 'CB:call', 'call:offer'].some((e) => config.webhookAllowedEvents.includes(e) @@ -331,9 +428,9 @@ class WhatsAppInstance { }, this.key ) - } else if (data.content.find((e) => e.tag === 'terminate')) { + } else if (data.content.find((e: { tag: string }) => e.tag === 'terminate')) { const content = data.content.find( - (e) => e.tag === 'terminate' + (e: { tag: string }) => e.tag === 'terminate' ) if ( @@ -415,7 +512,12 @@ class WhatsAppInstance { }) } - async deleteInstance(key) { + toProtoParticipants(parts: proto.IGroupParticipant[] | null | undefined) { + const rank = [null, 'admin', 'superadmin'] + return parts?.map(p => ({ id: p.userJid, admin: rank[p.rank ?? 0] })) + } + + async deleteInstance(key: string) { try { await Chat.findOneAndDelete({ key: key }) } catch (e) { @@ -423,7 +525,7 @@ class WhatsAppInstance { } } - async getInstanceDetail(key) { + async getInstanceDetail(key: string) { return { instance_key: key, phone_connected: this.instance?.online, @@ -432,19 +534,19 @@ class WhatsAppInstance { } } - getWhatsAppId(id) { + getWhatsAppId(id: string) { if (id.includes('@g.us') || id.includes('@s.whatsapp.net')) return id return id.includes('-') ? `${id}@g.us` : `${id}@s.whatsapp.net` } - async verifyId(id) { + async verifyId(id: string) { if (id.includes('@g.us')) return true - const [result] = await this.instance.sock?.onWhatsApp(id) + const [result] = await this.instance.sock?.onWhatsApp(id)! if (result?.exists) return true throw new Error('no account exists') } - async sendTextMessage(to, message) { + async sendTextMessage(to: string, message: string) { await this.verifyId(this.getWhatsAppId(to)) const data = await this.instance.sock?.sendMessage( this.getWhatsAppId(to), @@ -453,38 +555,38 @@ class WhatsAppInstance { return data } - async sendMediaFile(to, file, type, caption = '', filename) { + async sendMediaFile(to: string, file: FileType, type: MediaType, caption?: string, filename?: string) { await this.verifyId(this.getWhatsAppId(to)) + const data = await this.instance.sock?.sendMessage( this.getWhatsAppId(to), - { - mimetype: file.mimetype, - [type]: file.buffer, - caption: caption, - ptt: type === 'audio' ? true : false, - fileName: filename ? filename : file.originalname, - } + processMessage({ + type, + caption, + mimeType: file?.mimetype, + buffer: file?.stream, + fileName: filename ? filename : file?.originalname, + }) ) return data } - async sendUrlMediaFile(to, url, type, mimeType, caption = '') { + async sendUrlMediaFile(to: string, url: string, type: MediaType, mimeType: string, caption?: string) { await this.verifyId(this.getWhatsAppId(to)) const data = await this.instance.sock?.sendMessage( - this.getWhatsAppId(to), - { - [type]: { - url: url, - }, - caption: caption, - mimetype: mimeType, - } + this.getWhatsAppId(to), + processMessage({ + type, + caption, + url, + mimeType + }) ) return data } - async DownloadProfile(of) { + async DownloadProfile(of: string) { await this.verifyId(this.getWhatsAppId(of)) const ppUrl = await this.instance.sock?.profilePictureUrl( this.getWhatsAppId(of), @@ -493,7 +595,7 @@ class WhatsAppInstance { return ppUrl } - async getUserStatus(of) { + async getUserStatus(of: string) { await this.verifyId(this.getWhatsAppId(of)) const status = await this.instance.sock?.fetchStatus( this.getWhatsAppId(of) @@ -501,7 +603,7 @@ class WhatsAppInstance { return status } - async blockUnblock(to, data) { + async blockUnblock(to: string, data: Lock) { await this.verifyId(this.getWhatsAppId(to)) const status = await this.instance.sock?.updateBlockStatus( this.getWhatsAppId(to), @@ -510,7 +612,7 @@ class WhatsAppInstance { return status } - async sendButtonMessage(to, data) { + async sendButtonMessage(to: string, data: ButtonMessage) { await this.verifyId(this.getWhatsAppId(to)) const result = await this.instance.sock?.sendMessage( this.getWhatsAppId(to), @@ -524,7 +626,7 @@ class WhatsAppInstance { return result } - async sendContactMessage(to, data) { + async sendContactMessage(to: string, data: VCardData) { await this.verifyId(this.getWhatsAppId(to)) const vcard = generateVC(data) const result = await this.instance.sock?.sendMessage( @@ -539,7 +641,7 @@ class WhatsAppInstance { return result } - async sendListMessage(to, data) { + async sendListMessage(to: string, data: ListMessage) { await this.verifyId(this.getWhatsAppId(to)) const result = await this.instance.sock?.sendMessage( this.getWhatsAppId(to), @@ -555,15 +657,13 @@ class WhatsAppInstance { return result } - async sendMediaButtonMessage(to, data) { + async sendMediaButtonMessage(to: string, data: MediaButtonMessage) { await this.verifyId(this.getWhatsAppId(to)) const result = await this.instance.sock?.sendMessage( this.getWhatsAppId(to), { - [data.mediaType]: { - url: data.image, - }, + image: { url: data.image! }, footer: data.footerText ?? '', caption: data.text, templateButtons: processButton(data.buttons), @@ -574,7 +674,7 @@ class WhatsAppInstance { return result } - async setStatus(status, to) { + async setStatus(status: Status, to: string) { await this.verifyId(this.getWhatsAppId(to)) const result = await this.instance.sock?.sendPresenceUpdate(status, to) @@ -582,7 +682,7 @@ class WhatsAppInstance { } // change your display picture or a group's - async updateProfilePicture(id, url) { + async updateProfilePicture(id: string, url: string) { try { const img = await axios.get(url, { responseType: 'arraybuffer' }) const res = await this.instance.sock?.updateProfilePicture( @@ -600,10 +700,10 @@ class WhatsAppInstance { } // get user or group object from db by id - async getUserOrGroupById(id) { + async getUserOrGroupById(id: string) { try { let Chats = await this.getChat() - const group = Chats.find((c) => c.id === this.getWhatsAppId(id)) + const group = Chats?.find((c) => c.id === this.getWhatsAppId(id)) if (!group) throw new Error( 'unable to get group, check if the group exists' @@ -616,10 +716,14 @@ class WhatsAppInstance { } // Group Methods - parseParticipants(users) { + parseParticipants(users: string[]) { return users.map((users) => this.getWhatsAppId(users)) } + toParticipants(participants: GroupParticipant[]) { + return participants.map(p => ({ id: p.id, admin: p.admin ?? null })) + } + async updateDbGroupsParticipants() { try { let groups = await this.groupFetchAllParticipating() @@ -635,7 +739,7 @@ class WhatsAppInstance { ] of Object.entries(value.participants)) { participants.push(participant) } - group.participant = participants + group.participant = this.toParticipants(participants) if (value.creation) { group.creation = value.creation } @@ -653,7 +757,7 @@ class WhatsAppInstance { } } - async createNewGroup(name, users) { + async createNewGroup(name: string, users: string[]) { try { const group = await this.instance.sock?.groupCreate( name, @@ -666,11 +770,12 @@ class WhatsAppInstance { } } - async addNewParticipant(id, users) { + async addNewParticipant(id: string, users: string[]) { try { - const res = await this.instance.sock?.groupAdd( + const res = await this.instance.sock?.groupParticipantsUpdate( this.getWhatsAppId(id), - this.parseParticipants(users) + this.parseParticipants(users), + 'add' ) return res } catch { @@ -682,11 +787,12 @@ class WhatsAppInstance { } } - async makeAdmin(id, users) { + async makeAdmin(id: string, users: string[]) { try { - const res = await this.instance.sock?.groupMakeAdmin( + const res = await this.instance.sock?.groupParticipantsUpdate( this.getWhatsAppId(id), - this.parseParticipants(users) + this.parseParticipants(users), + 'promote' ) return res } catch { @@ -698,11 +804,12 @@ class WhatsAppInstance { } } - async demoteAdmin(id, users) { + async demoteAdmin(id: string, users: string[]) { try { - const res = await this.instance.sock?.groupDemoteAdmin( + const res = await this.instance.sock?.groupParticipantsUpdate( this.getWhatsAppId(id), - this.parseParticipants(users) + this.parseParticipants(users), + 'demote' ) return res } catch { @@ -716,7 +823,7 @@ class WhatsAppInstance { async getAllGroups() { let Chats = await this.getChat() - return Chats.filter((c) => c.id.includes('@g.us')).map((data, i) => { + return Chats?.filter((c) => c.id.includes('@g.us')).map((data, i) => { return { index: i, name: data.name, @@ -728,10 +835,10 @@ class WhatsAppInstance { }) } - async leaveGroup(id) { + async leaveGroup(id: string) { try { let Chats = await this.getChat() - const group = Chats.find((c) => c.id === id) + const group = Chats?.find((c) => c.id === id) if (!group) throw new Error('no group exists') return await this.instance.sock?.groupLeave(id) } catch (e) { @@ -740,10 +847,10 @@ class WhatsAppInstance { } } - async getInviteCodeGroup(id) { + async getInviteCodeGroup(id: string) { try { let Chats = await this.getChat() - const group = Chats.find((c) => c.id === id) + const group = Chats?.find((c) => c.id === id) if (!group) throw new Error( 'unable to get invite code, check if the group exists' @@ -755,7 +862,7 @@ class WhatsAppInstance { } } - async getInstanceInviteCodeGroup(id) { + async getInstanceInviteCodeGroup(id: string) { try { return await this.instance.sock?.groupInviteCode(id) } catch (e) { @@ -767,23 +874,23 @@ class WhatsAppInstance { // get Chat object from db async getChat(key = this.key) { let dbResult = await Chat.findOne({ key: key }).exec() - let ChatObj = dbResult.chat + let ChatObj = dbResult?.chat return ChatObj } // create new group by application - async createGroupByApp(newChat) { + async createGroupByApp(newChat: GroupMetadata[]) { try { let Chats = await this.getChat() let group = { id: newChat[0].id, name: newChat[0].subject, - participant: newChat[0].participants, + participant: this.toParticipants(newChat[0].participants), messages: [], creation: newChat[0].creation, subjectOwner: newChat[0].subjectOwner, } - Chats.push(group) + Chats?.push(group) await this.updateDb(Chats) } catch (e) { logger.error(e) @@ -791,13 +898,13 @@ class WhatsAppInstance { } } - async updateGroupSubjectByApp(newChat) { + async updateGroupSubjectByApp(newChat: Partial[]) { //console.log(newChat) try { if (newChat[0] && newChat[0].subject) { let Chats = await this.getChat() - Chats.find((c) => c.id === newChat[0].id).name = - newChat[0].subject + let Chat = Chats?.find((c) => c.id === newChat[0].id) + Chat!.name = newChat[0].subject await this.updateDb(Chats) } } catch (e) { @@ -806,12 +913,13 @@ class WhatsAppInstance { } } - async updateGroupParticipantsByApp(newChat) { + async updateGroupParticipantsByApp(newChat: { id: string; participants: string[]; action: ParticipantAction } + ) { //console.log(newChat) try { if (newChat && newChat.id) { let Chats = await this.getChat() - let chat = Chats.find((c) => c.id === newChat.id) + let chat = Chats?.find((c) => c.id === newChat.id) let is_owner = false if (chat) { if (chat.participant == undefined) { @@ -832,7 +940,7 @@ class WhatsAppInstance { is_owner = true } chat.participant = chat.participant.filter( - (p) => p.id != participant + (p: { id: any }) => p.id != participant ) } } @@ -840,11 +948,11 @@ class WhatsAppInstance { for (const participant of newChat.participants) { if ( chat.participant.filter( - (p) => p.id == participant + (p: { id: any }) => p.id == participant )[0] ) { chat.participant.filter( - (p) => p.id == participant + (p: { id: any }) => p.id == participant )[0].admin = null } } @@ -853,19 +961,20 @@ class WhatsAppInstance { for (const participant of newChat.participants) { if ( chat.participant.filter( - (p) => p.id == participant + (p: { id: any }) => p.id == participant )[0] ) { chat.participant.filter( - (p) => p.id == participant + (p: { id: any }) => p.id == participant )[0].admin = 'superadmin' } } } if (is_owner) { - Chats = Chats.filter((c) => c.id !== newChat.id) + Chats = Chats?.filter((c) => c.id !== newChat.id) } else { - Chats.filter((c) => c.id === newChat.id)[0] = chat + Chats = Chats?.filter((c) => c.id === newChat.id) + Chats![0] = chat } await this.updateDb(Chats) } @@ -887,7 +996,7 @@ class WhatsAppInstance { } // update promote demote remove - async groupParticipantsUpdate(id, users, action) { + async groupParticipantsUpdate(id: string, users: string[], action: ParticipantAction) { try { const res = await this.instance.sock?.groupParticipantsUpdate( this.getWhatsAppId(id), @@ -909,7 +1018,7 @@ class WhatsAppInstance { // update group settings like // only allow admins to send messages - async groupSettingUpdate(id, action) { + async groupSettingUpdate(id: string, action: GroupAction) { try { const res = await this.instance.sock?.groupSettingUpdate( this.getWhatsAppId(id), @@ -926,7 +1035,7 @@ class WhatsAppInstance { } } - async groupUpdateSubject(id, subject) { + async groupUpdateSubject(id: string, subject: string) { try { const res = await this.instance.sock?.groupUpdateSubject( this.getWhatsAppId(id), @@ -943,7 +1052,7 @@ class WhatsAppInstance { } } - async groupUpdateDescription(id, description) { + async groupUpdateDescription(id: string, description?: string) { try { const res = await this.instance.sock?.groupUpdateDescription( this.getWhatsAppId(id), @@ -961,7 +1070,7 @@ class WhatsAppInstance { } // update db document -> chat - async updateDb(object) { + async updateDb(object?: ChatType[]) { try { await Chat.updateOne({ key: this.key }, { chat: object }) } catch (e) { @@ -969,7 +1078,7 @@ class WhatsAppInstance { } } - async readMessage(msgObj) { + async readMessage(msgObj: MessageKey) { try { const key = { remoteJid: msgObj.remoteJid, @@ -983,7 +1092,7 @@ class WhatsAppInstance { } } - async reactMessage(id, key, emoji) { + async reactMessage(id: string, key: MessageKey, emoji: string | null) { try { const reactionMessage = { react: { @@ -1002,4 +1111,4 @@ class WhatsAppInstance { } } -exports.WhatsAppInstance = WhatsAppInstance +export default WhatsAppInstance diff --git a/src/api/class/session.ts b/src/api/class/session.ts index 9e93fe14..fc24ba7a 100644 --- a/src/api/class/session.ts +++ b/src/api/class/session.ts @@ -1,14 +1,26 @@ /* eslint-disable no-unsafe-optional-chaining */ -const { WhatsAppInstance } = require('../class/instance') -const logger = require('pino')() -const config = require('../../config/config') +import WhatsAppInstance from '../class/instance' +import pino from 'pino' +import config from '../../config/config' +import getDatabase from '../service/database' +import { AppType } from '../helper/types' +import { getInstanceService } from '../service/instance' + +const logger = pino() class Session { + app: AppType + + constructor(app: AppType){ + this.app = app; + } + async restoreSessions() { - let restoredSessions = new Array() - let allCollections = [] + let restoredSessions : string[] = [] + let allCollections : string[] = [] try { - const db = mongoClient.db('whatsapp-api') + let instances = getInstanceService(this.app).instances + const db = getDatabase(this.app) const result = await db.listCollections().toArray() result.forEach((collection) => { allCollections.push(collection.name) @@ -21,18 +33,19 @@ class Session { .toArray(async (err, result) => { if (err) throw err const webhook = !config.webhookEnabled - ? undefined + ? null : config.webhookEnabled const webhookUrl = !config.webhookUrl - ? undefined + ? null : config.webhookUrl const instance = new WhatsAppInstance( + this.app, key, webhook, webhookUrl ) await instance.init() - WhatsAppInstances[key] = instance + instances[key] = instance }) restoredSessions.push(key) }) @@ -44,4 +57,4 @@ class Session { } } -exports.Session = Session +export default Session diff --git a/src/api/controllers/group.controller.ts b/src/api/controllers/group.controller.ts index a1a9ca91..38ba6aea 100644 --- a/src/api/controllers/group.controller.ts +++ b/src/api/controllers/group.controller.ts @@ -1,67 +1,68 @@ -exports.create = async (req, res) => { - const data = await WhatsAppInstances[req.query.key].createNewGroup( +import { ReqHandler } from '../helper/types' +import getInstanceForReq from '../service/instance' + +export const create : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req).createNewGroup( req.body.name, req.body.users ) return res.status(201).json({ error: false, data: data }) } -exports.addNewParticipant = async (req, res) => { - const data = await WhatsAppInstances[req.query.key].addNewParticipant( +export const addNewParticipant : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req).addNewParticipant( req.body.id, req.body.users ) return res.status(201).json({ error: false, data: data }) } -exports.makeAdmin = async (req, res) => { - const data = await WhatsAppInstances[req.query.key].makeAdmin( +export const makeAdmin : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req).makeAdmin( req.body.id, req.body.users ) return res.status(201).json({ error: false, data: data }) } -exports.demoteAdmin = async (req, res) => { - const data = await WhatsAppInstances[req.query.key].demoteAdmin( +export const demoteAdmin : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req).demoteAdmin( req.body.id, req.body.users ) return res.status(201).json({ error: false, data: data }) } -exports.listAll = async (req, res) => { - const data = await WhatsAppInstances[req.query.key].getAllGroups( +export const listAll : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req).getAllGroups( req.query.key ) return res.status(201).json({ error: false, data: data }) } -exports.leaveGroup = async (req, res) => { - const data = await WhatsAppInstances[req.query.key].leaveGroup(req.query.id) +export const leaveGroup : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req).leaveGroup( req.query.id) return res.status(201).json({ error: false, data: data }) } -exports.getInviteCodeGroup = async (req, res) => { - const data = await WhatsAppInstances[req.query.key].getInviteCodeGroup( - req.query.id +export const getInviteCodeGroup : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req).getInviteCodeGroup( + req.query.id ) return res .status(201) .json({ error: false, link: 'https://chat.whatsapp.com/' + data }) } -exports.getInstanceInviteCodeGroup = async (req, res) => { - const data = await WhatsAppInstances[ - req.query.key - ].getInstanceInviteCodeGroup(req.query.id) +export const getInstanceInviteCodeGroup : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req).getInstanceInviteCodeGroup( req.query.id) return res .status(201) .json({ error: false, link: 'https://chat.whatsapp.com/' + data }) } -exports.getAllGroups = async (req, res) => { - const instance = WhatsAppInstances[req.query.key] +export const getAllGroups : ReqHandler = async (req, res) => { + const instance = getInstanceForReq(req) let data try { data = await instance.groupFetchAllParticipating() @@ -75,8 +76,8 @@ exports.getAllGroups = async (req, res) => { }) } -exports.groupParticipantsUpdate = async (req, res) => { - const data = await WhatsAppInstances[req.query.key].groupParticipantsUpdate( +export const groupParticipantsUpdate : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req).groupParticipantsUpdate( req.body.id, req.body.users, req.body.action @@ -84,39 +85,39 @@ exports.groupParticipantsUpdate = async (req, res) => { return res.status(201).json({ error: false, data: data }) } -exports.groupSettingUpdate = async (req, res) => { - const data = await WhatsAppInstances[req.query.key].groupSettingUpdate( +export const groupSettingUpdate : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req).groupSettingUpdate( req.body.id, req.body.action ) return res.status(201).json({ error: false, data: data }) } -exports.groupUpdateSubject = async (req, res) => { - const data = await WhatsAppInstances[req.query.key].groupUpdateSubject( +export const groupUpdateSubject : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req).groupUpdateSubject( req.body.id, req.body.subject ) return res.status(201).json({ error: false, data: data }) } -exports.groupUpdateDescription = async (req, res) => { - const data = await WhatsAppInstances[req.query.key].groupUpdateDescription( +export const groupUpdateDescription : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req).groupUpdateDescription( req.body.id, req.body.description ) return res.status(201).json({ error: false, data: data }) } -exports.groupInviteInfo = async (req, res) => { - const data = await WhatsAppInstances[req.query.key].groupGetInviteInfo( +export const groupInviteInfo : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req).groupGetInviteInfo( req.body.code ) return res.status(201).json({ error: false, data: data }) } -exports.groupJoin = async (req, res) => { - const data = await WhatsAppInstances[req.query.key].groupAcceptInvite( +export const groupJoin : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req).groupAcceptInvite( req.body.code ) return res.status(201).json({ error: false, data: data }) diff --git a/src/api/controllers/instance.controller.ts b/src/api/controllers/instance.controller.ts index ce803a88..b96a9088 100644 --- a/src/api/controllers/instance.controller.ts +++ b/src/api/controllers/instance.controller.ts @@ -1,17 +1,19 @@ -const { WhatsAppInstance } = require('../class/instance') -const fs = require('fs') -const path = require('path') -const config = require('../../config/config') -const { Session } = require('../class/session') +import WhatsAppInstance from '../class/instance' +import config from '../../config/config' +import Session from '../class/session' +import { ReqHandler } from '../helper/types' +import getInstanceForReq, { getInstanceService } from '../service/instance' +import getDatabase from '../service/database' -exports.init = async (req, res) => { - const key = req.query.key - const webhook = !req.query.webhook ? false : req.query.webhook +export const init : ReqHandler = async (req, res) => { + const key = req.query.key + const webhook = !req.query.webhook ? false : !!req.query.webhook const webhookUrl = !req.query.webhookUrl ? null : req.query.webhookUrl const appUrl = config.appUrl || req.protocol + '://' + req.headers.host const instance = new WhatsAppInstance(key, webhook, webhookUrl) const data = await instance.init() - WhatsAppInstances[data.key] = instance + let instances = getInstanceService(req.app).instances + instances[data.key] = instance res.json({ error: false, message: 'Initializing successfully', @@ -27,9 +29,9 @@ exports.init = async (req, res) => { }) } -exports.qr = async (req, res) => { +export const qr : ReqHandler = async (req, res) => { try { - const qrcode = await WhatsAppInstances[req.query.key]?.instance.qr + const qrcode = await getInstanceForReq(req)?.instance.qr res.render('qrcode', { qrcode: qrcode, }) @@ -40,9 +42,9 @@ exports.qr = async (req, res) => { } } -exports.qrbase64 = async (req, res) => { +export const qrbase64 : ReqHandler = async (req, res) => { try { - const qrcode = await WhatsAppInstances[req.query.key]?.instance.qr + const qrcode = await getInstanceForReq(req)?.instance.qr res.json({ error: false, message: 'QR Base64 fetched successfully', @@ -55,11 +57,11 @@ exports.qrbase64 = async (req, res) => { } } -exports.info = async (req, res) => { - const instance = WhatsAppInstances[req.query.key] +export const info : ReqHandler = async (req, res) => { + const instance = getInstanceForReq(req) let data try { - data = await instance.getInstanceDetail(req.query.key) + data = await instance.getInstanceDetail( req.query.key) } catch (error) { data = {} } @@ -70,7 +72,7 @@ exports.info = async (req, res) => { }) } -exports.restore = async (req, res, next) => { +export const restore : ReqHandler = async (req, res, next) => { try { const session = new Session() let restoredSessions = await session.restoreSessions() @@ -84,10 +86,10 @@ exports.restore = async (req, res, next) => { } } -exports.logout = async (req, res) => { +export const logout : ReqHandler = async (req, res) => { let errormsg try { - await WhatsAppInstances[req.query.key].instance?.sock?.logout() + await getInstanceForReq(req).instance?.sock?.logout() } catch (error) { errormsg = error } @@ -98,11 +100,12 @@ exports.logout = async (req, res) => { }) } -exports.delete = async (req, res) => { +export const remove : ReqHandler = async (req, res) => { let errormsg try { - await WhatsAppInstances[req.query.key].deleteInstance(req.query.key) - delete WhatsAppInstances[req.query.key] + await getInstanceForReq(req).deleteInstance( req.query.key) + let instances = getInstanceService(req.app).instances + delete instances[ req.query.key] } catch (error) { errormsg = error } @@ -113,10 +116,12 @@ exports.delete = async (req, res) => { }) } -exports.list = async (req, res) => { +export const list : ReqHandler = async (req, res) => { + let instances = getInstanceService(req.app).instances + if (req.query.active) { - let instance = [] - const db = mongoClient.db('whatsapp-api') + let instance : string[] = [] + const db = getDatabase(req.app) const result = await db.listCollections().toArray() result.forEach((collection) => { instance.push(collection.name) @@ -129,8 +134,8 @@ exports.list = async (req, res) => { }) } - let instance = Object.keys(WhatsAppInstances).map(async (key) => - WhatsAppInstances[key].getInstanceDetail(key) + let instance = Object.keys(instances).map(async (key) => + instances[key].getInstanceDetail(key) ) let data = await Promise.all(instance) diff --git a/src/api/controllers/message.controller.ts b/src/api/controllers/message.controller.ts index b11da33f..a653b55b 100644 --- a/src/api/controllers/message.controller.ts +++ b/src/api/controllers/message.controller.ts @@ -1,13 +1,16 @@ -exports.Text = async (req, res) => { - const data = await WhatsAppInstances[req.query.key].sendTextMessage( +import { ReqHandler } from "../helper/types" +import getInstanceForReq from "../service/instance" + +export const Text : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req).sendTextMessage( req.body.id, req.body.message ) return res.status(201).json({ error: false, data: data }) } -exports.Image = async (req, res) => { - const data = await WhatsAppInstances[req.query.key].sendMediaFile( +export const Image : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req).sendMediaFile( req.body.id, req.file, 'image', @@ -16,8 +19,8 @@ exports.Image = async (req, res) => { return res.status(201).json({ error: false, data: data }) } -exports.Video = async (req, res) => { - const data = await WhatsAppInstances[req.query.key].sendMediaFile( +export const Video : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req).sendMediaFile( req.body.id, req.file, 'video', @@ -26,8 +29,8 @@ exports.Video = async (req, res) => { return res.status(201).json({ error: false, data: data }) } -exports.Audio = async (req, res) => { - const data = await WhatsAppInstances[req.query.key].sendMediaFile( +export const Audio : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req).sendMediaFile( req.body.id, req.file, 'audio' @@ -35,8 +38,8 @@ exports.Audio = async (req, res) => { return res.status(201).json({ error: false, data: data }) } -exports.Document = async (req, res) => { - const data = await WhatsAppInstances[req.query.key].sendMediaFile( +export const Document : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req).sendMediaFile( req.body.id, req.file, 'document', @@ -46,8 +49,8 @@ exports.Document = async (req, res) => { return res.status(201).json({ error: false, data: data }) } -exports.Mediaurl = async (req, res) => { - const data = await WhatsAppInstances[req.query.key].sendUrlMediaFile( +export const Mediaurl : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req).sendUrlMediaFile( req.body.id, req.body.url, req.body.type, // Types are [image, video, audio, document] @@ -57,40 +60,40 @@ exports.Mediaurl = async (req, res) => { return res.status(201).json({ error: false, data: data }) } -exports.Button = async (req, res) => { +export const Button : ReqHandler = async (req, res) => { // console.log(res.body) - const data = await WhatsAppInstances[req.query.key].sendButtonMessage( + const data = await getInstanceForReq(req).sendButtonMessage( req.body.id, req.body.btndata ) return res.status(201).json({ error: false, data: data }) } -exports.Contact = async (req, res) => { - const data = await WhatsAppInstances[req.query.key].sendContactMessage( +export const Contact : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req).sendContactMessage( req.body.id, req.body.vcard ) return res.status(201).json({ error: false, data: data }) } -exports.List = async (req, res) => { - const data = await WhatsAppInstances[req.query.key].sendListMessage( +export const List : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req).sendListMessage( req.body.id, req.body.msgdata ) return res.status(201).json({ error: false, data: data }) } -exports.MediaButton = async (req, res) => { - const data = await WhatsAppInstances[req.query.key].sendMediaButtonMessage( +export const MediaButton : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req).sendMediaButtonMessage( req.body.id, req.body.btndata ) return res.status(201).json({ error: false, data: data }) } -exports.SetStatus = async (req, res) => { +export const SetStatus : ReqHandler = async (req, res) => { const presenceList = [ 'unavailable', 'available', @@ -106,19 +109,19 @@ exports.SetStatus = async (req, res) => { }) } - const data = await WhatsAppInstances[req.query.key]?.setStatus( + const data = await getInstanceForReq(req)?.setStatus( req.body.status, req.body.id ) return res.status(201).json({ error: false, data: data }) } -exports.Read = async (req, res) => { - const data = await WhatsAppInstances[req.query.key].readMessage(req.body.msg) +export const Read : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req).readMessage(req.body.msg) return res.status(201).json({ error: false, data: data }) } -exports.React = async (req, res) => { - const data = await WhatsAppInstances[req.query.key].reactMessage(req.body.id, req.body.key, req.body.emoji) +export const React : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req).reactMessage(req.body.id, req.body.key, req.body.emoji) return res.status(201).json({ error: false, data: data }) } diff --git a/src/api/controllers/misc.controller.ts b/src/api/controllers/misc.controller.ts index 2efca0a0..757de6b0 100644 --- a/src/api/controllers/misc.controller.ts +++ b/src/api/controllers/misc.controller.ts @@ -1,29 +1,32 @@ -exports.onWhatsapp = async (req, res) => { +import { ReqHandler } from "../helper/types" +import getInstanceForReq from "../service/instance" + +export const onWhatsapp : ReqHandler = async (req, res) => { // eslint-disable-next-line no-unsafe-optional-chaining - const data = await WhatsAppInstances[req.query.key]?.verifyId( - WhatsAppInstances[req.query.key]?.getWhatsAppId(req.query.id) + const data = await getInstanceForReq(req)?.verifyId( + getInstanceForReq(req)?.getWhatsAppId( req.query.id) ) return res.status(201).json({ error: false, data: data }) } -exports.downProfile = async (req, res) => { - const data = await WhatsAppInstances[req.query.key]?.DownloadProfile( - req.query.id +export const downProfile : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req)?.DownloadProfile( + req.query.id ) return res.status(201).json({ error: false, data: data }) } -exports.getStatus = async (req, res) => { - const data = await WhatsAppInstances[req.query.key]?.getUserStatus( - req.query.id +export const getStatus : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req)?.getUserStatus( + req.query.id ) return res.status(201).json({ error: false, data: data }) } -exports.blockUser = async (req, res) => { - const data = await WhatsAppInstances[req.query.key]?.blockUnblock( - req.query.id, - req.query.block_status +export const blockUser : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req)?.blockUnblock( + req.query.id, + <'block' | 'unblock'> req.query.block_status ) if (req.query.block_status == 'block') { return res @@ -35,17 +38,17 @@ exports.blockUser = async (req, res) => { .json({ error: false, message: 'Contact Unblocked' }) } -exports.updateProfilePicture = async (req, res) => { - const data = await WhatsAppInstances[req.query.key].updateProfilePicture( +export const updateProfilePicture : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req).updateProfilePicture( req.body.id, req.body.url ) return res.status(201).json({ error: false, data: data }) } -exports.getUserOrGroupById = async (req, res) => { - const data = await WhatsAppInstances[req.query.key].getUserOrGroupById( - req.query.id +export const getUserOrGroupById : ReqHandler = async (req, res) => { + const data = await getInstanceForReq(req).getUserOrGroupById( + req.query.id ) return res.status(201).json({ error: false, data: data }) } diff --git a/src/api/errors/api.error.ts b/src/api/errors/api.error.ts index d684e0a9..47f4bf15 100644 --- a/src/api/errors/api.error.ts +++ b/src/api/errors/api.error.ts @@ -1,7 +1,7 @@ -const ExtendableError = require('../errors/extendable.error') +import ExtendableError from '../errors/extendable.error' class APIError extends ExtendableError { - constructor({ message, errors, status = 500 }) { + constructor({ message = '', errors = '', status = 500 }) { super({ message, errors, @@ -10,4 +10,4 @@ class APIError extends ExtendableError { } } -module.exports = APIError +export default APIError diff --git a/src/api/errors/extendable.error.ts b/src/api/errors/extendable.error.ts index 3ca85e1c..0485a426 100644 --- a/src/api/errors/extendable.error.ts +++ b/src/api/errors/extendable.error.ts @@ -1,5 +1,8 @@ + class ExtendableError extends Error { - constructor({ message, errors, status }) { + errors: string + status: number + constructor({ message = '', errors = '', status = 0 }) { super(message) this.name = this.constructor.name this.message = message @@ -8,4 +11,4 @@ class ExtendableError extends Error { } } -module.exports = ExtendableError +export default ExtendableError diff --git a/src/api/helper/connectMongoClient.ts b/src/api/helper/connectMongoClient.ts index e45375e9..c13f9815 100644 --- a/src/api/helper/connectMongoClient.ts +++ b/src/api/helper/connectMongoClient.ts @@ -1,18 +1,22 @@ -const { MongoClient } = require('mongodb') -const logger = require('pino')() +import { MongoClient } from 'mongodb' +import pino from 'pino' +import { AppType } from './types' +import { getDatabaseService } from '../service/database' -module.exports = async function connectToCluster(uri) { - let mongoClient +const logger = pino() + +export default async function connectToCluster(app: AppType, uri: string) { + const database = getDatabaseService(app) try { - mongoClient = new MongoClient(uri, { - useNewUrlParser: true, - useUnifiedTopology: true, + database.mongoClient = new MongoClient(uri, { + // useNewUrlParser: true, + // useUnifiedTopology: true, }) logger.info('STATE: Connecting to MongoDB') - await mongoClient.connect() + await database.mongoClient.connect() logger.info('STATE: Successfully connected to MongoDB') - return mongoClient + return database.mongoClient } catch (error) { logger.error('STATE: Connection to MongoDB failed!', error) process.exit() diff --git a/src/api/helper/downloadMsg.ts b/src/api/helper/downloadMsg.ts index 1b0aef25..776b993f 100644 --- a/src/api/helper/downloadMsg.ts +++ b/src/api/helper/downloadMsg.ts @@ -1,6 +1,9 @@ -const { downloadContentFromMessage } = require('@whiskeysockets/baileys') +import { downloadContentFromMessage } from '@whiskeysockets/baileys' -module.exports = async function downloadMessage(msg, msgType) { +type MessageType = Parameters[0]; +type MessageTypeType = Parameters[1]; + +export default async function downloadMessage(msg: MessageType, msgType: MessageTypeType) { let buffer = Buffer.from([]) try { const stream = await downloadContentFromMessage(msg, msgType) diff --git a/src/api/helper/genVc.ts b/src/api/helper/genVc.ts index e0e24c11..a5a48cea 100644 --- a/src/api/helper/genVc.ts +++ b/src/api/helper/genVc.ts @@ -1,4 +1,11 @@ -module.exports = function generateVC(data) { + +export interface VCardData { + fullName: string; + organization: string; + phoneNumber: string; +} + +export default function generateVC(data: VCardData) { const result = 'BEGIN:VCARD\n' + 'VERSION:3.0\n' + diff --git a/src/api/helper/mongoAuthState.ts b/src/api/helper/mongoAuthState.ts index 02a5d87d..5b21652a 100644 --- a/src/api/helper/mongoAuthState.ts +++ b/src/api/helper/mongoAuthState.ts @@ -1,12 +1,10 @@ -const { proto } = require('@whiskeysockets/baileys/WAProto') -const { - Curve, - signedKeyPair, -} = require('@whiskeysockets/baileys/lib/Utils/crypto') -const { - generateRegistrationId, -} = require('@whiskeysockets/baileys/lib/Utils/generics') -const { randomBytes } = require('crypto') +import { proto } from '@whiskeysockets/baileys/WAProto' +import { AuthenticationCreds } from '@whiskeysockets/baileys' +import { Curve, signedKeyPair } from '@whiskeysockets/baileys/lib/Utils/crypto' +import { generateRegistrationId } from '@whiskeysockets/baileys/lib/Utils/generics' +import { randomBytes } from 'crypto' +import { CollectionType } from '../service/database' +import { TypeOfPromise } from './types' const initAuthCreds = () => { const identityKey = Curve.generateKeyPair() @@ -26,7 +24,7 @@ const initAuthCreds = () => { } const BufferJSON = { - replacer: (k, value) => { + replacer: (k: any, value: { type: string; data: any }) => { if ( Buffer.isBuffer(value) || value instanceof Uint8Array || @@ -41,7 +39,7 @@ const BufferJSON = { return value }, - reviver: (_, value) => { + reviver: (_: any, value: { buffer: boolean; type: string; data: any; value: any }) => { if ( typeof value === 'object' && !!value && @@ -57,15 +55,15 @@ const BufferJSON = { }, } -module.exports = useMongoDBAuthState = async (collection) => { - const writeData = (data, id) => { +export default async function useMongoDBAuthState(collection: CollectionType) { + const writeData = (data: any, id: string) => { return collection.replaceOne( { _id: id }, JSON.parse(JSON.stringify(data, BufferJSON.replacer)), { upsert: true } ) } - const readData = async (id) => { + const readData = async (id: string) => { try { const data = JSON.stringify(await collection.findOne({ _id: id })) return JSON.parse(data, BufferJSON.reviver) @@ -73,18 +71,18 @@ module.exports = useMongoDBAuthState = async (collection) => { return null } } - const removeData = async (id) => { + const removeData = async (id: string) => { try { await collection.deleteOne({ _id: id }) } catch (_a) {} } - const creds = (await readData('creds')) || (0, initAuthCreds)() + const creds: AuthenticationCreds = (await readData('creds')) || initAuthCreds() return { state: { creds, keys: { - get: async (type, ids) => { - const data = {} + get: async (type: string, ids: string[]) => { + const data: Record = {} await Promise.all( ids.map(async (id) => { let value = await readData(`${type}-${id}`) @@ -99,7 +97,7 @@ module.exports = useMongoDBAuthState = async (collection) => { ) return data }, - set: async (data) => { + set: async (data: Record) => { const tasks = [] for (const category of Object.keys(data)) { for (const id of Object.keys(data[category])) { @@ -119,3 +117,5 @@ module.exports = useMongoDBAuthState = async (collection) => { }, } } + +export type AuthState = TypeOfPromise> diff --git a/src/api/helper/processbtn.ts b/src/api/helper/processbtn.ts index 191cddab..653788e7 100644 --- a/src/api/helper/processbtn.ts +++ b/src/api/helper/processbtn.ts @@ -1,7 +1,20 @@ -module.exports = function processButton(buttons) { - const preparedButtons = [] - buttons.map((button) => { +export type ButtonDef = { + type: string; + title: string; + payload: string; +} + +export type Button = { + quickReplyButton?: { displayText: any }; + callButton?: { displayText: any; phoneNumber: any }; + urlButton?: { displayText: any; url: any } +} + +export default function processButton(buttons?: ButtonDef[]) { + const preparedButtons: Button[] = [] + + buttons?.map((button) => { if (button.type == 'replyButton') { preparedButtons.push({ quickReplyButton: { diff --git a/src/api/helper/processmessage.ts b/src/api/helper/processmessage.ts new file mode 100644 index 00000000..3af8a696 --- /dev/null +++ b/src/api/helper/processmessage.ts @@ -0,0 +1,45 @@ +import type { Readable } from 'stream'; + +export type MediaType = 'image' | 'video' | 'audio' | 'document' | 'sticker' + +export type MessageType = { + type: MediaType + url?: string + mimeType?: string + caption?: string + fileName?: string + buffer?: Readable +} + +export default function processMessage(msg: MessageType) { + const baseMsg = { + ...(msg.mimeType ? { mimetype: msg.mimeType } : {}) + } + const withCaption = { + ...baseMsg, + ...(msg.caption ? { caption: msg.caption } : {}) + } + const mediaUpload = msg.buffer ? { + stream: msg.buffer + } : { + url: msg.url! + } + switch(msg.type) { + case 'image': + return { image: mediaUpload, ...withCaption } + case 'video': + return { video: mediaUpload, ...withCaption } + case 'document': + return { + document: mediaUpload, + mimetype: msg.mimeType ?? 'application/octet-stream', + ...(msg.fileName ? { fileName: msg.fileName } : {}) + } + case 'audio': + return { audio: mediaUpload, ptt: true, ...baseMsg } + case 'sticker': + return { sticker: mediaUpload, ...baseMsg } + default: + throw new Error(`Unsupported media type: ${msg.type}`); + } +} diff --git a/src/api/helper/sleep.ts b/src/api/helper/sleep.ts index ed2c4bf1..e77c85cd 100644 --- a/src/api/helper/sleep.ts +++ b/src/api/helper/sleep.ts @@ -1,4 +1,5 @@ -module.exports = function sleep(ms) { + +export default function sleep(ms: number) { return new Promise((resolve) => { setTimeout(resolve, ms) }) diff --git a/src/api/helper/types.ts b/src/api/helper/types.ts new file mode 100644 index 00000000..82abe2d4 --- /dev/null +++ b/src/api/helper/types.ts @@ -0,0 +1,11 @@ +import { ErrorRequestHandler, Request, RequestHandler } from "express"; + +export type TypeOfPromise

= P extends Promise ? T : never; + +export type ErrHandler = ErrorRequestHandler; +export type ReqHandler = RequestHandler; + +export type ReqType = Request; +export type ResType = Response; +export type FileType = Request['file']; +export type AppType = ReqType['app'] diff --git a/src/api/middlewares/error.ts b/src/api/middlewares/error.ts index a7b11a95..a416fdf2 100644 --- a/src/api/middlewares/error.ts +++ b/src/api/middlewares/error.ts @@ -1,8 +1,9 @@ /* eslint-disable no-unused-vars */ -const APIError = require('../../api/errors/api.error') +import APIError from '../../api/errors/api.error' +import { ErrHandler, ReqHandler } from '../helper/types' -const handler = (err, req, res, next) => { - const statusCode = err.statusCode ? err.statusCode : 500 +export const handler : ErrHandler = (err, req, res, next) => { + const statusCode = err.status ? err.status : 500 res.setHeader('Content-Type', 'application/json') res.status(statusCode) @@ -13,12 +14,10 @@ const handler = (err, req, res, next) => { }) } -exports.handler = handler - -exports.notFound = (req, res, next) => { +export const notFound : ReqHandler = (req, res, next) => { const err = new APIError({ message: 'Not found', status: 404, }) - return handler(err, req, res) + return handler(err, req, res, next) } diff --git a/src/api/middlewares/keyCheck.ts b/src/api/middlewares/keyCheck.ts index c1f8acea..6cc87a27 100644 --- a/src/api/middlewares/keyCheck.ts +++ b/src/api/middlewares/keyCheck.ts @@ -1,11 +1,14 @@ -function keyVerification(req, res, next) { +import { ReqHandler } from "../helper/types" +import getInstanceForReq from "../service/instance" + +export const keyVerification : ReqHandler = (req, res, next) => { const key = req.query['key']?.toString() if (!key) { return res .status(403) .send({ error: true, message: 'no key query was present' }) } - const instance = WhatsAppInstances[key] + const instance = getInstanceForReq(req) if (!instance) { return res .status(403) @@ -14,4 +17,4 @@ function keyVerification(req, res, next) { next() } -module.exports = keyVerification +export default keyVerification diff --git a/src/api/middlewares/loginCheck.ts b/src/api/middlewares/loginCheck.ts index ae40cfe5..37e78c6f 100644 --- a/src/api/middlewares/loginCheck.ts +++ b/src/api/middlewares/loginCheck.ts @@ -1,11 +1,14 @@ -function loginVerification(req, res, next) { +import { ReqHandler } from "../helper/types" +import getInstanceForReq from "../service/instance" + +export const loginVerification : ReqHandler = (req, res, next) => { const key = req.query['key']?.toString() if (!key) { return res .status(403) .send({ error: true, message: 'no key query was present' }) } - const instance = WhatsAppInstances[key] + const instance = getInstanceForReq(req) if (!instance.instance?.online) { return res .status(401) @@ -14,4 +17,4 @@ function loginVerification(req, res, next) { next() } -module.exports = loginVerification +export default loginVerification diff --git a/src/api/middlewares/tokenCheck.ts b/src/api/middlewares/tokenCheck.ts index 6e70cae0..2d75db89 100644 --- a/src/api/middlewares/tokenCheck.ts +++ b/src/api/middlewares/tokenCheck.ts @@ -1,6 +1,7 @@ -const config = require('../../config/config') +import config from '../../config/config' +import { ReqHandler } from '../helper/types' -function tokenVerification(req, res, next) { +export const tokenVerification : ReqHandler = (req, res, next) => { const bearer = req.headers.authorization const token = bearer?.slice(7)?.toString() if (!token) { @@ -18,4 +19,4 @@ function tokenVerification(req, res, next) { next() } -module.exports = tokenVerification +export default tokenVerification diff --git a/src/api/models/chat.model.ts b/src/api/models/chat.model.ts index 5e11760b..6e9b47e4 100644 --- a/src/api/models/chat.model.ts +++ b/src/api/models/chat.model.ts @@ -1,16 +1,49 @@ -const mongoose = require('mongoose') +import mongoose from 'mongoose' -const chatSchema = new mongoose.Schema({ +export interface ChatParticipant { + id: string; + admin: string | null; +} + +const chatParticipant = new mongoose.Schema({ + id: { type: String, required: true }, + admin: { type: String, required: true }, +}) + +export interface ChatType { + id: string; + name: string; + participant?: ChatParticipant[]; + messages?: any[]; + creation?: number; + subjectOwner?: string; +} + +const chatType = new mongoose.Schema({ + id: { type: String, required: true }, + name: { type: String, required: true }, + participant: Array(chatParticipant), + messages: Array(), + creation: { type: Number }, + subjectOwner: { type: String }, +}) + +export interface Chat { + key: string; + chat: ChatType[]; +} + +const chatSchema = new mongoose.Schema({ key: { type: String, required: [true, 'key is missing'], unique: true, }, chat: { - type: Array, + type: Array(chatType), }, }) const Chat = mongoose.model('Chat', chatSchema) -module.exports = Chat +export default Chat diff --git a/src/api/routes/group.route.ts b/src/api/routes/group.route.ts index 88aa25fc..b4c327b0 100644 --- a/src/api/routes/group.route.ts +++ b/src/api/routes/group.route.ts @@ -1,44 +1,24 @@ -const express = require('express') -const controller = require('../controllers/group.controller') -const keyVerify = require('../middlewares/keyCheck') -const loginVerify = require('../middlewares/loginCheck') +import express from 'express' +import * as controller from '../controllers/group.controller' +import keyVerify from '../middlewares/keyCheck' +import loginVerify from '../middlewares/loginCheck' const router = express.Router() router.route('/create').post(keyVerify, loginVerify, controller.create) router.route('/listall').get(keyVerify, loginVerify, controller.listAll) router.route('/leave').get(keyVerify, loginVerify, controller.leaveGroup) - -router - .route('/inviteuser') - .post(keyVerify, loginVerify, controller.addNewParticipant) +router.route('/inviteuser').post(keyVerify, loginVerify, controller.addNewParticipant) router.route('/makeadmin').post(keyVerify, loginVerify, controller.makeAdmin) -router - .route('/demoteadmin') - .post(keyVerify, loginVerify, controller.demoteAdmin) -router - .route('/getinvitecode') - .get(keyVerify, loginVerify, controller.getInviteCodeGroup) -router - .route('/getinstanceinvitecode') - .get(keyVerify, loginVerify, controller.getInstanceInviteCodeGroup) -router - .route('/getallgroups') - .get(keyVerify, loginVerify, controller.getAllGroups) -router - .route('/participantsupdate') - .post(keyVerify, loginVerify, controller.groupParticipantsUpdate) -router - .route('/settingsupdate') - .post(keyVerify, loginVerify, controller.groupSettingUpdate) -router - .route('/updatesubject') - .post(keyVerify, loginVerify, controller.groupUpdateSubject) -router - .route('/updatedescription') - .post(keyVerify, loginVerify, controller.groupUpdateDescription) -router - .route('/inviteinfo') - .post(keyVerify, loginVerify, controller.groupInviteInfo) +router.route('/demoteadmin').post(keyVerify, loginVerify, controller.demoteAdmin) +router.route('/getinvitecode').get(keyVerify, loginVerify, controller.getInviteCodeGroup) +router.route('/getinstanceinvitecode').get(keyVerify, loginVerify, controller.getInstanceInviteCodeGroup) +router.route('/getallgroups').get(keyVerify, loginVerify, controller.getAllGroups) +router.route('/participantsupdate').post(keyVerify, loginVerify, controller.groupParticipantsUpdate) +router.route('/settingsupdate').post(keyVerify, loginVerify, controller.groupSettingUpdate) +router.route('/updatesubject').post(keyVerify, loginVerify, controller.groupUpdateSubject) +router.route('/updatedescription').post(keyVerify, loginVerify, controller.groupUpdateDescription) +router.route('/inviteinfo').post(keyVerify, loginVerify, controller.groupInviteInfo) router.route('/groupjoin').post(keyVerify, loginVerify, controller.groupJoin) -module.exports = router + +export default router diff --git a/src/api/routes/index.ts b/src/api/routes/index.ts index 3f3acac2..2e474fa1 100644 --- a/src/api/routes/index.ts +++ b/src/api/routes/index.ts @@ -1,9 +1,11 @@ -const express = require('express') + +import express from 'express' +import instanceRoutes from './instance.route' +import messageRoutes from './message.route' +import miscRoutes from './misc.route' +import groupRoutes from './group.route' + const router = express.Router() -const instanceRoutes = require('./instance.route') -const messageRoutes = require('./message.route') -const miscRoutes = require('./misc.route') -const groupRoutes = require('./group.route') router.get('/status', (req, res) => res.send('OK')) router.use('/instance', instanceRoutes) @@ -11,4 +13,4 @@ router.use('/message', messageRoutes) router.use('/group', groupRoutes) router.use('/misc', miscRoutes) -module.exports = router +export default router diff --git a/src/api/routes/instance.route.ts b/src/api/routes/instance.route.ts index e357b1de..e0400b35 100644 --- a/src/api/routes/instance.route.ts +++ b/src/api/routes/instance.route.ts @@ -1,16 +1,17 @@ -const express = require('express') -const controller = require('../controllers/instance.controller') -const keyVerify = require('../middlewares/keyCheck') -const loginVerify = require('../middlewares/loginCheck') +import express from 'express' +import * as controller from '../controllers/instance.controller' +import keyVerify from '../middlewares/keyCheck' +import loginVerify from '../middlewares/loginCheck' const router = express.Router() + router.route('/init').get(controller.init) router.route('/qr').get(keyVerify, controller.qr) router.route('/qrbase64').get(keyVerify, controller.qrbase64) router.route('/info').get(keyVerify, controller.info) router.route('/restore').get(controller.restore) router.route('/logout').delete(keyVerify, loginVerify, controller.logout) -router.route('/delete').delete(keyVerify, controller.delete) +router.route('/delete').delete(keyVerify, controller.remove) router.route('/list').get(controller.list) -module.exports = router +export default router diff --git a/src/api/routes/message.route.ts b/src/api/routes/message.route.ts index 62bbdba5..9d65c0dc 100644 --- a/src/api/routes/message.route.ts +++ b/src/api/routes/message.route.ts @@ -1,8 +1,8 @@ -const express = require('express') -const controller = require('../controllers/message.controller') -const keyVerify = require('../middlewares/keyCheck') -const loginVerify = require('../middlewares/loginCheck') -const multer = require('multer') +import express from 'express' +import * as controller from '../controllers/message.controller' +import keyVerify from '../middlewares/keyCheck' +import loginVerify from '../middlewares/loginCheck' +import multer from 'multer' const router = express.Router() const storage = multer.memoryStorage() @@ -18,10 +18,8 @@ router.route('/button').post(keyVerify, loginVerify, controller.Button) router.route('/contact').post(keyVerify, loginVerify, controller.Contact) router.route('/list').post(keyVerify, loginVerify, controller.List) router.route('/setstatus').put(keyVerify, loginVerify, controller.SetStatus) -router - .route('/mediabutton') - .post(keyVerify, loginVerify, controller.MediaButton) +router.route('/mediabutton').post(keyVerify, loginVerify, controller.MediaButton) router.route("/read").post(keyVerify, loginVerify, controller.Read) router.route("/react").post(keyVerify, loginVerify, controller.React) -module.exports = router +export default router diff --git a/src/api/routes/misc.route.ts b/src/api/routes/misc.route.ts index 56669a53..8af32792 100644 --- a/src/api/routes/misc.route.ts +++ b/src/api/routes/misc.route.ts @@ -1,7 +1,7 @@ -const express = require('express') -const controller = require('../controllers/misc.controller') -const keyVerify = require('../middlewares/keyCheck') -const loginVerify = require('../middlewares/loginCheck') +import express from 'express' +import * as controller from '../controllers/misc.controller' +import keyVerify from '../middlewares/keyCheck' +import loginVerify from '../middlewares/loginCheck' const router = express.Router() @@ -9,10 +9,7 @@ router.route('/onwhatsapp').get(keyVerify, loginVerify, controller.onWhatsapp) router.route('/downProfile').get(keyVerify, loginVerify, controller.downProfile) router.route('/getStatus').get(keyVerify, loginVerify, controller.getStatus) router.route('/blockUser').get(keyVerify, loginVerify, controller.blockUser) -router - .route('/updateProfilePicture') - .post(keyVerify, loginVerify, controller.updateProfilePicture) -router - .route('/getuserorgroupbyid') - .get(keyVerify, loginVerify, controller.getUserOrGroupById) -module.exports = router +router.route('/updateProfilePicture').post(keyVerify, loginVerify, controller.updateProfilePicture) +router.route('/getuserorgroupbyid').get(keyVerify, loginVerify, controller.getUserOrGroupById) + +export default router diff --git a/src/api/service/database.ts b/src/api/service/database.ts new file mode 100644 index 00000000..327d992a --- /dev/null +++ b/src/api/service/database.ts @@ -0,0 +1,23 @@ +import { Document, Collection, MongoClient } from 'mongodb' + +import { AppType } from "../helper/types"; + +interface Database +{ + mongoClient: MongoClient +} + +export type CollectionType = Collection + +export function initDatabaseService(app: AppType) { + app.set('databaseService', {}); +} + +export function getDatabaseService(app: AppType): Database { + const DatabaseService: Database = app.get('databaseService'); + return DatabaseService; +} + +export default function getDatabase(app: AppType) { + return getDatabaseService(app).mongoClient.db('whatsapp-api') +} diff --git a/src/api/service/instance.ts b/src/api/service/instance.ts new file mode 100644 index 00000000..f21a8c81 --- /dev/null +++ b/src/api/service/instance.ts @@ -0,0 +1,20 @@ +import WhatsAppInstance from "../class/instance"; +import { AppType, ReqType } from "../helper/types"; + +interface Instance +{ + instances: Record; +} + +export function initInstanceService(app: AppType) { + app.set('instanceService', { instances: {} }); +} + +export function getInstanceService(app: AppType): Instance { + const instanceService: Instance = app.get('instanceService'); + return instanceService; +} + +export default function getInstanceForReq(req: ReqType): WhatsAppInstance { + return getInstanceService(req.app).instances[ req.query.key]; +} diff --git a/src/config/config.ts b/src/config/config.ts index fcc94d99..d367d59a 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -1,3 +1,4 @@ + // Port number const PORT = process.env.PORT || '3333' const TOKEN = process.env.TOKEN || '' @@ -44,7 +45,7 @@ const MARK_MESSAGES_READ = !!( process.env.MARK_MESSAGES_READ && process.env.MARK_MESSAGES_READ === 'true' ) -module.exports = { +export default { port: PORT, token: TOKEN, restoreSessionsOnStartup: RESTORE_SESSIONS_ON_START_UP, @@ -60,8 +61,8 @@ module.exports = { url: MONGODB_URL, options: { // useCreateIndex: true, - useNewUrlParser: true, - useUnifiedTopology: true, + // useNewUrlParser: true, + // useUnifiedTopology: true, }, }, browser: { diff --git a/src/config/express-exception-handler.d.ts b/src/config/express-exception-handler.d.ts new file mode 100644 index 00000000..d97b5b20 --- /dev/null +++ b/src/config/express-exception-handler.d.ts @@ -0,0 +1,2 @@ + +declare module 'express-exception-handler'; diff --git a/src/config/express.ts b/src/config/express.ts index d8435a2f..c990587f 100644 --- a/src/config/express.ts +++ b/src/config/express.ts @@ -1,24 +1,29 @@ -const express = require('express') -const path = require('path') -const exceptionHandler = require('express-exception-handler') +import express from 'express' +import path from 'path' +import exceptionHandler from 'express-exception-handler' exceptionHandler.handle() const app = express() -const error = require('../api/middlewares/error') -const tokenCheck = require('../api/middlewares/tokenCheck') -const { protectRoutes } = require('./config') +import * as error from '../api/middlewares/error' +import tokenCheck from '../api/middlewares/tokenCheck' +import config from './config' app.use(express.json()) app.use(express.json({ limit: '50mb' })) app.use(express.urlencoded({ extended: true })) + app.set('view engine', 'ejs') app.set('views', path.join(__dirname, '../api/views')) -global.WhatsAppInstances = {} -const routes = require('../api/routes/') -if (protectRoutes) { +import { initDatabaseService } from '../api/service/database' +initDatabaseService(app) +import { initInstanceService } from '../api/service/instance' +initInstanceService(app) + +import routes from '../api/routes/' +if (config.protectRoutes) { app.use(tokenCheck) } app.use('/', routes) app.use(error.handler) -module.exports = app +export default app diff --git a/src/server.ts b/src/server.ts index e5849de8..11cd1f74 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,15 +1,16 @@ -const dotenv = require('dotenv') -const mongoose = require('mongoose') -const logger = require('pino')() -dotenv.config() +import dotenv from 'dotenv' +import mongoose from 'mongoose' +import pino from 'pino' -const app = require('./config/express') -const config = require('./config/config') +dotenv.config() +const logger = pino() -const { Session } = require('./api/class/session') -const connectToCluster = require('./api/helper/connectMongoClient') +import app from './config/express' +import config from './config/config' -let server +import Session from './api/class/session' +import connectToCluster from './api/helper/connectMongoClient' +import { ErrHandler } from './api/helper/types' if (config.mongoose.enabled) { mongoose.set('strictQuery', true); @@ -18,12 +19,12 @@ if (config.mongoose.enabled) { }) } -server = app.listen(config.port, async () => { +const server = app.listen(config.port, async () => { logger.info(`Listening on port ${config.port}`) - global.mongoClient = await connectToCluster(config.mongoose.url) + await connectToCluster(app, config.mongoose.url) if (config.restoreSessionsOnStartup) { logger.info(`Restoring Sessions`) - const session = new Session() + const session = new Session(app) let restoreSessions = await session.restoreSessions() logger.info(`${restoreSessions.length} Session(s) Restored`) } @@ -40,7 +41,7 @@ const exitHandler = () => { } } -const unexpectedErrorHandler = (error) => { +const unexpectedErrorHandler : ErrHandler = (error) => { logger.error(error) exitHandler() } @@ -55,4 +56,4 @@ process.on('SIGTERM', () => { } }) -module.exports = server +export default server diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..2e19a316 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "Node", + "outDir": "lib", + "strict": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "noImplicitThis": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "forceConsistentCasingInFileNames": true, + }, + "include": ["./src/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/yarn.lock b/yarn.lock index 0761b5aa..7e66d5d5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,19 +2,10 @@ # yarn lockfile v1 -"@adiwajshing/baileys@https://github.com/WhiskeySockets/Baileys.git": - version "5.0.0" - resolved "https://github.com/WhiskeySockets/Baileys.git#13810ec7ea3700ec0e1b676869a2522993827de5" - dependencies: - "@hapi/boom" "^9.1.3" - axios "^1.3.3" - futoin-hkdf "^1.5.1" - libsignal "https://github.com/adiwajshing/libsignal-node.git" - music-metadata "^7.12.3" - node-cache "^5.1.2" - pino "^7.0.0" - protobufjs "^6.11.3" - ws "^8.0.0" +"@adiwajshing/keyed-db@^0.2.4": + version "0.2.4" + resolved "https://registry.yarnpkg.com/@adiwajshing/keyed-db/-/keyed-db-0.2.4.tgz#2a09e88fce20b2672deb60a7750c5fe3ab0dfd99" + integrity sha512-yprSnAtj80/VKuDqRcFFLDYltoNV8tChNwFfIgcf6PGD4sjzWIBgs08pRuTqGH5mk5wgL6PBRSsMCZqtZwzFEw== "@aws-crypto/ie11-detection@^2.0.0": version "2.0.2" @@ -702,6 +693,11 @@ "@aws-sdk/util-buffer-from" "3.188.0" tslib "^2.3.1" +"@eshaz/web-worker@1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@eshaz/web-worker/-/web-worker-1.2.1.tgz#834385830529582589e9790350beb24bf3ac8018" + integrity sha512-v5AKAVtM0toVD2rDCGjzhySWlXG/sG5HVialdzrxFKTAnFZNCjQelX0n2tPK0tE86jf4s3hpWlpRtOh8OObktg== + "@eslint/eslintrc@^1.3.3": version "1.3.3" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.3.tgz#2b044ab39fdfa75b4688184f9e573ce3c5b0ff95" @@ -717,6 +713,13 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" +"@hapi/boom@^10.0.1": + version "10.0.1" + resolved "https://registry.yarnpkg.com/@hapi/boom/-/boom-10.0.1.tgz#ebb14688275ae150aa6af788dbe482e6a6062685" + integrity sha512-ERcCZaEjdH3OgSJlyjVk8pHIFeus91CjKP3v+MpgBNp5IvGzP2l/bRiD78nqYcKPaZdbKkK5vDBVPd2ohHBlsA== + dependencies: + "@hapi/hoek" "^11.0.2" + "@hapi/boom@^9.1.3": version "9.1.4" resolved "https://registry.yarnpkg.com/@hapi/boom/-/boom-9.1.4.tgz#1f9dad367c6a7da9f8def24b4a986fc5a7bd9db6" @@ -729,6 +732,11 @@ resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb" integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ== +"@hapi/hoek@^11.0.2": + version "11.0.2" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-11.0.2.tgz#cb3ea547daac7de5c9cf1d960c3f35c34f065427" + integrity sha512-aKmlCO57XFZ26wso4rJsW4oTUnrgTFw2jh3io7CAtO9w4UltBNwRXvXIVzzyfkaaLRo3nluP/19msA8vDUUuKw== + "@humanwhocodes/config-array@^0.11.6": version "0.11.6" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.6.tgz#6a51d603a3aaf8d4cf45b42b3f2ac9318a4adc4b" @@ -822,16 +830,85 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== +"@thi.ng/bitstream@^2.2.12": + version "2.2.22" + resolved "https://registry.yarnpkg.com/@thi.ng/bitstream/-/bitstream-2.2.22.tgz#e0442480f13bf3741637715006acedfe215c4f10" + integrity sha512-phKOd7gfDcZfiu5qHr2etak9D1jczY8tW5vojaiAwEwCA09ZkYTuiTlrBJdTGnqjc3//1Kb6O3EQo7two5GlbQ== + dependencies: + "@thi.ng/errors" "^2.2.17" + +"@thi.ng/errors@^2.2.17": + version "2.2.17" + resolved "https://registry.yarnpkg.com/@thi.ng/errors/-/errors-2.2.17.tgz#0a4cc59e4a2a3c607db65052f71be62d805dff9a" + integrity sha512-gL1KOHVQpSX9e5BDbKzxJcXj+tM2T6dJOxXTJz3nHNSXT6VdEsXCVeLKGUFBkLkA5QsH/nzmgQq7a5vS7cdGTA== + "@tokenizer/token@^0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.3.0.tgz#fe98a93fe789247e998c75e74e9c7c63217aa276" integrity sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A== +"@types/body-parser@*": + version "1.19.2" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" + integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.35" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" + integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== + dependencies: + "@types/node" "*" + +"@types/express-serve-static-core@^4.17.33": + version "4.17.35" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz#c95dd4424f0d32e525d23812aa8ab8e4d3906c4f" + integrity sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/express@*", "@types/express@^4.17.17": + version "4.17.17" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.17.tgz#01d5437f6ef9cfa8668e616e13c2f2ac9a491ae4" + integrity sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.33" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/http-errors@*": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.1.tgz#20172f9578b225f6c7da63446f56d4ce108d5a65" + integrity sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ== + "@types/long@^4.0.0", "@types/long@^4.0.1": version "4.0.2" resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== +"@types/mime@*": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" + integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== + +"@types/mime@^1": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" + integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== + +"@types/multer@^1.4.7": + version "1.4.7" + resolved "https://registry.yarnpkg.com/@types/multer/-/multer-1.4.7.tgz#89cf03547c28c7bbcc726f029e2a76a7232cc79e" + integrity sha512-/SNsDidUFCvqqcWDwxv2feww/yqhNeTRL5CVoL3jU4Goc4kKEL10T7Eye65ZqPNi4HRx8sAEX59pV1aEH7drNA== + dependencies: + "@types/express" "*" + "@types/node@*", "@types/node@>=13.7.0": version "18.0.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.0.tgz#67c7b724e1bcdd7a8821ce0d5ee184d3b4dd525a" @@ -842,6 +919,45 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== +"@types/qrcode@^1.5.1": + version "1.5.1" + resolved "https://registry.yarnpkg.com/@types/qrcode/-/qrcode-1.5.1.tgz#027c2dbfbc8505e1fe2f4033daba920dbd182b44" + integrity sha512-HpSN675K0PmxIDRpjMI3Mc2GiKo3dNu+X/F5SoItiaDS1lVfgC6Wac1c5lQDfKWbTJUSHWiHKzpJpBZG7k9gaA== + dependencies: + "@types/node" "*" + +"@types/qs@*": + version "6.9.7" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" + integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== + +"@types/range-parser@*": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" + integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== + +"@types/send@*": + version "0.17.1" + resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.1.tgz#ed4932b8a2a805f1fe362a70f4e62d0ac994e301" + integrity sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/serve-static@*": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.2.tgz#3e5419ecd1e40e7405d34093f10befb43f63381a" + integrity sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw== + dependencies: + "@types/http-errors" "*" + "@types/mime" "*" + "@types/node" "*" + +"@types/uuid@^9.0.2": + version "9.0.2" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.2.tgz#ede1d1b1e451548d44919dc226253e32a6952c4b" + integrity sha512-kNnC1GFBLuhImSnV7w4njQkUiJi0ZXUycu1rUaouPqiKlXkh77JKgdRnTAp1x5eBwcIwbtI+3otwzuIDEuDoxQ== + "@types/webidl-conversions@*": version "6.1.1" resolved "https://registry.yarnpkg.com/@types/webidl-conversions/-/webidl-conversions-6.1.1.tgz#e33bc8ea812a01f63f90481c666334844b12a09e" @@ -855,20 +971,55 @@ "@types/node" "*" "@types/webidl-conversions" "*" -"@whiskeysockets/baileys@^6.1.0": - version "6.1.0" - resolved "https://registry.yarnpkg.com/@whiskeysockets/baileys/-/baileys-6.1.0.tgz#a3776c5769586e0436c396689e065226b976e330" - integrity sha512-Uf2DIGzW0wRL+jcFmEsQjrr57CpWgwQfvEH1WSo8zJBRzezr+R2dzEeM7CMMpcQ9YP5VzRLXZodpkK7DUAy96w== +"@types/ws@^8.5.5": + version "8.5.5" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.5.tgz#af587964aa06682702ee6dcbc7be41a80e4b28eb" + integrity sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg== dependencies: + "@types/node" "*" + +"@wasm-audio-decoders/common@9.0.1": + version "9.0.1" + resolved "https://registry.yarnpkg.com/@wasm-audio-decoders/common/-/common-9.0.1.tgz#985f35d7f216da1b0f691132c3875793cc516022" + integrity sha512-s4KxPsxhD6e+EtjmHPzTmKJSJTEXHGYlBzUGGLN/plV01x3RecybW73nIMtdLLXL8k/zQ8HYFcA6d9lqCJcDjQ== + dependencies: + "@eshaz/web-worker" "1.2.1" + +"@wasm-audio-decoders/flac@^0.1.12": + version "0.1.12" + resolved "https://registry.yarnpkg.com/@wasm-audio-decoders/flac/-/flac-0.1.12.tgz#5e35512e8c0d4079c0926341cc37ad3a88e94e83" + integrity sha512-k4PELVzBu4p4j0YmA2K8VF0GxoWWYqkqa3C29nOikOss1SZLnX2CczfiJCklL9AA/9tlZrvBJz3/nDxXDqR35A== + dependencies: + "@wasm-audio-decoders/common" "9.0.1" + codec-parser "2.4.2" + +"@wasm-audio-decoders/ogg-vorbis@^0.1.7": + version "0.1.7" + resolved "https://registry.yarnpkg.com/@wasm-audio-decoders/ogg-vorbis/-/ogg-vorbis-0.1.7.tgz#a79e039db0cb56f6ff44f7d694fba71506186dee" + integrity sha512-MftO1fJeBRUuDAZfa9Yp/gf+786PWt/JyiFXj+Ntn0mxlKDyPr3pTXx8PvhmO2/s/nabHDhAhiZi4aXzaBEUpg== + dependencies: + "@wasm-audio-decoders/common" "9.0.1" + codec-parser "2.4.2" + +"@whiskeysockets/baileys@^6.4.0": + version "6.4.0" + resolved "https://registry.yarnpkg.com/@whiskeysockets/baileys/-/baileys-6.4.0.tgz#7effbec2d0d7529ce4ee338024fb65fb28b87089" + integrity sha512-SdCoVpILWX7z2WEe40BwNjMqpI6Guf0gbG80NNfsJBcRcDQIS9pkmazIhTvNKytWZm7BFZR+QNKcBUxdPqdLsQ== + dependencies: + "@adiwajshing/keyed-db" "^0.2.4" "@hapi/boom" "^9.1.3" + audio-decode "^2.1.3" axios "^1.3.3" + cache-manager "^5.2.2" futoin-hkdf "^1.5.1" - libsignal "https://github.com/adiwajshing/libsignal-node.git" + libphonenumber-js "^1.10.20" + libsignal "github:adiwajshing/libsignal-node" music-metadata "^7.12.3" node-cache "^5.1.2" pino "^7.0.0" protobufjs "^6.11.3" - ws "^8.0.0" + uuid "^9.0.0" + ws "^8.13.0" abbrev@1: version "1.1.1" @@ -1000,6 +1151,30 @@ atomic-sleep@^1.0.0: resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== +audio-buffer@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/audio-buffer/-/audio-buffer-5.0.0.tgz#92129984ebdbb8ad1c4ec6fd81525a0e0a29e99e" + integrity sha512-gsDyj1wwUp8u7NBB+eW6yhLb9ICf+0eBmDX8NGaAS00w8/fLqFdxUlL5Ge/U8kB64DlQhdonxYC59dXy1J7H/w== + +audio-decode@^2.1.3: + version "2.1.4" + resolved "https://registry.yarnpkg.com/audio-decode/-/audio-decode-2.1.4.tgz#86e7eb2ce62661037949ce19989e5464ac1e7a7d" + integrity sha512-i1KTHqBXcU3vVCPXfp4zZCaXsLYyO4CNbt6JhvV1so/PpOpBnIXNFgHEq0bWNsroUV/J6i2/Yck0QhoaCzGh+A== + dependencies: + "@wasm-audio-decoders/flac" "^0.1.12" + "@wasm-audio-decoders/ogg-vorbis" "^0.1.7" + audio-buffer "^5.0.0" + audio-type "^2.2.0" + mpg123-decoder "^0.4.8" + node-wav "^0.0.2" + ogg-opus-decoder "^1.6.4" + qoa-format "^1.0.0" + +audio-type@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/audio-type/-/audio-type-2.2.1.tgz#5ceae0b0b3cf3b1e117edabb9e5332eaf3bd4f25" + integrity sha512-En9AY6EG1qYqEy5L/quryzbA4akBpJrnBZNxeKTqGHC2xT9Qc4aZ8b7CcbOMFTTc/MGdoNyp+SN4zInZNKxMYA== + axios@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/axios/-/axios-1.1.3.tgz#8274250dada2edf53814ed7db644b9c2866c1e35" @@ -1129,6 +1304,14 @@ bytes@3.1.2: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== +cache-manager@^5.2.2: + version "5.2.3" + resolved "https://registry.yarnpkg.com/cache-manager/-/cache-manager-5.2.3.tgz#b6a8b4469c57fdfdae1deed7f81ea9e057c7eade" + integrity sha512-9OErI8fksFkxAMJ8Mco0aiZSdphyd90HcKiOMJQncSlU1yq/9lHHxrT8PDayxrmr9IIIZPOAEfXuGSD7g29uog== + dependencies: + lodash.clonedeep "^4.5.0" + lru-cache "^9.1.2" + call-bind@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" @@ -1257,6 +1440,11 @@ clone@2.x: resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w== +codec-parser@2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/codec-parser/-/codec-parser-2.4.2.tgz#32a3271c9a79711cd61effeeea4b5b4d8b52efb8" + integrity sha512-RN6gT8aGdDq4vx//Ln+MRDIKPXA3SpJKsCcQSZ4poF2bZCO4/G0C2Ko/MVNpbJ7Y9ewpzeuLlBx2zH8BpcM4ew== + color-convert@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" @@ -2272,17 +2460,14 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -"libsignal@git+https://github.com/adiwajshing/libsignal-node.git": - version "2.0.1" - uid "11dbd962ea108187c79a7c46fe4d6f790e23da97" - resolved "git+https://github.com/adiwajshing/libsignal-node.git#11dbd962ea108187c79a7c46fe4d6f790e23da97" - dependencies: - curve25519-js "^0.0.4" - protobufjs "6.8.8" +libphonenumber-js@^1.10.20: + version "1.10.38" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.38.tgz#edb9c1f13796ab17a8484180ef937184784628e9" + integrity sha512-4NjVXVUmpZ9Zsqq6FXa2+MKI+KAI3tOqA0pxXgXGluhpj4ge5didmbWJpMBqGB3AVGv1SnEtKdGTbxjSEG1kCQ== -"libsignal@https://github.com/adiwajshing/libsignal-node.git": +"libsignal@github:adiwajshing/libsignal-node": version "2.0.1" - resolved "https://github.com/adiwajshing/libsignal-node.git#11dbd962ea108187c79a7c46fe4d6f790e23da97" + resolved "https://codeload.github.com/adiwajshing/libsignal-node/tar.gz/11dbd962ea108187c79a7c46fe4d6f790e23da97" dependencies: curve25519-js "^0.0.4" protobufjs "6.8.8" @@ -2349,6 +2534,11 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" @@ -2384,6 +2574,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lru-cache@^9.1.2: + version "9.1.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-9.1.2.tgz#255fdbc14b75589d6d0e73644ca167a8db506835" + integrity sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ== + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -2575,6 +2770,13 @@ mpath@0.9.0: resolved "https://registry.yarnpkg.com/mpath/-/mpath-0.9.0.tgz#0c122fe107846e31fc58c75b09c35514b3871904" integrity sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew== +mpg123-decoder@^0.4.8: + version "0.4.8" + resolved "https://registry.yarnpkg.com/mpg123-decoder/-/mpg123-decoder-0.4.8.tgz#a2b5e77f491c7b7bf8b896e4764d71dcaec56e59" + integrity sha512-HXs8vbPjiFM0NOZ45T3C5i7mpYGEYhjH37SnFA907lOb9c93DQL40cDjerxj65IMwAYyPFq1aUjtDOjyR7O0gQ== + dependencies: + "@wasm-audio-decoders/common" "9.0.1" + mquery@4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/mquery/-/mquery-4.0.3.tgz#4d15f938e6247d773a942c912d9748bd1965f89d" @@ -2669,6 +2871,11 @@ node-fetch@2.6.7: dependencies: whatwg-url "^5.0.0" +node-wav@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/node-wav/-/node-wav-0.0.2.tgz#89cb63cf8cd66ec8ab455f5ba4864e5fcb4605e8" + integrity sha512-M6Rm/bbG6De/gKGxOpeOobx/dnGuP0dz40adqx38boqHhlWssBJZgLCPBNtb9NkrmnKYiV04xELq+R6PFOnoLA== + nodemon@^2.0.20: version "2.0.20" resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.20.tgz#e3537de768a492e8d74da5c5813cb0c7486fc701" @@ -2721,6 +2928,15 @@ object-inspect@^1.12.2, object-inspect@^1.9.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== +ogg-opus-decoder@^1.6.4: + version "1.6.4" + resolved "https://registry.yarnpkg.com/ogg-opus-decoder/-/ogg-opus-decoder-1.6.4.tgz#1f184c041994ca5e22da6ade05e2ccbaf8f22014" + integrity sha512-MYqiJyCZWjIKKRJMXRVi69D3VGbaR/FNCNcINNI5Ec5ZUm5pLyDT6f0cYksU0Hb3ZHXXQIp62VJChYqwk60EMg== + dependencies: + "@wasm-audio-decoders/common" "9.0.1" + codec-parser "2.4.2" + opus-decoder "0.7.1" + on-exit-leak-free@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-0.2.0.tgz#b39c9e3bf7690d890f4861558b0d7b90a442d209" @@ -2771,6 +2987,13 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" +opus-decoder@0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/opus-decoder/-/opus-decoder-0.7.1.tgz#48c50f1feb0b49fa078ade93f08caeff9142ffae" + integrity sha512-AOFCMKLn7LJm8pOkksY5TsW/6+XmNyh1OQS9gxmdOGHLNYoOBrjSfc0nPNcmUMGEzOrTqZtPi8VJ/ABs2Hndvg== + dependencies: + "@wasm-audio-decoders/common" "9.0.1" + p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -3060,6 +3283,13 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +qoa-format@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/qoa-format/-/qoa-format-1.0.0.tgz#1638adb119ed3a32cf65efdb2c7e278f6d64b2ef" + integrity sha512-Vjp2aV2x06tHbZesCi2UtISaFdlLzy47Vbt0rNwwdihKFvGtUeFfytdnA8XZYADqWtRbK19+XXeRkv1Stg4qSQ== + dependencies: + "@thi.ng/bitstream" "^2.2.12" + qrcode@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/qrcode/-/qrcode-1.5.1.tgz#0103f97317409f7bc91772ef30793a54cd59f0cb" @@ -3746,6 +3976,11 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== +typescript@^5.1.6: + version "5.1.6" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" + integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== + undefsafe@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" @@ -3867,10 +4102,10 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -ws@^8.0.0: - version "8.8.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.0.tgz#8e71c75e2f6348dbf8d78005107297056cb77769" - integrity sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ== +ws@^8.13.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== xtend@^4.0.0: version "4.0.2"