whatsapp-web.js 1.20.0-alpha.0 → 1.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ <!DOCTYPE html><html class="no-js" dir="ltr" loc="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><title>WhatsApp Web</title><meta name="viewport" content="width=device-width"><meta name="google" content="notranslate"><meta name="format-detection" content="telephone=no"><meta name="description" content="Quickly send and receive WhatsApp messages right from your computer."><meta name="og:description" content="Quickly send and receive WhatsApp messages right from your computer."><meta name="og:url" content="https://web.whatsapp.com/"><meta name="og:title" content="WhatsApp Web"><meta name="og:image" content="https://static.facebook.com/images/whatsapp/www/whatsapp-promo.png"><link id="favicon" rel="shortcut icon" href="/img/favicon/1x/favicon.png" type="image/png"><link rel="apple-touch-icon" sizes="194x194" href="/apple-touch-icon.png" type="image/png"><meta name="theme-color" content="#111b21" media="(prefers-color-scheme: dark)"><meta name="theme-color" content="#f0f2f5"><link id="whatsapp-pwa-manifest-link" rel="manifest" href="/manifest.json" crossorigin="use-credentials"><style>#initial_startup{--startup-background:#f0f2f5;--startup-background-rgb:240,242,245;--startup-icon:#bbc5cb;--secondary-lighter:#8696a0;--primary-title:#41525d;--progress-primary:#00c298;--progress-background:#e9edef}.dark #initial_startup{--startup-background:#111b21;--startup-background-rgb:17,27,33;--startup-icon:#676f73;--primary-title:rgba(233, 237, 239, 0.88);--secondary-lighter:#667781;--progress-primary:#0b846d;--progress-background:#233138}#app,body,html{width:100%;height:100%;padding:0;margin:0;overflow:hidden}#app{position:absolute;top:0;left:0}#initial_startup{position:fixed;top:0;left:0;display:flex;flex-direction:column;align-items:center;justify-content:center;width:100%;height:100%;user-select:none;background-color:var(--startup-background)}#initial_startup .graphic{margin-top:-40px;color:var(--startup-icon)}#initial_startup .graphic .resume-logo{transform:translateX(calc(50% - 52px / 2))}#initial_startup .graphic::after{position:relative;top:-100%;left:calc(50% - 72px * 2 - 72px / 2);display:block;width:calc(72px * 3);height:100%;content:'';background:linear-gradient(to right,rgba(var(--startup-background-rgb),.5) 0,rgba(var(--startup-background-rgb),.5) 33.33%,rgba(var(--startup-background-rgb),0) 44.1%,rgba(var(--startup-background-rgb),0) 55.8%,rgba(var(--startup-background-rgb),.5) 66.66%,rgba(var(--startup-background-rgb),.5) 100%);opacity:1;animation:shimmer 1.5s linear .6s infinite}html[dir=rtl] #initial_startup .graphic::after{animation-direction:reverse}@keyframes shimmer{from{left:calc(50% - 72px * 2 - 72px / 2)}to{left:calc(50% - 72px / 2)}}#initial_startup .progress{position:relative;width:420px;height:3px;margin-top:40px}#initial_startup .progress progress{vertical-align:top}#initial_startup .main{margin-top:40px;font-size:17px;color:var(--primary-title)}#initial_startup .secondary{margin-top:12px;font-size:14px;color:var(--secondary-lighter)}#initial_startup .secondary span{display:inline-block;margin-bottom:2px;vertical-align:middle}progress{-webkit-appearance:none;appearance:none;width:100%;height:3px;margin:0;color:var(--progress-primary);background-color:var(--progress-background);border:none}progress[value]::-webkit-progress-bar{background-color:var(--progress-background)}progress[value]::-moz-progress-bar,progress[value]::-webkit-progress-value{background-color:var(--progress-primary);transition:width .45s ease}</style><link href="/stylex-4a088306e44f3215558b4ef8b78657c5.css" rel="stylesheet"><link href="/app-a2c2cd1b2aea3ab61005.css" rel="stylesheet"></head><body class="web"><script data-binary-transparency-hash-key="inline-js-4b79b6dc91a7ee33373b115991c3eb287ed710cfb6708421b4cab682eddbfcbd">try{var systemThemeDark,theme=window.localStorage.getItem(""),systemThemeMode=window.localStorage.getItem("system-theme-mode");if(("true"===systemThemeMode||!theme)&&window.matchMedia){var systemTheme=window.matchMedia("(prefers-color-scheme: dark)");systemThemeDark=systemTheme&&systemTheme.matches}var darkTheme='"dark"'===theme||Boolean(systemThemeDark);darkTheme&&document.body.classList.add("dark")}catch(e){}</script><div id="app"></div><div id="hard_expire_time" data-time="1699300794.986"></div><div id="initial_startup"><div class="graphic"><span><svg width="250" height="52" xmlns="http://www.w3.org/2000/svg"><path class="resume-logo" d="M37.7 31.2c-.6-.4-3.8-2-4.4-2.1-.6-.2-1-.4-1.4.3l-2 2.5c-.4.4-.8.5-1.5.2-.6-.3-2.7-1-5.1-3.2-2-1.7-3.2-3.8-3.6-4.5-.4-.6 0-1 .3-1.3l1-1.1.6-1.1c.2-.4 0-.8 0-1.1l-2-4.8c-.6-1.3-1.1-1-1.5-1.1h-1.2c-.5 0-1.2.1-1.8.8-.5.6-2.2 2.2-2.2 5.3 0 3.2 2.3 6.3 2.6 6.7.3.4 4.6 7 11 9.7l3.7 1.4c1.5.5 3 .4 4 .2 1.3-.1 3.9-1.5 4.4-3 .5-1.5.5-2.8.4-3-.2-.4-.6-.5-1.3-.8M26 47.2c-3.9 0-7.6-1-11-3l-.7-.4-8.1 2L8.4 38l-.6-.8A21.4 21.4 0 0126 4.4a21.3 21.3 0 0121.4 21.4c0 11.8-9.6 21.4-21.4 21.4M44.2 7.6a25.8 25.8 0 00-40.6 31L0 52l13.7-3.6A25.8 25.8 0 0044.3 7.5" fill="currentColor"></path></svg></span></div><div class="progress"><progress value="0" max="100" dir="ltr"></progress></div><div class="main">WhatsApp</div><div class="secondary"><span><svg width="10" height="12" xmlns="http://www.w3.org/2000/svg"><path d="M5 1.6c1.4 0 2.5 1 2.6 2.4v1.5h.2c.5 0 1 .4 1 1V10c0 .6-.5 1-1 1H2.3a1 1 0 01-1.1-1V6.5c0-.6.5-1 1-1h.2V4.2c0-1.4 1-2.5 2.4-2.6H5zm0 1.2c-.7 0-1.3.6-1.3 1.3v1.4h2.6V4.2c0-.7-.4-1.2-1-1.3H5z" fill="currentColor"></path></svg> </span>&nbsp;End-to-end encrypted</div></div><link rel="preload" crossorigin="anonymous" as="fetch" href="/binary-transparency-manifest-2.2320.10.json" id="binary-transparency-manifest-preload"><script src="/libsignal-protocol-ee5b8ba.min.js"></script><script defer="defer" src="/runtime.48c50558556d819fec57.js"></script><script data-binary-transparency-hash-key="inline-js-026e63eb49dc1ea304d8d5b3073c120b618f17c45d3e4186fc3a06527b75bc4a">/*! Copyright (c) 2023 WhatsApp Inc. All Rights Reserved. */
2
+ (self.webpackChunkwhatsapp_web_client=self.webpackChunkwhatsapp_web_client||[]).push([[5617],{307914:e=>{e.exports=function(e){return e&&e.__esModule?e:{default:e}}},595318:e=>{e.exports=function(e){return e&&e.__esModule?e:{default:e}},e.exports.default=e.exports,e.exports.__esModule=!0},415227:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){const t=new Error(e);if(void 0===t.stack)try{throw t}catch(e){}return t}},670983:(e,t,r)=>{"use strict";var n=r(307914);Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"?";if(null==e)throw(0,o.default)("Unexpected null or undefined: "+t);return e};var o=n(r(415227))},801506:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.TICKET_URL=t.CLB_URL=t.CLB_TOKEN=t.CLB_CHECK_URL=void 0,t.CLB_CHECK_URL="https://crashlogs.whatsapp.net/wa_fls_upload_check",t.CLB_TOKEN="1063127757113399|745146ffa34413f9dbb5469f5370b7af",t.CLB_URL="https://crashlogs.whatsapp.net/wa_clb_data",t.TICKET_URL="https://web.whatsapp.com/web-contact-us"},207024:(e,t,r)=>{"use strict";var n=r(595318);Object.defineProperty(t,"__esModule",{value:!0}),t.getDistribution=function(){let e="unknown";return e="prod","web_prod"},t.getLogUserAgent=function(e){let t,{appVersion:r,browser:n,device:o}=e;return t="Web/"+n,`WhatsApp/${r} ${t} Device/${o}`},n(r(556869))},794858:(e,t,r)=>{"use strict";var n=r(595318),o=n(r(670983)),a=r(801506),s=r(207024),c=n(r(174285)),u=r(425017);function i(e){!function(e,t){const r=window.navigator.userAgent;if(r===p&&e.includes("getElementsByTagName"))return;const n=new FormData,i=new Blob([e],{type:"text/plain"});n.append("from_jid",function(){if(f)return f;try{f=JSON.parse((0,o.default)(c.default,"localStorage").getItem(d)),f&&(f=f.replace("-",""))}catch(e){}if(!f){f="unknown"+Math.floor(1e10*Math.random());const e=f;try{(0,o.default)(c.default,"localStorage").setItem(d,JSON.stringify(e))}catch(e){}}return(0,o.default)(f,"id")}()),n.append("agent",(0,s.getLogUserAgent)((0,u.parseUASimple)(r,"2.2320.10"))),n.append("file",i,"logs.txt"),n.append("tags","load");const l=new XMLHttpRequest,_=a.CLB_URL+"?access_token="+encodeURIComponent(a.CLB_TOKEN);l.open("POST",_,!0),l.send(n)}(e)}function l(e){let{error:t,reason:r,stack:n}=e;const o=(new Date).toISOString();return`${o}: error: ${t}\n${o}: reason for logs: ${r}\n${o}: userAgent: ${window.navigator.userAgent}\n${n}`}null==window.onerror&&(window.onerror=function(e,t,r){const n=t.split("?")[0];return"Uncaught SyntaxError: Unexpected token '<'"===e?(function(e){i(l({error:"failed to load a js or css bundle",reason:`failed to load [${e.split("/")[3].replace(/^\//,"")}]`,stack:""}))}(n),!0):(i(l({error:e,reason:`Error at [${n}:${r}]`,stack:""})),!1)});const d="WAUnknownID",p="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36";let f},425017:(e,t)=>{"use strict";function r(e){return e.includes("windows")?"Windows":e.includes("mac")?"Mac OS":e.includes("linux")?"Linux":"Unparsed"}Object.defineProperty(t,"__esModule",{value:!0}),t.parseUASimple=function(e,t){const n=e.toLowerCase();return{browser:s(n),device:r(n),appVersion:t}};const n=/(chrome|firefox)\/([\w\.]+)/i,o=/(edge|opr)\/([\w\.]+)/i,a={chrome:"Chrome",edge:"Edge",opr:"Opera",firefox:"Firefox"};function s(e){const t=e.match(o)||e.match(n);return null==t?"Unparsed":`${a[t[1]]} ${t[2]}`}},174285:(e,t)=>{"use strict";let r;Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;try{r=window.localStorage}catch(e){}var n=r;t.default=n},556869:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function e(t){const r=new Error(t);if(void 0===r.stack)try{throw e}catch(e){}return r}}},e=>{"use strict";e(e.s=794858)}])</script><script defer="defer" src="/vendor1~app.a91035d81a749b3d5627.js"></script><script defer="defer" src="/app.e77d49e02fab96b93f06.js"></script><script data-binary-transparency-hash-key="inline-js-9c8cd3d0f4d5af8c7f5cd1c45208edd308bfe472e353a50e4cbacbf6cb58627c">(i => {const l = document.getElementById(i); l &&fetch(l.href).then(b => b.text()).then(code => {const script = document.createElement('script');script.id = 'binary-transparency-manifest';script.type = 'application/json';script.innerHTML = code;document.body.append(script);});})('binary-transparency-manifest-preload');</script></body></html>
@@ -0,0 +1,2 @@
1
+ <!DOCTYPE html><html class="no-js" dir="ltr" loc="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><title>WhatsAppX Web</title><meta name="viewport" content="width=device-width"><meta name="google" content="notranslate"><meta name="format-detection" content="telephone=no"><meta name="description" content="Quickly send and receive WhatsApp messages right from your computer."><meta name="og:description" content="Quickly send and receive WhatsApp messages right from your computer."><meta name="og:url" content="https://web.whatsapp.com/"><meta name="og:title" content="WhatsApp Web"><meta name="og:image" content="https://static.facebook.com/images/whatsapp/www/whatsapp-promo.png"><link id="favicon" rel="shortcut icon" href="/img/favicon/1x/favicon.png" type="image/png"><link rel="apple-touch-icon" sizes="194x194" href="/apple-touch-icon.png" type="image/png"><meta name="theme-color" content="#111b21" media="(prefers-color-scheme: dark)"><meta name="theme-color" content="#f0f2f5"><link id="whatsapp-pwa-manifest-link" rel="manifest" href="/manifest.json" crossorigin="use-credentials"><style>#initial_startup{--startup-background:#f0f2f5;--startup-background-rgb:240,242,245;--startup-icon:#bbc5cb;--secondary-lighter:#8696a0;--primary-title:#41525d;--progress-primary:#00c298;--progress-background:#e9edef}.dark #initial_startup{--startup-background:#111b21;--startup-background-rgb:17,27,33;--startup-icon:#676f73;--primary-title:rgba(233, 237, 239, 0.88);--secondary-lighter:#667781;--progress-primary:#0b846d;--progress-background:#233138}#app,body,html{width:100%;height:100%;padding:0;margin:0;overflow:hidden}#app{position:absolute;top:0;left:0}#initial_startup{position:fixed;top:0;left:0;display:flex;flex-direction:column;align-items:center;justify-content:center;width:100%;height:100%;user-select:none;background-color:var(--startup-background)}#initial_startup .graphic{margin-top:-40px;color:var(--startup-icon)}#initial_startup .graphic .resume-logo{transform:translateX(calc(50% - 52px / 2))}#initial_startup .graphic::after{position:relative;top:-100%;left:calc(50% - 72px * 2 - 72px / 2);display:block;width:calc(72px * 3);height:100%;content:'';background:linear-gradient(to right,rgba(var(--startup-background-rgb),.5) 0,rgba(var(--startup-background-rgb),.5) 33.33%,rgba(var(--startup-background-rgb),0) 44.1%,rgba(var(--startup-background-rgb),0) 55.8%,rgba(var(--startup-background-rgb),.5) 66.66%,rgba(var(--startup-background-rgb),.5) 100%);opacity:1;animation:shimmer 1.5s linear .6s infinite}html[dir=rtl] #initial_startup .graphic::after{animation-direction:reverse}@keyframes shimmer{from{left:calc(50% - 72px * 2 - 72px / 2)}to{left:calc(50% - 72px / 2)}}#initial_startup .progress{position:relative;width:420px;height:3px;margin-top:40px}#initial_startup .progress progress{vertical-align:top}#initial_startup .main{margin-top:40px;font-size:17px;color:var(--primary-title)}#initial_startup .secondary{margin-top:12px;font-size:14px;color:var(--secondary-lighter)}#initial_startup .secondary span{display:inline-block;margin-bottom:2px;vertical-align:middle}progress{-webkit-appearance:none;appearance:none;width:100%;height:3px;margin:0;color:var(--progress-primary);background-color:var(--progress-background);border:none}progress[value]::-webkit-progress-bar{background-color:var(--progress-background)}progress[value]::-moz-progress-bar,progress[value]::-webkit-progress-value{background-color:var(--progress-primary);transition:width .45s ease}</style><link href="/stylex-534e186735a7216f8166323ac4f40ec0.css" rel="stylesheet"><link href="/app-397dc77d692df7ff99b6.css" rel="stylesheet"></head><body class="web"><script data-binary-transparency-hash-key="inline-js-4b79b6dc91a7ee33373b115991c3eb287ed710cfb6708421b4cab682eddbfcbd">try{var systemThemeDark,theme=window.localStorage.getItem(""),systemThemeMode=window.localStorage.getItem("system-theme-mode");if(("true"===systemThemeMode||!theme)&&window.matchMedia){var systemTheme=window.matchMedia("(prefers-color-scheme: dark)");systemThemeDark=systemTheme&&systemTheme.matches}var darkTheme='"dark"'===theme||Boolean(systemThemeDark);darkTheme&&document.body.classList.add("dark")}catch(e){}</script><div id="app"></div><div id="hard_expire_time" data-time="1700350311.621"></div><div id="initial_startup"><div class="graphic"><span><svg width="250" height="52" xmlns="http://www.w3.org/2000/svg"><path class="resume-logo" d="M37.7 31.2c-.6-.4-3.8-2-4.4-2.1-.6-.2-1-.4-1.4.3l-2 2.5c-.4.4-.8.5-1.5.2-.6-.3-2.7-1-5.1-3.2-2-1.7-3.2-3.8-3.6-4.5-.4-.6 0-1 .3-1.3l1-1.1.6-1.1c.2-.4 0-.8 0-1.1l-2-4.8c-.6-1.3-1.1-1-1.5-1.1h-1.2c-.5 0-1.2.1-1.8.8-.5.6-2.2 2.2-2.2 5.3 0 3.2 2.3 6.3 2.6 6.7.3.4 4.6 7 11 9.7l3.7 1.4c1.5.5 3 .4 4 .2 1.3-.1 3.9-1.5 4.4-3 .5-1.5.5-2.8.4-3-.2-.4-.6-.5-1.3-.8M26 47.2c-3.9 0-7.6-1-11-3l-.7-.4-8.1 2L8.4 38l-.6-.8A21.4 21.4 0 0126 4.4a21.3 21.3 0 0121.4 21.4c0 11.8-9.6 21.4-21.4 21.4M44.2 7.6a25.8 25.8 0 00-40.6 31L0 52l13.7-3.6A25.8 25.8 0 0044.3 7.5" fill="currentColor"></path></svg></span></div><div class="progress"><progress value="0" max="100" dir="ltr"></progress></div><div class="main">WhatsApp</div><div class="secondary"><span><svg width="10" height="12" xmlns="http://www.w3.org/2000/svg"><path d="M5 1.6c1.4 0 2.5 1 2.6 2.4v1.5h.2c.5 0 1 .4 1 1V10c0 .6-.5 1-1 1H2.3a1 1 0 01-1.1-1V6.5c0-.6.5-1 1-1h.2V4.2c0-1.4 1-2.5 2.4-2.6H5zm0 1.2c-.7 0-1.3.6-1.3 1.3v1.4h2.6V4.2c0-.7-.4-1.2-1-1.3H5z" fill="currentColor"></path></svg> </span>&nbsp;End-to-end encrypted</div></div><link rel="preload" crossorigin="anonymous" as="fetch" href="/binary-transparency-manifest-2.2322.15.json" id="binary-transparency-manifest-preload"><script src="/libsignal-protocol-ee5b8ba.min.js"></script><script defer="defer" src="/runtime.d9a06bd6a7a484d6e04e.js"></script><script data-binary-transparency-hash-key="inline-js-fa16de51ee7511dd7a3e11537a929d8433d81c4653032c11129c2f55dd563575">/*! Copyright (c) 2023 WhatsApp Inc. All Rights Reserved. */
2
+ (self.webpackChunkwhatsapp_web_client=self.webpackChunkwhatsapp_web_client||[]).push([[5617],{307914:e=>{e.exports=function(e){return e&&e.__esModule?e:{default:e}}},595318:e=>{e.exports=function(e){return e&&e.__esModule?e:{default:e}},e.exports.default=e.exports,e.exports.__esModule=!0},415227:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){const t=new Error(e);if(void 0===t.stack)try{throw t}catch(e){}return t}},670983:(e,t,r)=>{"use strict";var n=r(307914);Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"?";if(null==e)throw(0,o.default)("Unexpected null or undefined: "+t);return e};var o=n(r(415227))},801506:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.TICKET_URL=t.CLB_URL=t.CLB_TOKEN=t.CLB_CHECK_URL=void 0,t.CLB_CHECK_URL="https://crashlogs.whatsapp.net/wa_fls_upload_check",t.CLB_TOKEN="1063127757113399|745146ffa34413f9dbb5469f5370b7af",t.CLB_URL="https://crashlogs.whatsapp.net/wa_clb_data",t.TICKET_URL="https://web.whatsapp.com/web-contact-us"},207024:(e,t,r)=>{"use strict";var n=r(595318);Object.defineProperty(t,"__esModule",{value:!0}),t.getDistribution=function(){let e="unknown";return e="prod","web_prod"},t.getLogUserAgent=function(e){let t,{appVersion:r,browser:n,device:o}=e;return t="Web/"+n,`WhatsApp/${r} ${t} Device/${o}`},n(r(97359)),n(r(556869))},794858:(e,t,r)=>{"use strict";var n=r(595318),o=n(r(670983)),a=r(801506),s=r(207024),u=n(r(174285)),c=r(425017);function i(e){!function(e,t){const r=window.navigator.userAgent;if(r===f&&e.includes("getElementsByTagName"))return;const n=new FormData,i=new Blob([e],{type:"text/plain"});n.append("from_jid",function(){if(p)return p;try{p=JSON.parse((0,o.default)(u.default,"localStorage").getItem(d)),p&&(p=p.replace("-",""))}catch(e){}if(!p){p="unknown"+Math.floor(1e10*Math.random());const e=p;try{(0,o.default)(u.default,"localStorage").setItem(d,JSON.stringify(e))}catch(e){}}return(0,o.default)(p,"id")}()),n.append("agent",(0,s.getLogUserAgent)((0,c.parseUASimple)(r,"2.2322.15"))),n.append("file",i,"logs.txt"),n.append("tags","load");const l=new XMLHttpRequest,_=a.CLB_URL+"?access_token="+encodeURIComponent(a.CLB_TOKEN);l.open("POST",_,!0),l.send(n)}(e)}function l(e){let{error:t,reason:r,stack:n}=e;const o=(new Date).toISOString();return`${o}: error: ${t}\n${o}: reason for logs: ${r}\n${o}: userAgent: ${window.navigator.userAgent}\n${n}`}null==window.onerror&&(window.onerror=function(e,t,r){const n=t.split("?")[0];return"Uncaught SyntaxError: Unexpected token '<'"===e?(function(e){i(l({error:"failed to load a js or css bundle",reason:`failed to load [${e.split("/")[3].replace(/^\//,"")}]`,stack:""}))}(n),!0):(i(l({error:e,reason:`Error at [${n}:${r}]`,stack:""})),!1)});const d="WAUnknownID",f="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36";let p},425017:(e,t)=>{"use strict";function r(e){return e.includes("windows")?"Windows":e.includes("mac")?"Mac OS":e.includes("linux")?"Linux":"Unparsed"}Object.defineProperty(t,"__esModule",{value:!0}),t.parseUASimple=function(e,t){const n=e.toLowerCase();return{browser:s(n),device:r(n),appVersion:t}};const n=/(chrome|firefox)\/([\w\.]+)/i,o=/(edge|opr)\/([\w\.]+)/i,a={chrome:"Chrome",edge:"Edge",opr:"Opera",firefox:"Firefox"};function s(e){const t=e.match(o)||e.match(n);return null==t?"Unparsed":`${a[t[1]]} ${t[2]}`}},174285:(e,t)=>{"use strict";let r;Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;try{r=window.localStorage}catch(e){}var n=r;t.default=n},97359:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){return e.default}},556869:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function e(t){const r=new Error(t);if(void 0===r.stack)try{throw e}catch(e){}return r}}},e=>{"use strict";e(e.s=794858)}])</script><script defer="defer" src="/vendor1~app.a91035d81a749b3d5627.js"></script><script defer="defer" src="/app.df22dc4b1b984dc87f36.js"></script><script data-binary-transparency-hash-key="inline-js-9c8cd3d0f4d5af8c7f5cd1c45208edd308bfe472e353a50e4cbacbf6cb58627c">(i => {const l = document.getElementById(i); l &&fetch(l.href).then(b => b.text()).then(code => {const script = document.createElement('script');script.id = 'binary-transparency-manifest';script.type = 'application/json';script.innerHTML = code;document.body.append(script);});})('binary-transparency-manifest-preload');</script></body></html>
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![npm](https://img.shields.io/npm/v/whatsapp-web.js.svg)](https://www.npmjs.com/package/whatsapp-web.js) [![Depfu](https://badges.depfu.com/badges/4a65a0de96ece65fdf39e294e0c8dcba/overview.svg)](https://depfu.com/github/pedroslopez/whatsapp-web.js?project_id=9765) ![WhatsApp_Web 2.2301.6](https://img.shields.io/badge/WhatsApp_Web-2.2301.6-brightgreen.svg) [![Discord Chat](https://img.shields.io/discord/698610475432411196.svg?logo=discord)](https://discord.gg/H7DqQs4)
1
+ [![npm](https://img.shields.io/npm/v/whatsapp-web.js.svg)](https://www.npmjs.com/package/whatsapp-web.js) [![Depfu](https://badges.depfu.com/badges/4a65a0de96ece65fdf39e294e0c8dcba/overview.svg)](https://depfu.com/github/pedroslopez/whatsapp-web.js?project_id=9765) ![WhatsApp_Web 2.2322.15](https://img.shields.io/badge/WhatsApp_Web-2.2322.15-brightgreen.svg) [![Discord Chat](https://img.shields.io/discord/698610475432411196.svg?logo=discord)](https://discord.gg/H7DqQs4)
2
2
 
3
3
  # whatsapp-web.js
4
4
  A WhatsApp API client that connects through the WhatsApp Web browser app
package/example.js CHANGED
@@ -1,8 +1,12 @@
1
- const { Client, Location, List, Buttons, LocalAuth} = require('./index');
1
+ const { Client, Location, List, Buttons, LocalAuth } = require('./index');
2
2
 
3
3
  const client = new Client({
4
4
  authStrategy: new LocalAuth(),
5
- puppeteer: { headless: false }
5
+ // proxyAuthentication: { username: 'username', password: 'password' },
6
+ puppeteer: {
7
+ // args: ['--proxy-server=proxy-server-that-requires-authentication.example.com'],
8
+ headless: false
9
+ }
6
10
  });
7
11
 
8
12
  client.initialize();
@@ -189,11 +193,11 @@ client.on('message', async msg => {
189
193
  client.interface.openChatWindowAt(quotedMsg.id._serialized);
190
194
  }
191
195
  } else if (msg.body === '!buttons') {
192
- let button = new Buttons('Button body',[{body:'bt1'},{body:'bt2'},{body:'bt3'}],'title','footer');
196
+ let button = new Buttons('Button body', [{ body: 'bt1' }, { body: 'bt2' }, { body: 'bt3' }], 'title', 'footer');
193
197
  client.sendMessage(msg.from, button);
194
198
  } else if (msg.body === '!list') {
195
- let sections = [{title:'sectionTitle',rows:[{title:'ListItem1', description: 'desc'},{title:'ListItem2'}]}];
196
- let list = new List('List body','btnText',sections,'Title','footer');
199
+ let sections = [{ title: 'sectionTitle', rows: [{ title: 'ListItem1', description: 'desc' }, { title: 'ListItem2' }] }];
200
+ let list = new List('List body', 'btnText', sections, 'Title', 'footer');
197
201
  client.sendMessage(msg.from, list);
198
202
  } else if (msg.body === '!reaction') {
199
203
  msg.react('👍');
@@ -231,7 +235,7 @@ client.on('message_ack', (msg, ack) => {
231
235
  ACK_PLAYED: 4
232
236
  */
233
237
 
234
- if(ack == 3) {
238
+ if (ack == 3) {
235
239
  // The message was read
236
240
  }
237
241
  });
@@ -254,7 +258,7 @@ client.on('group_update', (notification) => {
254
258
  });
255
259
 
256
260
  client.on('change_state', state => {
257
- console.log('CHANGE STATE', state );
261
+ console.log('CHANGE STATE', state);
258
262
  });
259
263
 
260
264
  // Change to false if you don't want to reject incoming calls
@@ -270,3 +274,51 @@ client.on('disconnected', (reason) => {
270
274
  console.log('Client was logged out', reason);
271
275
  });
272
276
 
277
+ client.on('contact_changed', async (message, oldId, newId, isContact) => {
278
+ /** The time the event occurred. */
279
+ const eventTime = (new Date(message.timestamp * 1000)).toLocaleString();
280
+
281
+ console.log(
282
+ `The contact ${oldId.slice(0, -5)}` +
283
+ `${!isContact ? ' that participates in group ' +
284
+ `${(await client.getChatById(message.to ?? message.from)).name} ` : ' '}` +
285
+ `changed their phone number\nat ${eventTime}.\n` +
286
+ `Their new phone number is ${newId.slice(0, -5)}.\n`);
287
+
288
+ /**
289
+ * Information about the {@name message}:
290
+ *
291
+ * 1. If a notification was emitted due to a group participant changing their phone number:
292
+ * {@name message.author} is a participant's id before the change.
293
+ * {@name message.recipients[0]} is a participant's id after the change (a new one).
294
+ *
295
+ * 1.1 If the contact who changed their number WAS in the current user's contact list at the time of the change:
296
+ * {@name message.to} is a group chat id the event was emitted in.
297
+ * {@name message.from} is a current user's id that got an notification message in the group.
298
+ * Also the {@name message.fromMe} is TRUE.
299
+ *
300
+ * 1.2 Otherwise:
301
+ * {@name message.from} is a group chat id the event was emitted in.
302
+ * {@name message.to} is @type {undefined}.
303
+ * Also {@name message.fromMe} is FALSE.
304
+ *
305
+ * 2. If a notification was emitted due to a contact changing their phone number:
306
+ * {@name message.templateParams} is an array of two user's ids:
307
+ * the old (before the change) and a new one, stored in alphabetical order.
308
+ * {@name message.from} is a current user's id that has a chat with a user,
309
+ * whos phone number was changed.
310
+ * {@name message.to} is a user's id (after the change), the current user has a chat with.
311
+ */
312
+ });
313
+
314
+ client.on('group_admin_changed', (notification) => {
315
+ if (notification.type === 'promote') {
316
+ /**
317
+ * Emitted when a current user is promoted to an admin.
318
+ * {@link notification.author} is a user who performs the action of promoting/demoting the current user.
319
+ */
320
+ console.log(`You were promoted by ${notification.author}`);
321
+ } else if (notification.type === 'demote')
322
+ /** Emitted when a current user is demoted to a regular user. */
323
+ console.log(`You were demoted by ${notification.author}`);
324
+ });
package/index.d.ts CHANGED
@@ -148,6 +148,12 @@ declare namespace WAWebJS {
148
148
  /** Unmutes the Chat */
149
149
  unmuteChat(chatId: string): Promise<void>
150
150
 
151
+ /** Sets the current user's profile picture */
152
+ setProfilePicture(media: MessageMedia): Promise<boolean>
153
+
154
+ /** Deletes the current user's profile picture */
155
+ deleteProfilePicture(): Promise<boolean>
156
+
151
157
  /** Generic event */
152
158
  on(event: string, listener: (...args: any) => void): this
153
159
 
@@ -192,12 +198,30 @@ declare namespace WAWebJS {
192
198
  notification: GroupNotification
193
199
  ) => void): this
194
200
 
201
+ /** Emitted when a current user is promoted to an admin or demoted to a regular user */
202
+ on(event: 'group_admin_changed', listener: (
203
+ /** GroupNotification with more information about the action */
204
+ notification: GroupNotification
205
+ ) => void): this
206
+
195
207
  /** Emitted when group settings are updated, such as subject, description or picture */
196
208
  on(event: 'group_update', listener: (
197
209
  /** GroupNotification with more information about the action */
198
210
  notification: GroupNotification
199
211
  ) => void): this
200
212
 
213
+ /** Emitted when a contact or a group participant changed their phone number. */
214
+ on(event: 'contact_changed', listener: (
215
+ /** Message with more information about the event. */
216
+ message: Message,
217
+ /** Old user's id. */
218
+ oldId : String,
219
+ /** New user's id. */
220
+ newId : String,
221
+ /** Indicates if a contact or a group participant changed their phone number. */
222
+ isContact : Boolean
223
+ ) => void): this
224
+
201
225
  /** Emitted when media has been uploaded for a message sent by the client */
202
226
  on(event: 'media_uploaded', listener: (
203
227
  /** The message with media that was uploaded */
@@ -217,6 +241,12 @@ declare namespace WAWebJS {
217
241
  /** The new ACK value */
218
242
  ack: MessageAck
219
243
  ) => void): this
244
+
245
+ /** Emitted when a chat unread count changes */
246
+ on(event: 'unread_count', listener: (
247
+ /** The chat that was affected */
248
+ chat: Chat
249
+ ) => void): this
220
250
 
221
251
  /** Emitted when a new message is created, which may include the current user's own messages */
222
252
  on(event: 'message_create', listener: (
@@ -247,6 +277,22 @@ declare namespace WAWebJS {
247
277
  reaction: Reaction
248
278
  ) => void): this
249
279
 
280
+ /** Emitted when a chat is removed */
281
+ on(event: 'chat_removed', listener: (
282
+ /** The chat that was removed */
283
+ chat: Chat
284
+ ) => void): this
285
+
286
+ /** Emitted when a chat is archived/unarchived */
287
+ on(event: 'chat_archived', listener: (
288
+ /** The chat that was archived/unarchived */
289
+ chat: Chat,
290
+ /** State the chat is currently in */
291
+ currState: boolean,
292
+ /** State the chat was previously in */
293
+ prevState: boolean
294
+ ) => void): this
295
+
250
296
  /** Emitted when loading screen is appearing */
251
297
  on(event: 'loading_screen', listener: (percent: string, message: string) => void): this
252
298
 
@@ -319,6 +365,10 @@ declare namespace WAWebJS {
319
365
  puppeteer?: puppeteer.PuppeteerNodeLaunchOptions & puppeteer.ConnectOptions
320
366
  /** Determines how to save and restore sessions. Will use LegacySessionAuth if options.session is set. Otherwise, NoAuth will be used. */
321
367
  authStrategy?: AuthStrategy,
368
+ /** The version of WhatsApp Web to use. Use options.webVersionCache to configure how the version is retrieved. */
369
+ webVersion?: string,
370
+ /** Determines how to retrieve the WhatsApp Web version specified in options.webVersion. */
371
+ webVersionCache?: WebCacheOptions,
322
372
  /** How many times should the qrcode be refreshed before giving up
323
373
  * @default 0 (disabled) */
324
374
  qrMaxRetries?: number,
@@ -341,9 +391,29 @@ declare namespace WAWebJS {
341
391
  userAgent?: string
342
392
  /** Ffmpeg path to use when formating videos to webp while sending stickers
343
393
  * @default 'ffmpeg' */
344
- ffmpegPath?: string
394
+ ffmpegPath?: string,
395
+ /** Object with proxy autentication requirements @default: undefined */
396
+ proxyAuthentication?: {username: string, password: string} | undefined
397
+ }
398
+
399
+ export interface LocalWebCacheOptions {
400
+ type: 'local',
401
+ path?: string,
402
+ strict?: boolean
403
+ }
404
+
405
+ export interface RemoteWebCacheOptions {
406
+ type: 'remote',
407
+ remotePath: string,
408
+ strict?: boolean
409
+ }
410
+
411
+ export interface NoWebCacheOptions {
412
+ type: 'none'
345
413
  }
346
414
 
415
+ export type WebCacheOptions = NoWebCacheOptions | LocalWebCacheOptions | RemoteWebCacheOptions;
416
+
347
417
  /**
348
418
  * Base class which all authentication strategies extend
349
419
  */
@@ -498,8 +568,10 @@ declare namespace WAWebJS {
498
568
  MESSAGE_REVOKED_ME = 'message_revoke_me',
499
569
  MESSAGE_ACK = 'message_ack',
500
570
  MEDIA_UPLOADED = 'media_uploaded',
571
+ CONTACT_CHANGED = 'contact_changed',
501
572
  GROUP_JOIN = 'group_join',
502
573
  GROUP_LEAVE = 'group_leave',
574
+ GROUP_ADMIN_CHANGED = 'group_admin_changed',
503
575
  GROUP_UPDATE = 'group_update',
504
576
  QR_RECEIVED = 'qr',
505
577
  LOADING_SCREEN = 'loading_screen',
@@ -637,6 +709,7 @@ declare namespace WAWebJS {
637
709
  * broadcast: false,
638
710
  * fromMe: false,
639
711
  * hasQuotedMsg: false,
712
+ * hasReaction: false,
640
713
  * location: undefined,
641
714
  * mentionedIds: []
642
715
  * }
@@ -666,6 +739,8 @@ declare namespace WAWebJS {
666
739
  hasMedia: boolean,
667
740
  /** Indicates if the message was sent as a reply to another message */
668
741
  hasQuotedMsg: boolean,
742
+ /** Indicates whether there are reactions to the message */
743
+ hasReaction: boolean,
669
744
  /** Indicates the duration of the message in seconds */
670
745
  duration: string,
671
746
  /** ID that represents the message */
@@ -767,6 +842,10 @@ declare namespace WAWebJS {
767
842
  * Gets the payment details associated with a given message
768
843
  */
769
844
  getPayment: () => Promise<Payment>,
845
+ /**
846
+ * Gets the reactions associated with the given message
847
+ */
848
+ getReactions: () => Promise<ReactionList[]>,
770
849
  }
771
850
 
772
851
  /** ID that represents a message */
@@ -1019,6 +1098,8 @@ declare namespace WAWebJS {
1019
1098
  timestamp: number,
1020
1099
  /** Amount of messages unread */
1021
1100
  unreadCount: number,
1101
+ /** Last message fo chat */
1102
+ lastMessage: Message,
1022
1103
 
1023
1104
  /** Archives this chat */
1024
1105
  archive: () => Promise<void>,
@@ -1162,6 +1243,10 @@ declare namespace WAWebJS {
1162
1243
  revokeInvite: () => Promise<void>;
1163
1244
  /** Makes the bot leave the group */
1164
1245
  leave: () => Promise<void>;
1246
+ /** Sets the group's picture.*/
1247
+ setPicture: (media: MessageMedia) => Promise<boolean>;
1248
+ /** Deletes the group's picture */
1249
+ deletePicture: () => Promise<boolean>;
1165
1250
  }
1166
1251
 
1167
1252
  /**
@@ -1365,6 +1450,13 @@ declare namespace WAWebJS {
1365
1450
  senderId: string
1366
1451
  ack?: number
1367
1452
  }
1453
+
1454
+ export type ReactionList = {
1455
+ id: string,
1456
+ aggregateEmoji: string,
1457
+ hasReactionByMe: boolean,
1458
+ senders: Array<Reaction>
1459
+ }
1368
1460
  }
1369
1461
 
1370
1462
  export = WAWebJS
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whatsapp-web.js",
3
- "version": "1.20.0-alpha.0",
3
+ "version": "1.21.0",
4
4
  "description": "Library for interacting with the WhatsApp Web API ",
5
5
  "main": "./index.js",
6
6
  "typings": "./index.d.ts",
package/src/Client.js CHANGED
@@ -10,7 +10,8 @@ const { WhatsWebURL, DefaultOptions, Events, WAState } = require('./util/Constan
10
10
  const { ExposeStore, LoadUtils } = require('./util/Injected');
11
11
  const ChatFactory = require('./factories/ChatFactory');
12
12
  const ContactFactory = require('./factories/ContactFactory');
13
- const { ClientInfo, Message, MessageMedia, Contact, Location, GroupNotification, Label, Call, Buttons, List, Reaction } = require('./structures');
13
+ const WebCacheFactory = require('./webCache/WebCacheFactory');
14
+ const { ClientInfo, Message, MessageMedia, Contact, Location, GroupNotification, Label, Call, Buttons, List, Reaction, Chat } = require('./structures');
14
15
  const LegacySessionAuth = require('./authStrategies/LegacySessionAuth');
15
16
  const NoAuth = require('./authStrategies/NoAuth');
16
17
 
@@ -19,6 +20,8 @@ const NoAuth = require('./authStrategies/NoAuth');
19
20
  * @extends {EventEmitter}
20
21
  * @param {object} options - Client options
21
22
  * @param {AuthStrategy} options.authStrategy - Determines how to save and restore sessions. Will use LegacySessionAuth if options.session is set. Otherwise, NoAuth will be used.
23
+ * @param {string} options.webVersion - The version of WhatsApp Web to use. Use options.webVersionCache to configure how the version is retrieved.
24
+ * @param {object} options.webVersionCache - Determines how to retrieve the WhatsApp Web version. Defaults to a local cache (LocalWebCache) that falls back to latest if the requested version is not found.
22
25
  * @param {number} options.authTimeoutMs - Timeout for authentication selector in puppeteer
23
26
  * @param {object} options.puppeteer - Puppeteer launch options. View docs here: https://github.com/puppeteer/puppeteer/
24
27
  * @param {number} options.qrMaxRetries - How many times should the qrcode be refreshed before giving up
@@ -29,6 +32,7 @@ const NoAuth = require('./authStrategies/NoAuth');
29
32
  * @param {string} options.userAgent - User agent to use in puppeteer
30
33
  * @param {string} options.ffmpegPath - Ffmpeg path to use when formating videos to webp while sending stickers
31
34
  * @param {boolean} options.bypassCSP - Sets bypassing of page's Content-Security-Policy.
35
+ * @param {object} options.proxyAuthentication - Proxy Authentication object.
32
36
  *
33
37
  * @fires Client#qr
34
38
  * @fires Client#authenticated
@@ -45,6 +49,8 @@ const NoAuth = require('./authStrategies/NoAuth');
45
49
  * @fires Client#group_update
46
50
  * @fires Client#disconnected
47
51
  * @fires Client#change_state
52
+ * @fires Client#contact_changed
53
+ * @fires Client#group_admin_changed
48
54
  */
49
55
  class Client extends EventEmitter {
50
56
  constructor(options = {}) {
@@ -100,6 +106,10 @@ class Client extends EventEmitter {
100
106
  browser = await puppeteer.launch({...puppeteerOpts, args: browserArgs});
101
107
  page = (await browser.pages())[0];
102
108
  }
109
+
110
+ if (this.options.proxyAuthentication !== undefined) {
111
+ await page.authenticate(this.options.proxyAuthentication);
112
+ }
103
113
 
104
114
  await page.setUserAgent(this.options.userAgent);
105
115
  if (this.options.bypassCSP) await page.setBypassCSP(true);
@@ -108,6 +118,7 @@ class Client extends EventEmitter {
108
118
  this.pupPage = page;
109
119
 
110
120
  await this.authStrategy.afterBrowserInitialized();
121
+ await this.initWebVersionCache();
111
122
 
112
123
  await page.goto(WhatsWebURL, {
113
124
  waitUntil: 'load',
@@ -317,6 +328,13 @@ class Client extends EventEmitter {
317
328
  * @param {GroupNotification} notification GroupNotification with more information about the action
318
329
  */
319
330
  this.emit(Events.GROUP_LEAVE, notification);
331
+ } else if (msg.subtype === 'promote' || msg.subtype === 'demote') {
332
+ /**
333
+ * Emitted when a current user is promoted to an admin or demoted to a regular user.
334
+ * @event Client#group_admin_changed
335
+ * @param {GroupNotification} notification GroupNotification with more information about the action
336
+ */
337
+ this.emit(Events.GROUP_ADMIN_CHANGED, notification);
320
338
  } else {
321
339
  /**
322
340
  * Emitted when group settings are updated, such as subject, description or picture.
@@ -376,6 +394,36 @@ class Client extends EventEmitter {
376
394
  last_message = msg;
377
395
  }
378
396
 
397
+ /**
398
+ * The event notification that is received when one of
399
+ * the group participants changes thier phone number.
400
+ */
401
+ const isParticipant = msg.type === 'gp2' && msg.subtype === 'modify';
402
+
403
+ /**
404
+ * The event notification that is received when one of
405
+ * the contacts changes thier phone number.
406
+ */
407
+ const isContact = msg.type === 'notification_template' && msg.subtype === 'change_number';
408
+
409
+ if (isParticipant || isContact) {
410
+ /** {@link GroupNotification} object does not provide enough information about this event, so a {@link Message} object is used. */
411
+ const message = new Message(this, msg);
412
+
413
+ const newId = isParticipant ? msg.recipients[0] : msg.to;
414
+ const oldId = isParticipant ? msg.author : msg.templateParams.find(id => id !== newId);
415
+
416
+ /**
417
+ * Emitted when a contact or a group participant changes their phone number.
418
+ * @event Client#contact_changed
419
+ * @param {Message} message Message with more information about the event.
420
+ * @param {String} oldId The user's id (an old one) who changed their phone number
421
+ * and who triggered the notification.
422
+ * @param {String} newId The user's new id after the change.
423
+ * @param {Boolean} isContact Indicates if a contact or a group participant changed their phone number.
424
+ */
425
+ this.emit(Events.CONTACT_CHANGED, message, oldId, newId, isContact);
426
+ }
379
427
  });
380
428
 
381
429
  await page.exposeFunction('onRemoveMessageEvent', (msg) => {
@@ -407,6 +455,15 @@ class Client extends EventEmitter {
407
455
 
408
456
  });
409
457
 
458
+ await page.exposeFunction('onChatUnreadCountEvent', async (data) =>{
459
+ const chat = await this.getChatById(data.id);
460
+
461
+ /**
462
+ * Emitted when the chat unread count changes
463
+ */
464
+ this.emit(Events.UNREAD_COUNT, chat);
465
+ });
466
+
410
467
  await page.exposeFunction('onMessageMediaUploadedEvent', (msg) => {
411
468
 
412
469
  const message = new Message(this, msg);
@@ -507,6 +564,26 @@ class Client extends EventEmitter {
507
564
  }
508
565
  });
509
566
 
567
+ await page.exposeFunction('onRemoveChatEvent', (chat) => {
568
+ /**
569
+ * Emitted when a chat is removed
570
+ * @event Client#chat_removed
571
+ * @param {Chat} chat
572
+ */
573
+ this.emit(Events.CHAT_REMOVED, new Chat(this, chat));
574
+ });
575
+
576
+ await page.exposeFunction('onArchiveChatEvent', (chat, currState, prevState) => {
577
+ /**
578
+ * Emitted when a chat is archived/unarchived
579
+ * @event Client#chat_archived
580
+ * @param {Chat} chat
581
+ * @param {boolean} currState
582
+ * @param {boolean} prevState
583
+ */
584
+ this.emit(Events.CHAT_ARCHIVED, new Chat(this, chat), currState, prevState);
585
+ });
586
+
510
587
  await page.evaluate(() => {
511
588
  window.Store.Msg.on('change', (msg) => { window.onChangeMessageEvent(window.WWebJS.getMessageModel(msg)); });
512
589
  window.Store.Msg.on('change:type', (msg) => { window.onChangeMessageTypeEvent(window.WWebJS.getMessageModel(msg)); });
@@ -516,6 +593,8 @@ class Client extends EventEmitter {
516
593
  window.Store.AppState.on('change:state', (_AppState, state) => { window.onAppStateChangedEvent(state); });
517
594
  window.Store.Conn.on('change:battery', (state) => { window.onBatteryStateChangedEvent(state); });
518
595
  window.Store.Call.on('add', (call) => { window.onIncomingCall(call); });
596
+ window.Store.Chat.on('remove', async (chat) => { window.onRemoveChatEvent(await window.WWebJS.getChatModel(chat)); });
597
+ window.Store.Chat.on('change:archive', async (chat, currState, prevState) => { window.onArchiveChatEvent(await window.WWebJS.getChatModel(chat), currState, prevState); });
519
598
  window.Store.Msg.on('add', (msg) => {
520
599
  if (msg.isNewMsg) {
521
600
  if(msg.type === 'ciphertext') {
@@ -526,7 +605,8 @@ class Client extends EventEmitter {
526
605
  }
527
606
  }
528
607
  });
529
-
608
+ window.Store.Chat.on('change:unreadCount', (chat) => {window.onChatUnreadCountEvent(chat);});
609
+
530
610
  {
531
611
  const module = window.Store.createOrUpdateReactionsModule;
532
612
  const ogMethod = module.createOrUpdateReactions;
@@ -562,6 +642,35 @@ class Client extends EventEmitter {
562
642
  });
563
643
  }
564
644
 
645
+ async initWebVersionCache() {
646
+ const { type: webCacheType, ...webCacheOptions } = this.options.webVersionCache;
647
+ const webCache = WebCacheFactory.createWebCache(webCacheType, webCacheOptions);
648
+
649
+ const requestedVersion = this.options.webVersion;
650
+ const versionContent = await webCache.resolve(requestedVersion);
651
+
652
+ if(versionContent) {
653
+ await this.pupPage.setRequestInterception(true);
654
+ this.pupPage.on('request', async (req) => {
655
+ if(req.url() === WhatsWebURL) {
656
+ req.respond({
657
+ status: 200,
658
+ contentType: 'text/html',
659
+ body: versionContent
660
+ });
661
+ } else {
662
+ req.continue();
663
+ }
664
+ });
665
+ } else {
666
+ this.pupPage.on('response', async (res) => {
667
+ if(res.ok() && res.url() === WhatsWebURL) {
668
+ await webCache.persist(await res.text());
669
+ }
670
+ });
671
+ }
672
+ }
673
+
565
674
  /**
566
675
  * Closes the client
567
676
  */
@@ -937,7 +1046,7 @@ class Client extends EventEmitter {
937
1046
  unmuteDate = unmuteDate ? unmuteDate.getTime() / 1000 : -1;
938
1047
  await this.pupPage.evaluate(async (chatId, timestamp) => {
939
1048
  let chat = await window.Store.Chat.get(chatId);
940
- await chat.mute.mute(timestamp, !0);
1049
+ await chat.mute.mute({expiration: timestamp, sendDevice:!0});
941
1050
  }, chatId, unmuteDate || -1);
942
1051
  }
943
1052
 
@@ -1178,6 +1287,31 @@ class Client extends EventEmitter {
1178
1287
 
1179
1288
  return blockedContacts.map(contact => ContactFactory.create(this.client, contact));
1180
1289
  }
1290
+
1291
+ /**
1292
+ * Sets the current user's profile picture.
1293
+ * @param {MessageMedia} media
1294
+ * @returns {Promise<boolean>} Returns true if the picture was properly updated.
1295
+ */
1296
+ async setProfilePicture(media) {
1297
+ const success = await this.pupPage.evaluate((chatid, media) => {
1298
+ return window.WWebJS.setPicture(chatid, media);
1299
+ }, this.info.wid._serialized, media);
1300
+
1301
+ return success;
1302
+ }
1303
+
1304
+ /**
1305
+ * Deletes the current user's profile picture.
1306
+ * @returns {Promise<boolean>} Returns true if the picture was properly deleted.
1307
+ */
1308
+ async deleteProfilePicture() {
1309
+ const success = await this.pupPage.evaluate((chatid) => {
1310
+ return window.WWebJS.deletePicture(chatid);
1311
+ }, this.info.wid._serialized);
1312
+
1313
+ return success;
1314
+ }
1181
1315
  }
1182
1316
 
1183
1317
  module.exports = Client;
@@ -75,6 +75,12 @@ class Chat extends Base {
75
75
  */
76
76
  this.muteExpiration = data.muteExpiration;
77
77
 
78
+ /**
79
+ * Last message fo chat
80
+ * @type {Message}
81
+ */
82
+ this.lastMessage = data.lastMessage ? new Message(super.client, data.lastMessage) : undefined;
83
+
78
84
  return super._patch(data);
79
85
  }
80
86
 
@@ -213,6 +213,31 @@ class GroupChat extends Chat {
213
213
  return true;
214
214
  }
215
215
 
216
+ /**
217
+ * Deletes the group's picture.
218
+ * @returns {Promise<boolean>} Returns true if the picture was properly deleted. This can return false if the user does not have the necessary permissions.
219
+ */
220
+ async deletePicture() {
221
+ const success = await this.client.pupPage.evaluate((chatid) => {
222
+ return window.WWebJS.deletePicture(chatid);
223
+ }, this.id._serialized);
224
+
225
+ return success;
226
+ }
227
+
228
+ /**
229
+ * Sets the group's picture.
230
+ * @param {MessageMedia} media
231
+ * @returns {Promise<boolean>} Returns true if the picture was properly updated. This can return false if the user does not have the necessary permissions.
232
+ */
233
+ async setPicture(media) {
234
+ const success = await this.client.pupPage.evaluate((chatid, media) => {
235
+ return window.WWebJS.setPicture(chatid, media);
236
+ }, this.id._serialized, media);
237
+
238
+ return success;
239
+ }
240
+
216
241
  /**
217
242
  * Gets the invite code for a specific group
218
243
  * @returns {Promise<string>} Group's invite code
@@ -5,7 +5,8 @@ const MessageMedia = require('./MessageMedia');
5
5
  const Location = require('./Location');
6
6
  const Order = require('./Order');
7
7
  const Payment = require('./Payment');
8
- const { MessageTypes } = require('../util/Constants');
8
+ const Reaction = require('./Reaction');
9
+ const {MessageTypes} = require('../util/Constants');
9
10
 
10
11
  /**
11
12
  * Represents a Message on WhatsApp
@@ -88,8 +89,7 @@ class Message extends Base {
88
89
  * String that represents from which device type the message was sent
89
90
  * @type {string}
90
91
  */
91
- this.deviceType = data.id.id.length > 21 ? 'android' : data.id.id.substring(0, 2) == '3A' ? 'ios' : 'web';
92
-
92
+ this.deviceType = typeof data.id.id === 'string' && data.id.id.length > 21 ? 'android' : typeof data.id.id === 'string' && data.id.id.substring(0, 2) === '3A' ? 'ios' : 'web';
93
93
  /**
94
94
  * Indicates if the message was forwarded
95
95
  * @type {boolean}
@@ -108,7 +108,7 @@ class Message extends Base {
108
108
  * Indicates if the message is a status update
109
109
  * @type {boolean}
110
110
  */
111
- this.isStatus = data.isStatusV3;
111
+ this.isStatus = data.isStatusV3 || data.id.remote === 'status@broadcast';
112
112
 
113
113
  /**
114
114
  * Indicates if the message was starred
@@ -134,6 +134,12 @@ class Message extends Base {
134
134
  */
135
135
  this.hasQuotedMsg = data.quotedMsg ? true : false;
136
136
 
137
+ /**
138
+ * Indicates whether there are reactions to the message
139
+ * @type {boolean}
140
+ */
141
+ this.hasReaction = data.hasReaction ? true : false;
142
+
137
143
  /**
138
144
  * Indicates the duration of the message in seconds
139
145
  * @type {string}
@@ -403,7 +409,7 @@ class Message extends Base {
403
409
  }
404
410
 
405
411
  try {
406
- const decryptedMedia = await window.Store.DownloadManager.downloadAndDecrypt({
412
+ const decryptedMedia = await window.Store.DownloadManager.downloadAndMaybeDecrypt({
407
413
  directPath: msg.directPath,
408
414
  encFilehash: msg.encFilehash,
409
415
  filehash: msg.filehash,
@@ -436,15 +442,16 @@ class Message extends Base {
436
442
  * @param {?boolean} everyone If true and the message is sent by the current user or the user is an admin, will delete it for everyone in the chat.
437
443
  */
438
444
  async delete(everyone) {
439
- await this.client.pupPage.evaluate((msgId, everyone) => {
445
+ await this.client.pupPage.evaluate(async (msgId, everyone) => {
440
446
  let msg = window.Store.Msg.get(msgId);
441
-
447
+ let chat = await window.Store.Chat.find(msg.id.remote);
448
+
442
449
  const canRevoke = window.Store.MsgActionChecks.canSenderRevokeMsg(msg) || window.Store.MsgActionChecks.canAdminRevokeMsg(msg);
443
450
  if (everyone && canRevoke) {
444
- return window.Store.Cmd.sendRevokeMsgs(msg.chat, [msg], { type: msg.id.fromMe ? 'Sender' : 'Admin' });
451
+ return window.Store.Cmd.sendRevokeMsgs(chat, [msg], { clearMedia: true, type: msg.id.fromMe ? 'Sender' : 'Admin' });
445
452
  }
446
453
 
447
- return window.Store.Cmd.sendDeleteMsgs(msg.chat, [msg], true);
454
+ return window.Store.Cmd.sendDeleteMsgs(chat, [msg], true);
448
455
  }, this.id._serialized, everyone);
449
456
  }
450
457
 
@@ -452,11 +459,12 @@ class Message extends Base {
452
459
  * Stars this message
453
460
  */
454
461
  async star() {
455
- await this.client.pupPage.evaluate((msgId) => {
462
+ await this.client.pupPage.evaluate(async (msgId) => {
456
463
  let msg = window.Store.Msg.get(msgId);
457
-
464
+
458
465
  if (window.Store.MsgActionChecks.canStarMsg(msg)) {
459
- return window.Store.Cmd.sendStarMsgs(msg.chat, [msg], false);
466
+ let chat = await window.Store.Chat.find(msg.id.remote);
467
+ return window.Store.Cmd.sendStarMsgs(chat, [msg], false);
460
468
  }
461
469
  }, this.id._serialized);
462
470
  }
@@ -465,11 +473,12 @@ class Message extends Base {
465
473
  * Unstars this message
466
474
  */
467
475
  async unstar() {
468
- await this.client.pupPage.evaluate((msgId) => {
476
+ await this.client.pupPage.evaluate(async (msgId) => {
469
477
  let msg = window.Store.Msg.get(msgId);
470
478
 
471
479
  if (window.Store.MsgActionChecks.canStarMsg(msg)) {
472
- return window.Store.Cmd.sendUnstarMsgs(msg.chat, [msg], false);
480
+ let chat = await window.Store.Chat.find(msg.id.remote);
481
+ return window.Store.Cmd.sendUnstarMsgs(chat, [msg], false);
473
482
  }
474
483
  }, this.id._serialized);
475
484
  }
@@ -529,6 +538,44 @@ class Message extends Base {
529
538
  }
530
539
  return undefined;
531
540
  }
541
+
542
+
543
+ /**
544
+ * Reaction List
545
+ * @typedef {Object} ReactionList
546
+ * @property {string} id Original emoji
547
+ * @property {string} aggregateEmoji aggregate emoji
548
+ * @property {boolean} hasReactionByMe Flag who sent the reaction
549
+ * @property {Array<Reaction>} senders Reaction senders, to this message
550
+ */
551
+
552
+ /**
553
+ * Gets the reactions associated with the given message
554
+ * @return {Promise<ReactionList[]>}
555
+ */
556
+ async getReactions() {
557
+ if (!this.hasReaction) {
558
+ return undefined;
559
+ }
560
+
561
+ const reactions = await this.client.pupPage.evaluate(async (msgId) => {
562
+ const msgReactions = await window.Store.Reactions.find(msgId);
563
+ if (!msgReactions || !msgReactions.reactions.length) return null;
564
+ return msgReactions.reactions.serialize();
565
+ }, this.id._serialized);
566
+
567
+ if (!reactions) {
568
+ return undefined;
569
+ }
570
+
571
+ return reactions.map(reaction => {
572
+ reaction.senders = reaction.senders.map(sender => {
573
+ sender.timestamp = Math.round(sender.timestamp / 1000);
574
+ return new Reaction(this.client, sender);
575
+ });
576
+ return reaction;
577
+ });
578
+ }
532
579
  }
533
580
 
534
581
  module.exports = Message;
@@ -7,13 +7,18 @@ exports.DefaultOptions = {
7
7
  headless: true,
8
8
  defaultViewport: null
9
9
  },
10
+ webVersion: '2.2322.15',
11
+ webVersionCache: {
12
+ type: 'local',
13
+ },
10
14
  authTimeoutMs: 0,
11
15
  qrMaxRetries: 0,
12
16
  takeoverOnConflict: false,
13
17
  takeoverTimeoutMs: 0,
14
18
  userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36',
15
19
  ffmpegPath: 'ffmpeg',
16
- bypassCSP: false
20
+ bypassCSP: false,
21
+ proxyAuthentication: undefined
17
22
  };
18
23
 
19
24
  /**
@@ -36,15 +41,20 @@ exports.Events = {
36
41
  AUTHENTICATED: 'authenticated',
37
42
  AUTHENTICATION_FAILURE: 'auth_failure',
38
43
  READY: 'ready',
44
+ CHAT_REMOVED: 'chat_removed',
45
+ CHAT_ARCHIVED: 'chat_archived',
39
46
  MESSAGE_RECEIVED: 'message',
40
47
  MESSAGE_CREATE: 'message_create',
41
48
  MESSAGE_REVOKED_EVERYONE: 'message_revoke_everyone',
42
49
  MESSAGE_REVOKED_ME: 'message_revoke_me',
43
50
  MESSAGE_ACK: 'message_ack',
51
+ UNREAD_COUNT: 'unread_count',
44
52
  MESSAGE_REACTION: 'message_reaction',
45
53
  MEDIA_UPLOADED: 'media_uploaded',
54
+ CONTACT_CHANGED: 'contact_changed',
46
55
  GROUP_JOIN: 'group_join',
47
56
  GROUP_LEAVE: 'group_leave',
57
+ GROUP_ADMIN_CHANGED: 'group_admin_changed',
48
58
  GROUP_UPDATE: 'group_update',
49
59
  QR_RECEIVED: 'qr',
50
60
  LOADING_SCREEN: 'loading_screen',
@@ -17,7 +17,7 @@ exports.ExposeStore = (moduleRaidStr) => {
17
17
  window.Store.Invite = window.mR.findModule('resetGroupInviteCode')[0];
18
18
  window.Store.InviteInfo = window.mR.findModule('queryGroupInvite')[0];
19
19
  window.Store.Label = window.mR.findModule('LabelCollection')[0].LabelCollection;
20
- window.Store.MediaPrep = window.mR.findModule('MediaPrep')[0];
20
+ window.Store.MediaPrep = window.mR.findModule('prepRawMedia')[0];
21
21
  window.Store.MediaObject = window.mR.findModule('getOrCreateMediaObject')[0];
22
22
  window.Store.NumberInfo = window.mR.findModule('formattedPhoneNumber')[0];
23
23
  window.Store.MediaTypes = window.mR.findModule('msgToMediaType')[0];
@@ -25,7 +25,7 @@ exports.ExposeStore = (moduleRaidStr) => {
25
25
  window.Store.MsgKey = window.mR.findModule((module) => module.default && module.default.fromString)[0].default;
26
26
  window.Store.MessageInfo = window.mR.findModule('sendQueryMsgInfo')[0];
27
27
  window.Store.OpaqueData = window.mR.findModule(module => module.default && module.default.createFromData)[0].default;
28
- window.Store.QueryExist = window.mR.findModule('queryExists')[0].queryExists;
28
+ window.Store.QueryExist = window.mR.findModule('queryExists')[0] ? window.mR.findModule('queryExists')[0].queryExists : window.mR.findModule('queryExist')[0].queryWidExists;
29
29
  window.Store.QueryProduct = window.mR.findModule('queryProduct')[0];
30
30
  window.Store.QueryOrder = window.mR.findModule('queryOrder')[0];
31
31
  window.Store.SendClear = window.mR.findModule('sendClear')[0];
@@ -41,7 +41,7 @@ exports.ExposeStore = (moduleRaidStr) => {
41
41
  window.Store.ProfilePic = window.mR.findModule('profilePicResync')[0];
42
42
  window.Store.PresenceUtils = window.mR.findModule('sendPresenceAvailable')[0];
43
43
  window.Store.ChatState = window.mR.findModule('sendChatStateComposing')[0];
44
- window.Store.GroupParticipants = window.mR.findModule('promoteParticipants')[1];
44
+ window.Store.GroupParticipants = window.mR.findModule('promoteParticipants')[0];
45
45
  window.Store.JoinInviteV4 = window.mR.findModule('sendJoinGroupViaInviteV4')[0];
46
46
  window.Store.findCommonGroups = window.mR.findModule('findCommonGroups')[0].findCommonGroups;
47
47
  window.Store.StatusUtils = window.mR.findModule('setMyStatus')[0];
@@ -54,6 +54,8 @@ exports.ExposeStore = (moduleRaidStr) => {
54
54
  window.Store.QuotedMsg = window.mR.findModule('getQuotedMsgObj')[0];
55
55
  window.Store.Socket = window.mR.findModule('deprecatedSendIq')[0];
56
56
  window.Store.SocketWap = window.mR.findModule('wap')[0];
57
+ window.Store.SearchContext = window.mR.findModule('getSearchContext')[0].getSearchContext;
58
+ window.Store.DrawerManager = window.mR.findModule('DrawerManager')[0].DrawerManager;
57
59
  window.Store.StickerTools = {
58
60
  ...window.mR.findModule('toWebpSticker')[0],
59
61
  ...window.mR.findModule('addWebpMetadata')[0]
@@ -62,7 +64,8 @@ exports.ExposeStore = (moduleRaidStr) => {
62
64
  window.Store.GroupUtils = {
63
65
  ...window.mR.findModule('createGroup')[0],
64
66
  ...window.mR.findModule('setGroupDescription')[0],
65
- ...window.mR.findModule('sendExitGroup')[0]
67
+ ...window.mR.findModule('sendExitGroup')[0],
68
+ ...window.mR.findModule('sendSetPicture')[0]
66
69
  };
67
70
 
68
71
  if (!window.Store.Chat._find) {
@@ -116,7 +119,10 @@ exports.LoadUtils = () => {
116
119
  forceDocument: options.sendMediaAsDocument,
117
120
  forceGif: options.sendVideoAsGif
118
121
  });
119
-
122
+
123
+ if (options.caption){
124
+ attOptions.caption = options.caption;
125
+ }
120
126
  content = options.sendMediaAsSticker ? undefined : attOptions.preview;
121
127
 
122
128
  delete options.attachment;
@@ -242,11 +248,12 @@ exports.LoadUtils = () => {
242
248
 
243
249
  const meUser = window.Store.User.getMaybeMeUser();
244
250
  const isMD = window.Store.MDBackend;
245
-
251
+ const newId = await window.Store.MsgKey.newId();
252
+
246
253
  const newMsgId = new window.Store.MsgKey({
247
254
  from: meUser,
248
255
  to: chat.id,
249
- id: window.Store.MsgKey.newId(),
256
+ id: newId,
250
257
  participant: isMD && chat.id.isGroup() ? meUser : undefined,
251
258
  selfDir: 'out',
252
259
  });
@@ -271,6 +278,7 @@ exports.LoadUtils = () => {
271
278
  ...ephemeralFields,
272
279
  ...locationOptions,
273
280
  ...attOptions,
281
+ ...(attOptions.toJSON ? attOptions.toJSON() : {}),
274
282
  ...quotedMsgOptions,
275
283
  ...vcardOptions,
276
284
  ...buttonOptions,
@@ -426,7 +434,15 @@ exports.LoadUtils = () => {
426
434
  await window.Store.GroupMetadata.update(chatWid);
427
435
  res.groupMetadata = chat.groupMetadata.serialize();
428
436
  }
429
-
437
+
438
+ res.lastMessage = null;
439
+ if (res.msgs && res.msgs.length) {
440
+ const lastMessage = window.Store.Msg.get(chat.lastReceivedKey._serialized);
441
+ if (lastMessage) {
442
+ res.lastMessage = window.WWebJS.getMessageModel(lastMessage);
443
+ }
444
+ }
445
+
430
446
  delete res.msgs;
431
447
  delete res.msgUnsyncedButtonReplyMsgs;
432
448
  delete res.unsyncedButtonReplies;
@@ -625,4 +641,73 @@ exports.LoadUtils = () => {
625
641
  ]);
626
642
  await window.Store.Socket.deprecatedCastStanza(stanza);
627
643
  };
644
+
645
+ window.WWebJS.cropAndResizeImage = async (media, options = {}) => {
646
+ if (!media.mimetype.includes('image'))
647
+ throw new Error('Media is not an image');
648
+
649
+ if (options.mimetype && !options.mimetype.includes('image'))
650
+ delete options.mimetype;
651
+
652
+ options = Object.assign({ size: 640, mimetype: media.mimetype, quality: .75, asDataUrl: false }, options);
653
+
654
+ const img = await new Promise ((resolve, reject) => {
655
+ const img = new Image();
656
+ img.onload = () => resolve(img);
657
+ img.onerror = reject;
658
+ img.src = `data:${media.mimetype};base64,${media.data}`;
659
+ });
660
+
661
+ const sl = Math.min(img.width, img.height);
662
+ const sx = Math.floor((img.width - sl) / 2);
663
+ const sy = Math.floor((img.height - sl) / 2);
664
+
665
+ const canvas = document.createElement('canvas');
666
+ canvas.width = options.size;
667
+ canvas.height = options.size;
668
+
669
+ const ctx = canvas.getContext('2d');
670
+ ctx.drawImage(img, sx, sy, sl, sl, 0, 0, options.size, options.size);
671
+
672
+ const dataUrl = canvas.toDataURL(options.mimetype, options.quality);
673
+
674
+ if (options.asDataUrl)
675
+ return dataUrl;
676
+
677
+ return Object.assign(media, {
678
+ mimetype: options.mimeType,
679
+ data: dataUrl.replace(`data:${options.mimeType};base64,`, '')
680
+ });
681
+ };
682
+
683
+ window.WWebJS.setPicture = async (chatid, media) => {
684
+ const thumbnail = await window.WWebJS.cropAndResizeImage(media, { asDataUrl: true, mimetype: 'image/jpeg', size: 96 });
685
+ const profilePic = await window.WWebJS.cropAndResizeImage(media, { asDataUrl: true, mimetype: 'image/jpeg', size: 640 });
686
+
687
+ const chatWid = window.Store.WidFactory.createWid(chatid);
688
+ try {
689
+ const collection = window.Store.ProfilePicThumb.get(chatid);
690
+ if (!collection.canSet()) return;
691
+
692
+ const res = await window.Store.GroupUtils.sendSetPicture(chatWid, thumbnail, profilePic);
693
+ return res ? res.status === 200 : false;
694
+ } catch (err) {
695
+ if(err.name === 'ServerStatusCodeError') return false;
696
+ throw err;
697
+ }
698
+ };
699
+
700
+ window.WWebJS.deletePicture = async (chatid) => {
701
+ const chatWid = window.Store.WidFactory.createWid(chatid);
702
+ try {
703
+ const collection = window.Store.ProfilePicThumb.get(chatid);
704
+ if (!collection.canDelete()) return;
705
+
706
+ const res = await window.Store.GroupUtils.requestDeletePicture(chatWid);
707
+ return res ? res.status === 200 : false;
708
+ } catch (err) {
709
+ if(err.name === 'ServerStatusCodeError') return false;
710
+ throw err;
711
+ }
712
+ };
628
713
  };
@@ -50,7 +50,9 @@ class InterfaceController {
50
50
  async openChatWindowAt(msgId) {
51
51
  await this.pupPage.evaluate(async msgId => {
52
52
  let msg = await window.Store.Msg.get(msgId);
53
- await window.Store.Cmd.openChatAt(msg.chat, msg.chat.getSearchContext(msg));
53
+ let chat = await window.Store.Chat.find(msg.id.remote);
54
+ let searchContext = await window.Store.SearchContext(chat,msg);
55
+ await window.Store.Cmd.openChatAt(chat, searchContext);
54
56
  }, msgId);
55
57
  }
56
58
 
@@ -70,7 +72,7 @@ class InterfaceController {
70
72
  */
71
73
  async closeRightDrawer() {
72
74
  await this.pupPage.evaluate(async () => {
73
- await window.Store.Cmd.closeDrawerRight();
75
+ await window.Store.DrawerManager.closeDrawerRight();
74
76
  });
75
77
  }
76
78
 
@@ -0,0 +1,43 @@
1
+ const path = require('path');
2
+ const fs = require('fs');
3
+
4
+ const { WebCache, VersionResolveError } = require('./WebCache');
5
+
6
+ /**
7
+ * LocalWebCache - Fetches a WhatsApp Web version from a local file store
8
+ * @param {object} options - options
9
+ * @param {string} options.path - Path to the directory where cached versions are saved, default is: "./.wwebjs_cache/"
10
+ * @param {boolean} options.strict - If true, will throw an error if the requested version can't be fetched. If false, will resolve to the latest version.
11
+ */
12
+ class LocalWebCache extends WebCache {
13
+ constructor(options = {}) {
14
+ super();
15
+
16
+ this.path = options.path || './.wwebjs_cache/';
17
+ this.strict = options.strict || false;
18
+ }
19
+
20
+ async resolve(version) {
21
+ const filePath = path.join(this.path, `${version}.html`);
22
+
23
+ try {
24
+ return fs.readFileSync(filePath, 'utf-8');
25
+ }
26
+ catch (err) {
27
+ if (this.strict) throw new VersionResolveError(`Couldn't load version ${version} from the cache`);
28
+ return null;
29
+ }
30
+ }
31
+
32
+ async persist(indexHtml) {
33
+ // extract version from index (e.g. manifest-2.2206.9.json -> 2.2206.9)
34
+ const version = indexHtml.match(/manifest-([\d\\.]+)\.json/)[1];
35
+ if(!version) return;
36
+
37
+ const filePath = path.join(this.path, `${version}.html`);
38
+ fs.mkdirSync(this.path, { recursive: true });
39
+ fs.writeFileSync(filePath, indexHtml);
40
+ }
41
+ }
42
+
43
+ module.exports = LocalWebCache;
@@ -0,0 +1,40 @@
1
+ const fetch = require('node-fetch');
2
+ const { WebCache, VersionResolveError } = require('./WebCache');
3
+
4
+ /**
5
+ * RemoteWebCache - Fetches a WhatsApp Web version index from a remote server
6
+ * @param {object} options - options
7
+ * @param {string} options.remotePath - Endpoint that should be used to fetch the version index. Use {version} as a placeholder for the version number.
8
+ * @param {boolean} options.strict - If true, will throw an error if the requested version can't be fetched. If false, will resolve to the latest version. Defaults to false.
9
+ */
10
+ class RemoteWebCache extends WebCache {
11
+ constructor(options = {}) {
12
+ super();
13
+
14
+ if (!options.remotePath) throw new Error('webVersionCache.remotePath is required when using the remote cache');
15
+ this.remotePath = options.remotePath;
16
+ this.strict = options.strict || false;
17
+ }
18
+
19
+ async resolve(version) {
20
+ const remotePath = this.remotePath.replace('{version}', version);
21
+
22
+ try {
23
+ const cachedRes = await fetch(remotePath);
24
+ if (cachedRes.ok) {
25
+ return cachedRes.text();
26
+ }
27
+ } catch (err) {
28
+ console.error(`Error fetching version ${version} from remote`, err);
29
+ }
30
+
31
+ if (this.strict) throw new VersionResolveError(`Couldn't load version ${version} from the archive`);
32
+ return null;
33
+ }
34
+
35
+ async persist() {
36
+ // Nothing to do here
37
+ }
38
+ }
39
+
40
+ module.exports = RemoteWebCache;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Default implementation of a web version cache that does nothing.
3
+ */
4
+ class WebCache {
5
+ async resolve() { return null; }
6
+ async persist() { }
7
+ }
8
+
9
+ class VersionResolveError extends Error { }
10
+
11
+ module.exports = {
12
+ WebCache,
13
+ VersionResolveError
14
+ };
@@ -0,0 +1,20 @@
1
+ const RemoteWebCache = require('./RemoteWebCache');
2
+ const LocalWebCache = require('./LocalWebCache');
3
+ const { WebCache } = require('./WebCache');
4
+
5
+ const createWebCache = (type, options) => {
6
+ switch (type) {
7
+ case 'remote':
8
+ return new RemoteWebCache(options);
9
+ case 'local':
10
+ return new LocalWebCache(options);
11
+ case 'none':
12
+ return new WebCache();
13
+ default:
14
+ throw new Error(`Invalid WebCache type ${type}`);
15
+ }
16
+ };
17
+
18
+ module.exports = {
19
+ createWebCache,
20
+ };