From 6721144b770f55a3195326c7f301e56cb062ac3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3nalan=20de=20Lima?= Date: Thu, 10 Jun 2021 18:05:52 -0300 Subject: [PATCH] feat: new functions --- src/api/layers/controls.layer.ts | 6 +- src/api/layers/retriever.layer.ts | 4 +- src/api/layers/sender.layer.ts | 9 +- src/controllers/initializer.ts | 400 ++++++++++-------- src/lib/wapi/functions/get-unread-messages.js | 25 +- src/lib/wapi/functions/index.js | 1 + .../wapi/functions/process-message-object.js | 9 +- src/lib/wapi/functions/send-message.js | 8 +- src/lib/wapi/functions/set-new-message.js | 19 + src/lib/wapi/serializers/serialize-message.js | 1 + src/lib/wapi/store/store-objects.js | 6 +- src/lib/wapi/wapi.js | 2 + src/types/WAPI.d.ts | 2 +- 13 files changed, 279 insertions(+), 213 deletions(-) create mode 100644 src/lib/wapi/functions/set-new-message.js diff --git a/src/api/layers/controls.layer.ts b/src/api/layers/controls.layer.ts index 231fa9ae4..4a87e65a2 100644 --- a/src/api/layers/controls.layer.ts +++ b/src/api/layers/controls.layer.ts @@ -22,7 +22,7 @@ export class ControlsLayer extends UILayer { */ public async unblockContact(contactId: string) { return this.page.evaluate( - (contactId) => WAPI.unblockContact(contactId), + (contactId: string) => WAPI.unblockContact(contactId), contactId ); } @@ -34,7 +34,7 @@ export class ControlsLayer extends UILayer { */ public async blockContact(contactId: string) { return this.page.evaluate( - (contactId) => WAPI.blockContact(contactId), + (contactId: string) => WAPI.blockContact(contactId), contactId ); } @@ -46,7 +46,7 @@ export class ControlsLayer extends UILayer { */ public async markUnseenMessage(contactId: string) { return this.page.evaluate( - (contactId) => WAPI.markUnseenMessage(contactId), + (contactId: string) => WAPI.markUnseenMessage(contactId), contactId ); } diff --git a/src/api/layers/retriever.layer.ts b/src/api/layers/retriever.layer.ts index fa74688fe..ceba2b6f5 100644 --- a/src/api/layers/retriever.layer.ts +++ b/src/api/layers/retriever.layer.ts @@ -231,7 +231,7 @@ export class RetrieverLayer extends SenderLayer { */ public async loadEarlierMessages(contactId: string) { return this.page.evaluate( - (contactId) => WAPI.loadEarlierMessages(contactId), + (contactId: string) => WAPI.loadEarlierMessages(contactId), contactId ); } @@ -242,7 +242,7 @@ export class RetrieverLayer extends SenderLayer { */ public async getStatus(contactId: string) { return this.page.evaluate( - (contactId) => WAPI.getStatus(contactId), + (contactId: string) => WAPI.getStatus(contactId), contactId ); } diff --git a/src/api/layers/sender.layer.ts b/src/api/layers/sender.layer.ts index 2a658f3b0..11ea2f06b 100644 --- a/src/api/layers/sender.layer.ts +++ b/src/api/layers/sender.layer.ts @@ -211,11 +211,12 @@ export class SenderLayer extends ListenerLayer { * @param to chat id: xxxxx@us.c * @param content text message * @param idMessage add id message + * @param passId new id */ public async sendText( to: string, content: string, - idMessage?: string + passId?: any ): Promise { return new Promise(async (resolve, reject) => { const typeFunction = 'sendText'; @@ -241,10 +242,10 @@ export class SenderLayer extends ListenerLayer { return reject(validating); } const result = await this.page.evaluate( - ({ to, content }) => { - return WAPI.sendMessage(to, content, undefined, idMessage); + ({ to, content, passId }) => { + return WAPI.sendMessage(to, content, undefined, passId); }, - { to, content } + { to, content, passId } ); if (result['erro'] == true) { return reject(result); diff --git a/src/controllers/initializer.ts b/src/controllers/initializer.ts index 0fd9d9ecc..25eeae2bd 100644 --- a/src/controllers/initializer.ts +++ b/src/controllers/initializer.ts @@ -25,6 +25,8 @@ export type CatchQR = ( */ export type StatusFind = (statusGet: string, session: string) => void; +export type ReconnectQrcode = (client: Whatsapp) => void; + /** * A callback will be received, informing user about browser and page instance */ @@ -72,7 +74,8 @@ export async function create( statusFind?: StatusFind, options?: CreateConfig, browserSessionToken?: tokenSession, - browserInstance?: BrowserInstance + browserInstance?: BrowserInstance, + reconnectQrcode?: ReconnectQrcode ): Promise; export async function create( @@ -81,230 +84,259 @@ export async function create( statusFind?: StatusFind, options?: CreateConfig, browserSessionToken?: tokenSession, - browserInstance?: BrowserInstance + browserInstance?: BrowserInstance, + reconnectQrcode?: ReconnectQrcode ): Promise { let session = 'session'; + return new Promise(async (resolve, reject) => { + if ( + typeof sessionOrOption === 'string' && + sessionOrOption.replace(/\s/g, '').length + ) { + session = sessionOrOption.replace(/\s/g, ''); + } else if (typeof sessionOrOption === 'object') { + session = sessionOrOption.session; + catchQR = sessionOrOption.catchQR || catchQR; + statusFind = sessionOrOption.statusFind || statusFind; + browserSessionToken = + sessionOrOption.browserSessionToken || browserSessionToken; + browserInstance = sessionOrOption.browserInstance || browserInstance; + options = sessionOrOption; + } + let browserToken: any; - if ( - typeof sessionOrOption === 'string' && - sessionOrOption.replace(/\s/g, '').length - ) { - session = sessionOrOption.replace(/\s/g, ''); - } else if (typeof sessionOrOption === 'object') { - session = sessionOrOption.session; - catchQR = sessionOrOption.catchQR || catchQR; - statusFind = sessionOrOption.statusFind || statusFind; - browserSessionToken = - sessionOrOption.browserSessionToken || browserSessionToken; - browserInstance = sessionOrOption.browserInstance || browserInstance; - options = sessionOrOption; - } - let browserToken: any; - - const spinnies = getSpinnies({ - disableSpins: options ? options.disableSpins : false, - }); - - const mergedOptions = { ...defaultOptions, ...options }; + const spinnies = getSpinnies({ + disableSpins: options ? options.disableSpins : false, + }); - if (!mergedOptions.disableWelcome) { - welcomeScreen(); - } + const mergedOptions = { ...defaultOptions, ...options }; - if (mergedOptions.updatesLog) { - const ver = await checkUpdates(spinnies); - if (ver === false) { - throw `Unable to access: "https://www.npmjs.com", check your internet`; + if (!mergedOptions.disableWelcome) { + welcomeScreen(); } - } - // Initialize whatsapp - if (mergedOptions.browserWS) { - spinnies.add(`browser-${session}`, { - text: `Waiting... checking the wss server...`, - }); - } else { - spinnies.add(`browser-${session}`, { - text: 'Waiting... checking the browser...', - }); - } + if (mergedOptions.updatesLog) { + const ver = await checkUpdates(spinnies); + if (ver === false) { + return reject( + `Unable to access: "https://www.npmjs.com", check your internet` + ); + } + } - const browser = await initBrowser(session, mergedOptions); + // Initialize whatsapp + if (mergedOptions.browserWS) { + spinnies.add(`browser-${session}`, { + text: `Waiting... checking the wss server...`, + }); + } else { + spinnies.add(`browser-${session}`, { + text: 'Waiting... checking the browser...', + }); + } - // Erro of connect wss - if (typeof browser === 'string' && browser === 'connect') { - spinnies.fail(`browser-${session}`, { - text: `Error when try to connect ${mergedOptions.browserWS}`, - }); - statusFind && statusFind('serverWssNotConnected', this.session); - throw `Error when try to connect ${mergedOptions.browserWS}`; - } - - // Erro open browser - if (typeof browser === 'string' && browser === 'launch') { - spinnies.fail(`browser-${session}`, { - text: `Error no open browser....`, - }); - statusFind && statusFind('noOpenBrowser', this.session); - throw `Error no open browser....`; - } + const browser = await initBrowser(session, mergedOptions); - if (mergedOptions.browserWS) { - spinnies.succeed(`browser-${session}`, { - text: `Has been properly connected to the wss server`, - }); - } else { - spinnies.succeed(`browser-${session}`, { - text: `Browser successfully opened`, - }); - } + // Erro of connect wss + if (typeof browser === 'string' && browser === 'connect') { + spinnies.fail(`browser-${session}`, { + text: `Error when try to connect ${mergedOptions.browserWS}`, + }); + statusFind && statusFind('serverWssNotConnected', this.session); + return reject(`Error when try to connect ${mergedOptions.browserWS}`); + } - if (!mergedOptions.browserWS) { - spinnies.add(`browser-${session}`, { - text: 'checking headless...', - }); + // Erro open browser + if (typeof browser === 'string' && browser === 'launch') { + spinnies.fail(`browser-${session}`, { + text: `Error no open browser....`, + }); + statusFind && statusFind('noOpenBrowser', this.session); + return reject(`Error no open browser....`); + } - if (mergedOptions.headless) { + if (mergedOptions.browserWS) { spinnies.succeed(`browser-${session}`, { - text: 'headless option is active, browser hidden', + text: `Has been properly connected to the wss server`, }); } else { spinnies.succeed(`browser-${session}`, { - text: 'headless option is disabled, browser visible', + text: `Browser successfully opened`, }); } - } - if (typeof browser === 'object') { - if (!mergedOptions.browserWS && browser['_process']) { - browser['_process'].once('close', () => { - browser['isClose'] = true; + if (!mergedOptions.browserWS) { + spinnies.add(`browser-${session}`, { + text: 'checking headless...', }); - } - - checkingCloses(browser, mergedOptions, (result) => { - statusFind && statusFind(result, session); - }).catch(() => { - throw 'The client has been closed'; - }); - if (SessionTokenCkeck(browserSessionToken)) { - browserToken = browserSessionToken; + if (mergedOptions.headless) { + spinnies.succeed(`browser-${session}`, { + text: 'headless option is active, browser hidden', + }); + } else { + spinnies.succeed(`browser-${session}`, { + text: 'headless option is disabled, browser visible', + }); + } } - spinnies.add(`whatzapp-${session}`, { - text: 'Checking page...', - }); + if (typeof browser === 'object') { + if (!mergedOptions.browserWS && browser['_process']) { + browser['_process'].once('close', () => { + browser['isClose'] = true; + }); + } - // Initialize whatsapp - const page: false | Page = await initWhatsapp( - session, - mergedOptions, - browser, - browserToken - ); - - if (browserInstance) { - browserInstance(browser, page); - } + checkingCloses(browser, mergedOptions, (result) => { + statusFind && statusFind(result, session); + }).catch(() => { + return reject('The client has been closed'); + }); - if (page === false) { - spinnies.fail(`whatzapp-${session}`, { - text: 'Error accessing the page: "https://web.whatsapp.com"', + if (SessionTokenCkeck(browserSessionToken)) { + browserToken = browserSessionToken; + } + + spinnies.add(`whatzapp-${session}`, { + text: 'Checking page...', }); - throw 'Error when trying to access the page: "https://web.whatsapp.com"'; - } - spinnies.succeed(`whatzapp-${session}`, { - text: 'Page successfully accessed', - }); + // Initialize whatsapp + const page: false | Page = await initWhatsapp( + session, + mergedOptions, + browser, + browserToken + ); - const client = new Whatsapp(browser, page, session, mergedOptions); + if (page === false) { + spinnies.fail(`whatzapp-${session}`, { + text: 'Error accessing the page: "https://web.whatsapp.com"', + }); + return reject( + 'Error when trying to access the page: "https://web.whatsapp.com"' + ); + } - client.onStreamChange(async (stateStream) => { - if (stateStream === SocketStream.CONNECTED) { - statusFind && statusFind('chatsAvailable', session); + if (browserInstance) { + browserInstance(browser, page); } - if (stateStream === SocketStream.DISCONNECTED) { - let onQR: boolean = await page.evaluate(() => { - if ( - document.querySelector('canvas') && - document.querySelectorAll('#startup').length == 0 - ) { - return true; - } else { - return false; - } - }); - if (onQR === true && checkFileJson(mergedOptions, session)) { - if (statusFind) { - statusFind('desconnectedMobile', session); + + spinnies.succeed(`whatzapp-${session}`, { + text: 'Page successfully accessed', + }); + + const client = new Whatsapp(browser, page, session, mergedOptions); + + client.onStreamChange(async (stateStream) => { + if (stateStream === SocketStream.CONNECTED) { + statusFind && statusFind('chatsAvailable', session); + } + if (stateStream === SocketStream.DISCONNECTED) { + let onQR: boolean = await page.evaluate(() => { + if ( + document.querySelector('canvas') && + document.querySelectorAll('#startup').length == 0 + ) { + return true; + } else { + return false; + } + }); + if (onQR === true && checkFileJson(mergedOptions, session)) { + if (statusFind) { + statusFind('desconnectedMobile', session); + } + deleteFiles(mergedOptions, session, spinnies); } - deleteFiles(mergedOptions, session, spinnies); } - } - }); + }); - client.onStateChange((state) => { - if (state === SocketState.PAIRING) { - const device = page.evaluate(() => { - if (document.querySelectorAll('#startup').length) { - return true; - } else { - return false; - } - }); - if (device) { - if (statusFind) { - statusFind('deviceNotConnected', session); + client.onStateChange((state) => { + if (state === SocketState.PAIRING) { + const device = page.evaluate(() => { + if (document.querySelectorAll('#startup').length) { + return true; + } else { + return false; + } + }); + if (device) { + if (statusFind) { + statusFind('deviceNotConnected', session); + } } } - } - if (mergedOptions.createPathFileToken) { - if (state === SocketState.CONNECTED) { - setTimeout(() => { - saveToken(page, session, mergedOptions).catch((e) => { - spinnies.update(`browser-${session}`, { - text: e, + + if (mergedOptions.createPathFileToken) { + if (state === SocketState.CONNECTED) { + setTimeout(() => { + saveToken(page, session, mergedOptions).catch((e) => { + spinnies.update(`browser-${session}`, { + text: e, + }); }); - }); - }, 1000); + }, 1000); + } + } + }); + + if (mergedOptions.waitForLogin) { + const isLogged = await client.waitForLogin(catchQR, statusFind); + if (!isLogged) { + return reject('Not Logged'); } - } - }); - if (mergedOptions.waitForLogin) { - const isLogged = await client.waitForLogin(catchQR, statusFind); - if (!isLogged) { - throw 'Not Logged'; + let waitLoginPromise = null; + client.onStateChange(async (state) => { + if ( + state === SocketState.UNPAIRED || + state === SocketState.UNPAIRED_IDLE + ) { + if (!waitLoginPromise) { + waitLoginPromise = client + .waitForLogin(catchQR, statusFind) + .then(() => { + if (reconnectQrcode) { + reconnectQrcode(client); + } + }) + .catch(() => {}) + .finally(() => { + waitLoginPromise = null; + }); + } + await waitLoginPromise; + } + }); } - let waitLoginPromise = null; - client.onStateChange(async (state) => { - if ( - state === SocketState.UNPAIRED || - state === SocketState.UNPAIRED_IDLE - ) { - if (!waitLoginPromise) { - waitLoginPromise = client - .waitForLogin(catchQR, statusFind) - .catch(() => {}) - .finally(() => { - waitLoginPromise = null; - }); + if (mergedOptions.debug) { + const debugURL = `http://localhost:${readFileSync( + `./${session}/DevToolsActivePort` + ).slice(0, -54)}`; + console.log(`\nDebug: \x1b[34m${debugURL}\x1b[0m`); + } + await page + .waitForSelector('#app .two', { visible: true }) + .catch(() => {}); + await page.waitForFunction( + () => { + if ( + window.Store && + window.Store.WidFactory && + window.Store.WidFactory.createWid + ) { + return true; } - await waitLoginPromise; + }, + { + timeout: 0, + polling: 100, } - }); - } - - if (mergedOptions.debug) { - const debugURL = `http://localhost:${readFileSync( - `./${session}/DevToolsActivePort` - ).slice(0, -54)}`; - console.log(`\nDebug: \x1b[34m${debugURL}\x1b[0m`); + ); + return resolve(client); } - await page.waitForSelector('#app .two', { visible: true }).catch(() => {}); - return client; - } + }); } diff --git a/src/lib/wapi/functions/get-unread-messages.js b/src/lib/wapi/functions/get-unread-messages.js index 1e764a832..2bbd9a33a 100644 --- a/src/lib/wapi/functions/get-unread-messages.js +++ b/src/lib/wapi/functions/get-unread-messages.js @@ -1,14 +1,23 @@ export async function getUnreadMessages() { - let chats = await Store.Chat.filter((e) => e.unreadCount > 0); let arr = []; - for (let i in chats) { - let t = chats[i].msgs._models.slice(-chats[i].unreadCount); - for (let r in t) { - let message = WAPI.processMessageObj(t[r], true, true); - if (message) { - arr.push(message); + window.WAPI.waitForStore(['Chat', 'Msg'], async () => { + let chats = await Store.Chat.filter((e) => e.unreadCount > 0); + + for (let i in chats) { + let f = Number(i); + if (!isNaN(f)) { + let t = chats[i].msgs._models.slice(-chats[i].unreadCount); + for (let r in t) { + let h = Number(r); + if (!isNaN(h)) { + let message = WAPI.processMessageObj(t[r], true, true); + if (message) { + arr.push(message); + } + } + } } } - } + }); return arr; } diff --git a/src/lib/wapi/functions/index.js b/src/lib/wapi/functions/index.js index 8250defbd..997f222d7 100644 --- a/src/lib/wapi/functions/index.js +++ b/src/lib/wapi/functions/index.js @@ -99,3 +99,4 @@ export { logout } from './logout'; export { setPresenceOnline } from './set-presence-online'; export { setPresenceOffline } from './set-presence-offline'; export { archiveChat } from './archive-chat'; +export { setNewMessageId } from './set-new-message'; diff --git a/src/lib/wapi/functions/process-message-object.js b/src/lib/wapi/functions/process-message-object.js index 07c7c538d..e5aa01473 100644 --- a/src/lib/wapi/functions/process-message-object.js +++ b/src/lib/wapi/functions/process-message-object.js @@ -1,9 +1,10 @@ export function processMessageObj(messageObj, includeMe, includeNotifications) { if (messageObj.isNotification) { - if (includeNotifications) return WAPI._serializeMessageObj(messageObj); - else return; - // System message - // (i.e. "Messages you send to this chat and calls are now secured with end-to-end encryption...") + if (includeNotifications) { + return WAPI._serializeMessageObj(messageObj); + } else { + return; + } } else if (messageObj.id.fromMe === false || includeMe) { return WAPI._serializeMessageObj(messageObj); } diff --git a/src/lib/wapi/functions/send-message.js b/src/lib/wapi/functions/send-message.js index e5559fffd..25395e353 100644 --- a/src/lib/wapi/functions/send-message.js +++ b/src/lib/wapi/functions/send-message.js @@ -1,4 +1,4 @@ -export async function sendMessage(to, content, status = false, newMsgId) { +export async function sendMessage(to, content, status = false, passId) { if (status && content.length > 700) { return WAPI.scope(undefined, true, null, 'Use a maximum of 700 characters'); } @@ -21,9 +21,9 @@ export async function sendMessage(to, content, status = false, newMsgId) { if (chat && chat.status != 404) { const t = status != false ? 'sendStatusText' : 'sendText'; const m = { type: t, text: content }; - if (!newMsgId) { - newMsgId = await window.WAPI.getNewMessageId(chat.id); - } + const newMsgId = !passId + ? await window.WAPI.getNewMessageId(chat.id) + : await window.WAPI.setNewMessageId(passId); const fromwWid = await window.Store.Conn.wid; let inChat = await WAPI.getchatId(to).catch(() => {}); if (inChat) { diff --git a/src/lib/wapi/functions/set-new-message.js b/src/lib/wapi/functions/set-new-message.js new file mode 100644 index 000000000..bb504e64f --- /dev/null +++ b/src/lib/wapi/functions/set-new-message.js @@ -0,0 +1,19 @@ +export async function setNewMessageId(info) { + if (typeof info === 'object' && info.number && info._serialized && info.id) { + const chat = await WAPI.sendExist(info.number); + delete info.number; + if (chat.id) { + const newMsgId = new Object(); + newMsgId.fromMe = true; + newMsgId.id = info.id; + newMsgId.remote = await new Store.WidFactory.createWid(chat.id); + newMsgId._serialized = `${newMsgId.fromMe}_${newMsgId.remote}_${newMsgId.id}`; + const Msgkey = await new Store.MsgKey(newMsgId); + return Msgkey; + } else { + return false; + } + } else { + return false; + } +} diff --git a/src/lib/wapi/serializers/serialize-message.js b/src/lib/wapi/serializers/serialize-message.js index 5cde1d091..2c803b5ee 100644 --- a/src/lib/wapi/serializers/serialize-message.js +++ b/src/lib/wapi/serializers/serialize-message.js @@ -3,6 +3,7 @@ export const _serializeMessageObj = (obj) => { return null; } const _chat = obj['chat'] ? WAPI._serializeChatObj(obj['chat']) : {}; + if (obj.quotedMsg) obj.quotedMsgObj(); return Object.assign(window.WAPI._serializeRawObj(obj), { id: obj.id._serialized, diff --git a/src/lib/wapi/store/store-objects.js b/src/lib/wapi/store/store-objects.js index 0735138e0..5c8440cab 100644 --- a/src/lib/wapi/store/store-objects.js +++ b/src/lib/wapi/store/store-objects.js @@ -55,9 +55,9 @@ export const storeObjects = [ { id: 'WapQuery', conditions: (module) => - module.queryExist - ? module - : module.default && module.default.queryExist + module.default && + module.default.contactFindQuery && + module.default.queryExist ? module.default : null, }, diff --git a/src/lib/wapi/wapi.js b/src/lib/wapi/wapi.js index 9959be985..5b846bf5e 100644 --- a/src/lib/wapi/wapi.js +++ b/src/lib/wapi/wapi.js @@ -104,6 +104,7 @@ import { setPresenceOnline, setPresenceOffline, archiveChat, + setNewMessageId, } from './functions'; import { base64ToFile, @@ -178,6 +179,7 @@ if (typeof window.WAPI === 'undefined') { window.WAPI.checkIdMessage = checkIdMessage; window.WAPI.returnReply = returnReply; window.WAPI.getStore = getStore; + window.WAPI.setNewMessageId = setNewMessageId; //Profile window.WAPI.setProfilePic = setProfilePic; diff --git a/src/types/WAPI.d.ts b/src/types/WAPI.d.ts index c3dde5330..4de5d48ba 100644 --- a/src/types/WAPI.d.ts +++ b/src/types/WAPI.d.ts @@ -160,7 +160,7 @@ interface WAPI { to: string, content: string, status?: boolean, - idMessage?: string + passId?: Object ) => Promise; sendMessageMentioned: (...args: any) => any; sendMessageOptions: (