├── extension-chrome ├── images │ ├── icon128.png │ ├── icon48.png │ ├── icon16-off.png │ ├── icon16-on.png │ ├── icon32-off.png │ └── icon32-on.png ├── manifest.json ├── background.js ├── popup │ ├── popup.html │ ├── popup.css │ └── popup.js └── script │ ├── content_script.js │ └── wappbot.js ├── LICENSE ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── README.md ├── wappbot.js └── newFullMethodWapi.js /extension-chrome/images/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boehlergerman/WappBot/HEAD/extension-chrome/images/icon128.png -------------------------------------------------------------------------------- /extension-chrome/images/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boehlergerman/WappBot/HEAD/extension-chrome/images/icon48.png -------------------------------------------------------------------------------- /extension-chrome/images/icon16-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boehlergerman/WappBot/HEAD/extension-chrome/images/icon16-off.png -------------------------------------------------------------------------------- /extension-chrome/images/icon16-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boehlergerman/WappBot/HEAD/extension-chrome/images/icon16-on.png -------------------------------------------------------------------------------- /extension-chrome/images/icon32-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boehlergerman/WappBot/HEAD/extension-chrome/images/icon32-off.png -------------------------------------------------------------------------------- /extension-chrome/images/icon32-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boehlergerman/WappBot/HEAD/extension-chrome/images/icon32-on.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 boehlergerman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /extension-chrome/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "WappBot", 3 | "description": "Automatic answering of incoming messages by means of javascript and using Whatsapp Web", 4 | "version": "2.0.1", 5 | "author": "Boehler German", 6 | "homepage_url": "https://github.com/boehlergerman/WappBot", 7 | "manifest_version": 2, 8 | "permissions": [ 9 | "tabs", 10 | "activeTab", 11 | "storage", 12 | "nativeMessaging", 13 | "webRequest", 14 | "webRequestBlocking", 15 | "browsingData", 16 | "https://*.whatsapp.com/" 17 | ], 18 | "background": { 19 | "scripts": [ 20 | "background.js" 21 | ] 22 | }, 23 | "content_scripts": [ 24 | { 25 | "matches": [ 26 | "https://*.whatsapp.com/" 27 | ], 28 | "js": [ 29 | "script/content_script.js" 30 | ] 31 | } 32 | ], 33 | "externally_connectable": { 34 | "matches": [ 35 | "https://*.whatsapp.com/" 36 | ] 37 | }, 38 | "web_accessible_resources": [ 39 | "script/wappbot.js" 40 | ], 41 | "content_security_policy": "script-src 'self' https://*.whatsapp.com/; object-src 'self'", 42 | "browser_action": { 43 | "default_title": "WappBot", 44 | "default_icon": { 45 | "16": "images/icon32-off.png" 46 | }, 47 | "default_popup": "popup/popup.html" 48 | }, 49 | "icons": { 50 | "48": "images/icon48.png", 51 | "128": "images/icon128.png" 52 | } 53 | } -------------------------------------------------------------------------------- /extension-chrome/background.js: -------------------------------------------------------------------------------- 1 | var isCSPDisabled = false; 2 | var tabid; 3 | var filter = { 4 | urls: ["*://*/*"], 5 | types: ["main_frame", "sub_frame"] 6 | }; 7 | 8 | var onHeadersReceived = function (details) { 9 | for (var i = 0; i < details.responseHeaders.length; i++) { 10 | if ('content-security-policy' === details.responseHeaders[i].name.toLowerCase()) { 11 | details.responseHeaders[i].value = ''; 12 | } 13 | } 14 | 15 | return { 16 | responseHeaders: details.responseHeaders 17 | }; 18 | }; 19 | 20 | function updateUI() { 21 | var iconName = isCSPDisabled ? 'on' : 'off'; 22 | var title = isCSPDisabled ? 'disabled' : 'enabled'; 23 | 24 | chrome.browserAction.setIcon({ path: "images/icon32-" + iconName + ".png" }); 25 | chrome.browserAction.setTitle({ title: 'Content-Security-Policy headers are ' + title }); 26 | } 27 | 28 | chrome.runtime.onMessage.addListener( 29 | function (request, sender, sendResponse) { 30 | 31 | isCSPDisabled = request; 32 | tabid = sender.tab.id; 33 | chrome.browsingData.remove({}, { "serviceWorkers": true }, function () { }); 34 | 35 | updateUI(); 36 | 37 | } 38 | ); 39 | 40 | chrome.webRequest.onHeadersReceived.addListener(onHeadersReceived, filter, ["blocking", "responseHeaders"]); 41 | 42 | chrome.tabs.onRemoved.addListener(function (tabCloseid, removed) { 43 | if (tabid === tabCloseid) { 44 | isCSPDisabled = false; 45 | updateUI(); 46 | } 47 | }) 48 | 49 | chrome.windows.onRemoved.addListener(function (windowid) { 50 | isCSPDisabled = false; 51 | updateUI(); 52 | }) 53 | 54 | // chrome.tabs.onUpdated.addListener(function (tabCloseid, changeInfo, tab) { 55 | // if (tabid === tabCloseid) { 56 | // console.log("Refresh Tab!"); 57 | // isCSPDisabled = false; 58 | // updateUI(); 59 | // } 60 | // }); -------------------------------------------------------------------------------- /extension-chrome/popup/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | WappBot 4 | 5 | 6 | 7 | 8 | 9 |

WappBot

10 |
11 | Use WappBot API? 12 | 13 | 14 | 23 |
24 |
25 |
26 | 33 |
34 |
35 | 43 |
44 |
45 | 52 |
53 |
54 | 55 |
56 |
57 | 58 |
59 | 60 |
61 |
62 | 63 |
64 | 65 | 66 | -------------------------------------------------------------------------------- /extension-chrome/script/content_script.js: -------------------------------------------------------------------------------- 1 | var pollInterval = 5000; 2 | var timerId; 3 | var isCSPDisabled = false; 4 | 5 | function injectVariables(variables, tag) { 6 | var node = document.getElementsByTagName(tag)[0]; 7 | var script = document.createElement("script"); 8 | script.setAttribute("type", "text/javascript"); 9 | console.log("[Info]: Injecting variables"); 10 | for (var i = 0; i < variables.length; i++) { 11 | script.textContent = 12 | "var " + 13 | variables[i].name + 14 | " = " + 15 | JSON.stringify(variables[i].value) + 16 | ";"; 17 | } 18 | node.appendChild(script); 19 | } 20 | function injectScript(file_path, tag) { 21 | var node = document.getElementsByTagName(tag)[0]; 22 | var script = document.createElement("script"); 23 | script.setAttribute("type", "text/javascript"); 24 | script.setAttribute("src", file_path); 25 | node.appendChild(script); 26 | } 27 | 28 | function injectCode(code, tag) { 29 | var node = document.getElementsByTagName(tag)[0]; 30 | var script = document.createElement("script"); 31 | script.setAttribute("type", "text/javascript"); 32 | script.textContent = code; 33 | node.appendChild(script); 34 | } 35 | 36 | 37 | function start() { 38 | try { 39 | console.log('[Info]: waiting for whatsapp start'); 40 | var elementOfInterest = document.getElementsByClassName('_1BjNO'); 41 | if (elementOfInterest.length > 0) { 42 | isCSPDisabled = !isCSPDisabled; 43 | 44 | chrome.storage.local.get(['WappBot'], function (result) { 45 | injectCode(`window["WappBot"] = ${JSON.stringify(result.WappBot)}`, 'body'); 46 | }); 47 | 48 | chrome.runtime.sendMessage(isCSPDisabled); 49 | 50 | injectVariables([{ name: "extensionID", value: chrome.runtime.id }], "body"); 51 | injectScript(chrome.extension.getURL("script/wappbot.js"), "body"); 52 | } 53 | else timerId = window.setTimeout(start, pollInterval); 54 | } catch (error) { 55 | console.error(error); 56 | } 57 | } 58 | 59 | chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) { 60 | if (message.type === "media_url") { 61 | var x = new XMLHttpRequest(); 62 | x.open("GET", message.url, true); 63 | x.responseType = "blob"; 64 | x.onload = function () { 65 | if (this.status == 200) { 66 | var myurl = window.URL.createObjectURL(this.response); 67 | sendResponse({ 68 | type: "media_success", 69 | url: myurl 70 | }); 71 | } else { 72 | sendResponse({ 73 | type: "media_fail" 74 | }); 75 | } 76 | }; 77 | x.send(); 78 | } 79 | return true; 80 | }); 81 | 82 | start(); -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at germanboehler@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository, please first discuss the change you wish to make via issue, 4 | email, or any other method with the owners of this repository before making a change. 5 | 6 | Please note we have a code of conduct, please follow it in all your interactions with the project. 7 | 8 | ## Pull Request Process 9 | 10 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a 11 | build. 12 | 2. Update the README.md with details of changes to the interface, this includes new environment 13 | variables, exposed ports, useful file locations and container parameters. 14 | 3. Increase the version numbers in any examples files and the README.md to the new version that this 15 | Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). 16 | 4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you 17 | do not have permission to do that, you may request the second reviewer to merge it for you. 18 | 19 | ## Code of Conduct 20 | 21 | ### Our Pledge 22 | 23 | In the interest of fostering an open and welcoming environment, we as 24 | contributors and maintainers pledge to making participation in our project and 25 | our community a harassment-free experience for everyone, regardless of age, body 26 | size, disability, ethnicity, gender identity and expression, level of experience, 27 | nationality, personal appearance, race, religion, or sexual identity and 28 | orientation. 29 | 30 | ### Our Standards 31 | 32 | Examples of behavior that contributes to creating a positive environment 33 | include: 34 | 35 | * Using welcoming and inclusive language 36 | * Being respectful of differing viewpoints and experiences 37 | * Gracefully accepting constructive criticism 38 | * Focusing on what is best for the community 39 | * Showing empathy towards other community members 40 | 41 | Examples of unacceptable behavior by participants include: 42 | 43 | * The use of sexualized language or imagery and unwelcome sexual attention or 44 | advances 45 | * Trolling, insulting/derogatory comments, and personal or political attacks 46 | * Public or private harassment 47 | * Publishing others' private information, such as a physical or electronic 48 | address, without explicit permission 49 | * Other conduct which could reasonably be considered inappropriate in a 50 | professional setting 51 | 52 | ### Our Responsibilities 53 | 54 | Project maintainers are responsible for clarifying the standards of acceptable 55 | behavior and are expected to take appropriate and fair corrective action in 56 | response to any instances of unacceptable behavior. 57 | 58 | Project maintainers have the right and responsibility to remove, edit, or 59 | reject comments, commits, code, wiki edits, issues, and other contributions 60 | that are not aligned to this Code of Conduct, or to ban temporarily or 61 | permanently any contributor for other behaviors that they deem inappropriate, 62 | threatening, offensive, or harmful. 63 | 64 | ### Scope 65 | 66 | This Code of Conduct applies both within project spaces and in public spaces 67 | when an individual is representing the project or its community. Examples of 68 | representing a project or community include using an official project e-mail 69 | address, posting via an official social media account, or acting as an appointed 70 | representative at an online or offline event. Representation of a project may be 71 | further defined and clarified by project maintainers. 72 | 73 | ### Enforcement 74 | 75 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 76 | reported by contacting the project team at [germanboehler@gmail.com]. All 77 | complaints will be reviewed and investigated and will result in a response that 78 | is deemed necessary and appropriate to the circumstances. The project team is 79 | obligated to maintain confidentiality with regard to the reporter of an incident. 80 | Further details of specific enforcement policies may be posted separately. 81 | 82 | Project maintainers who do not follow or enforce the Code of Conduct in good 83 | faith may face temporary or permanent repercussions as determined by other 84 | members of the project's leadership. 85 | 86 | ### Attribution 87 | 88 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 89 | available at [http://contributor-covenant.org/version/1/4][version] 90 | 91 | [homepage]: http://contributor-covenant.org 92 | [version]: http://contributor-covenant.org/version/1/4/ 93 | -------------------------------------------------------------------------------- /extension-chrome/popup/popup.css: -------------------------------------------------------------------------------- 1 | @import url(https://fonts.googleapis.com/css?family=ABeeZee); 2 | body { 3 | background: #2c3e50; 4 | margin-top: 10px; 5 | padding: 10px 0px; 6 | font-family: "ABeeZee", sans-serif; 7 | font-size: 15px; 8 | } 9 | 10 | h1 { 11 | text-align: center; 12 | color: #fff; 13 | font-size: 20px; 14 | margin-bottom: 10px; 15 | } 16 | 17 | .container { 18 | width: 260px; 19 | margin: 0 auto; 20 | border-bottom: 4px solid #3498db; 21 | padding: 8px 0px; 22 | -webkit-box-sizing: border-box; 23 | -moz-box-sizing: border-box; 24 | -ms-box-sizing: border-box; 25 | -o-box-sizing: border-box; 26 | box-sizing: border-box; 27 | } 28 | 29 | .subcontainer { 30 | border-width: 2px; 31 | border-color: #34495e; 32 | } 33 | 34 | input { 35 | display: none; 36 | font-size: 15px; 37 | } 38 | 39 | span { 40 | float: left; 41 | padding-bottom: 10px; 42 | color: #ecf0f1; 43 | } 44 | 45 | .style label { 46 | width: 18px; 47 | height: 18px; 48 | margin-bottom: -3px; 49 | margin-left: 10px; 50 | background: #e74c3c; 51 | border-radius: 3px; 52 | cursor: pointer; 53 | position: relative; 54 | display: inline-block; 55 | -webkit-transition: 300ms all; 56 | -moz-transition: 300ms all; 57 | -ms-transition: 300ms all; 58 | -o-transition: 300ms all; 59 | transition: 300ms all; 60 | } 61 | 62 | .style label::after { 63 | content: ""; 64 | position: absolute; 65 | width: 12px; 66 | height: 12px; 67 | left: 3px; 68 | top: 3px; 69 | background: #c0392b; 70 | border-radius: 3px; 71 | opacity: 1; 72 | z-index: 1; 73 | -webkit-transition: 300ms all; 74 | -moz-transition: 300ms all; 75 | -ms-transition: 300ms all; 76 | -o-transition: 300ms all; 77 | transition: 300ms all; 78 | } 79 | 80 | .style input:checked+label { 81 | background: #27ae60; 82 | } 83 | 84 | .style input:checked+label::after { 85 | opacity: 1; 86 | background: #2ecc71; 87 | } 88 | 89 | input[type=text], 90 | input[type=email] { 91 | display: inline-block; 92 | border: none; 93 | background: #34495e; 94 | -webkit-box-sizing: border-box; 95 | -moz-box-sizing: border-box; 96 | -ms-box-sizing: border-box; 97 | -o-box-sizing: border-box; 98 | box-sizing: border-box; 99 | padding: 8px; 100 | color: #ecf0f1; 101 | width: 100%; 102 | } 103 | 104 | input[type=text]:focus, 105 | input[type=email]:focus { 106 | outline: none; 107 | } 108 | 109 | input[type=text]::-webkit-input-placeholder, 110 | input[type=email]::-webkit-input-placeholder { 111 | color: #999; 112 | } 113 | 114 | input[type=text]:-moz-input-placeholder, 115 | input[type=email]:-moz-input-placeholder { 116 | color: #999; 117 | } 118 | 119 | input[type=text]::-moz-placeholder, 120 | input[type=email]::-moz-placeholder { 121 | color: #999; 122 | } 123 | 124 | input[type=text]:-ms-input-placeholder, 125 | input[type=email]:-ms-input-placeholder { 126 | color: #999; 127 | } 128 | 129 | textarea { 130 | width: 100%; 131 | max-width: 100%; 132 | min-width: 100%; 133 | min-height: 80px; 134 | -webkit-box-sizing: border-box; 135 | -moz-box-sizing: border-box; 136 | -ms-box-sizing: border-box; 137 | -o-box-sizing: border-box; 138 | box-sizing: border-box; 139 | padding: 8px; 140 | background: #34495e; 141 | border: none; 142 | color: #ecf0f1; 143 | font-size: 15px; 144 | font-family: "ABeeZee", sans-serif; 145 | } 146 | 147 | textarea:focus { 148 | outline: none; 149 | } 150 | 151 | textarea::-webkit-input-placeholder { 152 | color: #999; 153 | } 154 | 155 | textarea:-moz-input-placeholder { 156 | color: #999; 157 | font-family: "ABeeZee", sans-serif; 158 | } 159 | 160 | textarea::-moz-placeholder { 161 | color: #999; 162 | font-family: "ABeeZee", sans-serif; 163 | } 164 | 165 | textarea:-ms-input-placeholder { 166 | color: #999; 167 | } 168 | 169 | /* ===================== FILE INPUT ===================== */ 170 | 171 | .file-area { 172 | width: 100%; 173 | position: relative; 174 | } 175 | 176 | .file-area input[type=file] { 177 | position: absolute; 178 | width: 100%; 179 | height: 100%; 180 | top: 0; 181 | left: 0; 182 | right: 0; 183 | bottom: 0; 184 | opacity: 0; 185 | cursor: pointer; 186 | } 187 | 188 | .file-area .file-dummy { 189 | width: 100%; 190 | padding: 30px; 191 | background: rgba(255, 255, 255, 0.2); 192 | border: 2px dashed rgba(255, 255, 255, 0.2); 193 | text-align: center; 194 | transition: background 0.3s ease-in-out; 195 | } 196 | 197 | .file-area .file-dummy .success { 198 | display: none; 199 | } 200 | 201 | .file-area:hover .file-dummy { 202 | background: rgba(255, 255, 255, 0.1); 203 | } 204 | 205 | .file-area input[type=file]:focus+.file-dummy { 206 | outline: 2px solid rgba(255, 255, 255, 0.5); 207 | outline: -webkit-focus-ring-color auto 5px; 208 | } 209 | 210 | .file-area input[type=file]:valid+.file-dummy { 211 | border-color: rgba(0, 255, 0, 0.4); 212 | background-color: rgba(0, 255, 0, 0.3); 213 | } 214 | 215 | .file-area input[type=file]:valid+.file-dummy .success { 216 | display: inline-block; 217 | } 218 | 219 | .file-area input[type=file]:valid+.file-dummy .default { 220 | display: none; 221 | } 222 | 223 | button[type=submit] { 224 | font-size: 15px; 225 | display: block; 226 | -webkit-box-sizing: border-box; 227 | -moz-box-sizing: border-box; 228 | -ms-box-sizing: border-box; 229 | -o-box-sizing: border-box; 230 | box-sizing: border-box; 231 | border: none; 232 | -webkit-appearance: none; 233 | color: #ecf0f1; 234 | padding: 8px; 235 | background: #34495e; 236 | width: 100%; 237 | cursor: pointer; 238 | } 239 | 240 | button[type=submit]:active, 241 | button[type=submit]:focus { 242 | outline: none; 243 | } 244 | 245 | #choice { 246 | background: #00B200 247 | } 248 | 249 | /* Tag */ 250 | 251 | .tags-input-wrapper { 252 | display: inline-block; 253 | position: relative; 254 | width: 100%; 255 | background-size: contain; 256 | } 257 | 258 | .tags-input-wrapper input { 259 | border: none; 260 | background: transparent; 261 | outline: none; 262 | width: 100px; 263 | margin-left: 8px; 264 | } 265 | 266 | .tags-input-wrapper .tag { 267 | display: inline-block; 268 | background-color: #00B200; 269 | color: white; 270 | padding: 0px 3px 0px 7px; 271 | margin-right: 5px; 272 | margin-bottom: 5px; 273 | box-shadow: 0 5px 15px -2px #005900 274 | } 275 | 276 | .tags-input-wrapper .tag a { 277 | margin: 0 3px 3px; 278 | display: inline-block; 279 | cursor: pointer; 280 | } 281 | 282 | #tag-input1, 283 | #divChoiceName { 284 | display: none; 285 | } 286 | 287 | #divConfirm { 288 | padding-bottom: 50px; 289 | } 290 | 291 | *[tooltip]:focus:after { 292 | padding-top: 20px; 293 | content: attr(tooltip); 294 | display:block; 295 | position: absolute; 296 | color: #FFA500; 297 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WappBot 2 | 3 | Experimental project, which allows to detect the instant in which a new message is received and to carry out an automatic answer, with the possibility of giving personalized options. 4 | This project is based on an excellent application of an unofficial API for whatsapp, which is thanked below. 5 | https://github.com/mukulhase/WebWhatsapp-Wrapper 6 | 7 | The main objective is to present a simple implementation without the need for large installations. 8 | 9 | ## Getting Started 10 | 11 | To start it is necessary to have a registered instance of whatsapp web, and then copy the code contained in the file WappBot.js (See the configuration section for more details, otherwise the code contains default settings). 12 | Once copied and configured correctly, open your browser console F12 and paste the code. 13 | 14 | for more information go to the wiki - [WIKI](https://github.com/boehlergerman/WappBot/wiki) 15 | 16 | ### Prerequisites 17 | 18 | you must have a valid whatsapp account and access to whatsapp web 19 | 20 | 21 | ### Settings 22 | 23 | ```sh 24 | Default configuration: 25 | { 26 | useApi: false, 27 | uriApi: "https://wapp-bot.herokuapp.com/message", 28 | ignoreChat: [], 29 | ignoreGroupChat: false, 30 | messageInitial: { 31 | text: "Hello I'm WappBot send a reply \n", 32 | image: null 33 | }, 34 | messageIncorrect: "Incorrect option entered, we remind you that the options are: \n", 35 | messageOption: { 36 | "@Date": { 37 | text: new Date().toLocaleDateString(), 38 | image: null 39 | }, 40 | "@Christmas": { 41 | text: (() => { 42 | let myDate = new Date(); 43 | let cmas = Date.parse("Dec 25, " + myDate.getFullYear()) 44 | let today = Date.parse(myDate) 45 | 46 | let daysToChristmas = Math.round((cmas - today) / (1000 * 60 * 60 * 24)) 47 | if (daysToChristmas == 0) 48 | return "Today is Christmas ... Merry Christmas!" 49 | if (daysToChristmas < 0) 50 | return "Christmas was " + -1 * (daysToChristmas) + " days ago."; 51 | if (daysToChristmas > 0) 52 | return "There are " + daysToChristmas + " days to Christmas!" 53 | })(), 54 | image: null 55 | } 56 | } 57 | ``` 58 | ```sh 59 | Options: 60 | useApi [Bool] [Default False], allows you to use an API for message processing, 61 | see the DialogFlow-Nodejs-WappBot repository. 62 | 63 | uriApi [String] URL of the api you wish to consult, 64 | you can use the proposed API, it's free :) 65 | [WappBot-API](https://github.com/boehlergerman/DialogFlow-Nodejs-WappBotAPI) 66 | 67 | ignoreChat [Array] prevents repeated messages, preventing a "hello" from being sent again 68 | when an incorrect option is entered 69 | 70 | ignoreGroupChat [Bool] [Default false] prevents automatic message sending if the chat is a group 71 | 72 | messageInitial [Object] configuration of the first message to be sent to the chat 73 | | that generated an incoming message 74 | | 75 | |-> text [String | Self-Invoking Anonymous Function] Welcome text 76 | | 77 | |-> image [String] [Default Null] [Support Base64 Full Format | URL IMAGE] 78 | image to be sent together with the welcome text 79 | 80 | messageIncorrect [String] reply message in case an incorrect option is entered 81 | 82 | messageOption [Object] configuration of the response options that are enabled for the user 83 | | 84 | | 85 | |-> KeyOption [String] identifier to be shown to the user, it is convenient to use a special 86 | | character at the beginning such as @ # $, 87 | | to avoid misinterpreting the user's message. 88 | | 89 | |-> text [String | Self-Invoking Anonymous Function] 90 | | Text to display when the user enters the option properly 91 | | 92 | |-> image [String] [Default Null] [Support Base64 Full Format | URL IMAGE] 93 | image to be sent together with the text of the desired option 94 | 95 | ``` 96 | 97 | ## Important 98 | URL use is limited by Content-Security-Policy 99 | 100 | Whatsapp Web contains meta tag that avoids that from the context of the site are executed requests to the outside that are not contemplated in the header, for a security issue this header can only add more restriction and not remove it. 101 | 102 | Therefore, the use of URLAPI or IMAGE configuration in URL format and not Base64 will lead to an error in the console when you receive an incoming message. 103 | This problem can be solved by installing an extension in your browser that removes the security of CSP and thus be able to use the full potential of HTTPS and HTTP requests within the context of Whatsapp Web. 104 | 105 | [Disable CSP Extension recommend](https://bit.ly/2FFEnkT) 106 | 107 | Otherwise you should use applications such as NWJS or Electron that simulate a context above whatsapp web. 108 | 109 | ## Wappbot extension for google chrome 110 | 111 | Wappbot also exists in extension format for google chrome that allows you to use without knowing javascript, *also add the functionality to disable CSP automatically* 112 | 113 | > link to the chrome play store: [Play store](https://chrome.google.com/webstore/detail/wappbot/kfoipoajagcbedgamieppifonpbhnbkd) 114 | 115 | > link to the wiki to know how to use it: [Wiki extension chrome](https://github.com/boehlergerman/WappBot/wiki/how-to-use-it-with-extension) 116 | 117 | ![_](https://camo.githubusercontent.com/0828200a8808e85d6eb7ebdc00f5782832bb6ebf/68747470733a2f2f692e6962622e636f2f3330625132314c2f57617070426f74312e706e67) 118 | 119 | 120 | ## Examples and results 121 | ##### starting using script in console 122 | 123 | ![ ](https://media.giphy.com/media/WpUY2bTxcC5XkCeo7w/giphy.gif) 124 | 125 | ##### starting using extension chrome, Choice Settings 126 | 127 | [![_](https://i.ibb.co/XbBRNDW/2020-02-08-22-47-20-Screen-Recording-08-Feb-20-7-37-03-PM-wmv.png)](https://imgur.com/xtaK7hV) 128 | 129 | ##### starting using extension chrome, WappBot API with DialogFlow (Spanish) 130 | 131 | ![ ](https://media.giphy.com/media/gFb1rPzQ2UrIrvMB9b/giphy.gif) 132 | 133 | ```sh 134 | useApi: true, 135 | uriApi: "https://wapp-bot.herokuapp.com/message" 136 | ``` 137 | 138 | ![ ](https://media.giphy.com/media/d563lgarun9cSispf3/giphy.gif) 139 | 140 | 141 | ##### results with image configuration, remember to disable CSP 142 | 143 | ```sh 144 | messageInitial: { 145 | text: "Hello I'm WappBot send a reply \n", 146 | image: "https://i.imgur.com/4ufAcMb.png" 147 | }, 148 | messageIncorrect: "Incorrect option entered, we remind you that the options are: \n", 149 | messageOption: { 150 | "@Date": { 151 | text: new Date().toLocaleDateString(), 152 | image: null 153 | }, 154 | "@Christmas": { 155 | text: "My text", 156 | image: "https://i.imgur.com/GJXbceA.jpg" 157 | } 158 | } 159 | ``` 160 | ![ ](https://media.giphy.com/media/LoNnXqcxsvz22p64OS/giphy.gif) 161 | 162 | 163 | ## Authors 164 | 165 | * **BoehlerGerman** - *Initial work* - [BoehlerGerman](https://github.com/boehlergerman) 166 | 167 | 168 | ## License 169 | 170 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details 171 | 172 | ## Legal 173 | 174 | This code is in no way affiliated with, authorized, maintained, sponsored or endorsed by WhatsApp or any of its affiliates or subsidiaries. This is an independent and unofficial software. Use at your own risk. 175 | -------------------------------------------------------------------------------- /extension-chrome/popup/popup.js: -------------------------------------------------------------------------------- 1 | // Pure JS: 2 | "use strict"; 3 | 4 | var choice = {}; 5 | var globalTag; 6 | 7 | document.addEventListener('DOMContentLoaded', function () { 8 | loadTags(); 9 | restoreData(); 10 | document.getElementById("txtUrlAPI").style.display = "none"; 11 | document.getElementById("chkApi").addEventListener("click", () => { 12 | disable(); 13 | saveData(); 14 | showUrlAPi(); 15 | }); 16 | document.getElementById("confirm").addEventListener("click", confirm); 17 | document.getElementById("formChoice").addEventListener("submit", (e) => { 18 | e.preventDefault(); 19 | document.getElementById("choice").removeAttribute("tooltip"); 20 | 21 | const message = document.getElementById("txtMessage"); 22 | const imageUrl = document.getElementById("txtImage"); 23 | const choiceKey = document.getElementById("txtChoiceName"); 24 | 25 | 26 | 27 | for (const iterator of ["Initial", "Incorrect", "Choice1", "Choice2", "Choice3", "Choice4", "Choice5", "Choice6", "Choice7", "Choice8"]) { 28 | if (!choice[iterator]) { 29 | let element = { 30 | text: message.value, 31 | image: imageUrl.value === "" ? null : imageUrl.value, 32 | keyName: choiceKey.value === "" ? iterator : choiceKey.value 33 | } 34 | 35 | if (!globalTag) { 36 | var tagInput1 = new TagsInput({ 37 | selector: 'tag-input1', 38 | duplicate: false, 39 | max: 10 40 | }); 41 | globalTag = tagInput1.addData([iterator]) 42 | } else globalTag.addTag(iterator); 43 | 44 | if (iterator.includes("Choice")) 45 | element.text = ' \t' + element.text; 46 | else element.text += ' \n'; 47 | 48 | choice[iterator] = element; 49 | break; 50 | } 51 | else { 52 | if (choice[iterator].keyName.toUpperCase() === choiceKey.value.toUpperCase()) { 53 | document.getElementById("choice").setAttribute("tooltip", 'The choice name already exists'); 54 | break; 55 | } 56 | } 57 | } 58 | 59 | choiceSetting(); 60 | 61 | saveData(); 62 | 63 | message.value = ""; 64 | imageUrl.value = ""; 65 | choiceKey.value = ""; 66 | }); 67 | }); 68 | 69 | 70 | function choiceSetting() { 71 | if (Object.keys(choice).includes("Initial") && Object.keys(choice).includes("Incorrect")) { 72 | document.getElementById("divChoiceName").style.display = 'block'; 73 | document.getElementById("txtChoiceName").required = true; 74 | } else { 75 | document.getElementById("txtChoiceName").value = ""; 76 | document.getElementById("divChoiceName").style.display = 'none'; 77 | document.getElementById("txtChoiceName").required = false; 78 | } 79 | } 80 | 81 | function disable() { 82 | const check = document.getElementById('chkApi').checked 83 | document.getElementById("txtMessage").disabled = check; 84 | document.getElementById("txtImage").disabled = check; 85 | document.getElementById("choice").disabled = check; 86 | document.getElementById("txtChoiceName").disabled = check; 87 | } 88 | 89 | function showUrlAPi() { 90 | const chboxs = document.getElementById("chkApi"); 91 | document.getElementById("txtUrlAPI").style.display = chboxs.checked ? "block" : "none"; 92 | } 93 | 94 | function confirm() { 95 | const useApi = document.getElementById('chkApi').checked 96 | 97 | window.WappBot = { 98 | configWappBot: { 99 | useApi: useApi, 100 | uriApi: document.getElementById("txtUrlAPI").value, 101 | ignoreChat: [] 102 | } 103 | } 104 | 105 | if (useApi) { 106 | chrome.storage.local.set({ WappBot: window.WappBot }, function () { 107 | console.log('Value is set to ' + window.WappBot); 108 | }); 109 | document.getElementById("confirm").setAttribute("tooltip", 'API configuration accepted, refresh Whatsapp Web Please'); 110 | return; 111 | } 112 | 113 | if (Object.keys(choice).length <= 2 || !Object.keys(choice).includes("Initial") || !Object.keys(choice).includes("Incorrect")) { 114 | document.getElementById("confirm").setAttribute("tooltip", 'Must contain at least 3 options (initial, incorrect and one choice)'); 115 | return; 116 | } 117 | 118 | for (const key in choice) { 119 | if (choice.hasOwnProperty(key)) { 120 | const element = Object.assign({}, choice[key]); 121 | switch (key) { 122 | case "Initial": 123 | delete element["keyName"]; 124 | window.WappBot.configWappBot["messageInitial"] = element; 125 | break; 126 | case "Incorrect": 127 | window.WappBot.configWappBot["messageIncorrect"] = element.text; 128 | break; 129 | default: 130 | if (!window.WappBot.configWappBot["messageOption"]) window.WappBot.configWappBot["messageOption"] = {}; 131 | const keyName = element.keyName; delete element["keyName"]; 132 | window.WappBot.configWappBot["messageOption"][keyName] = element; 133 | break; 134 | } 135 | } 136 | } 137 | chrome.storage.local.set({ WappBot: window.WappBot }, function () { 138 | console.log('Value is set to ' + window.WappBot); 139 | }); 140 | 141 | document.getElementById("confirm").setAttribute("tooltip", 'Successfully loaded configuration, refresh Whatsapp Web Please'); 142 | } 143 | 144 | // Util data store 145 | 146 | function saveData() { 147 | chrome.storage.local.set({ StorePopup: { "choice": choice, "useApi": document.getElementById('chkApi').checked } }, function () { 148 | console.log('Value is set to ' + JSON.stringify(choice)); 149 | }); 150 | } 151 | 152 | function restoreData() { 153 | chrome.storage.local.get(['StorePopup'], function (result) { 154 | if (!!result.StorePopup) { 155 | console.log(result); 156 | choice = result.StorePopup.choice; 157 | document.getElementById('chkApi').checked = result.StorePopup.useApi; 158 | disable(); 159 | const keys = Object.keys(choice); 160 | if (keys.length > 0) { 161 | var tagInput1 = new TagsInput({ 162 | selector: 'tag-input1', 163 | duplicate: false, 164 | max: 10 165 | }); 166 | globalTag = tagInput1.addData(keys); 167 | choiceSetting(); 168 | } 169 | } 170 | }); 171 | 172 | } 173 | 174 | 175 | // Tags 176 | 177 | 178 | function loadTags() { 179 | var TagsInput = function TagsInput(opts) { 180 | this.options = Object.assign(TagsInput.defaults, opts); 181 | this.orignal_input = document.getElementById(opts.selector); 182 | this.arr = []; 183 | this.wrapper = document.createElement('div'); 184 | // this.wrapper = document.getElementsByClassName("tags-input-wrapper")[0]; 185 | this.input = document.createElement('input'); 186 | buildUI(this); 187 | addEvents(this); 188 | }; 189 | 190 | TagsInput.prototype.addTag = function (string) { 191 | if (this.anyErrors(string)) return; 192 | this.arr.push(string); 193 | var tagInput = this; 194 | var tag = document.createElement('span'); 195 | tag.className = this.options.tagClass; 196 | tag.innerText = string; 197 | var closeIcon = document.createElement('a'); 198 | closeIcon.innerHTML = '×'; 199 | closeIcon.addEventListener('click', function (e) { 200 | e.preventDefault(); 201 | var tag = this.parentNode; 202 | 203 | for (var i = 0; i < tagInput.wrapper.childNodes.length; i++) { 204 | if (tagInput.wrapper.childNodes[i] == tag) tagInput.deleteTag(tag, i); 205 | } 206 | }); 207 | tag.appendChild(closeIcon); 208 | this.wrapper.insertBefore(tag, this.input); 209 | this.orignal_input.value = this.arr.join(','); 210 | return this; 211 | }; 212 | 213 | TagsInput.prototype.deleteTag = function (tag, i) { 214 | tag.remove(); 215 | const key = this.arr[i]; 216 | delete choice[key]; 217 | this.arr.splice(i, 1); 218 | choiceSetting(); 219 | saveData(); 220 | this.orignal_input.value = this.arr.join(','); 221 | return this; 222 | }; 223 | 224 | TagsInput.prototype.anyErrors = function (string) { 225 | if (this.options.max != null && this.arr.length >= this.options.max) { 226 | console.log('max tags limit reached'); 227 | return true; 228 | } 229 | 230 | if (!this.options.duplicate && this.arr.indexOf(string) != -1) { 231 | console.log('duplicate found " ' + string + ' " '); 232 | return true; 233 | } 234 | 235 | return false; 236 | }; 237 | 238 | TagsInput.prototype.addData = function (array) { 239 | var plugin = this; 240 | array.forEach(function (string) { 241 | plugin.addTag(string); 242 | }); 243 | return this; 244 | }; 245 | 246 | TagsInput.prototype.getInputString = function () { 247 | return this.arr.join(','); 248 | }; // Private function to initialize the UI Elements 249 | 250 | 251 | function buildUI(tags) { 252 | tags.wrapper.append(tags.input); 253 | tags.wrapper.classList.add(tags.options.wrapperClass); 254 | // document.getElementById(tags.orignal_input).style.display = 'none'; 255 | tags.orignal_input.style.display = 'none'; 256 | tags.orignal_input.parentNode.insertBefore(tags.wrapper, tags.orignal_input); 257 | } 258 | 259 | function addEvents(tags) { 260 | tags.wrapper.addEventListener('click', function () { 261 | tags.input.focus(); 262 | }); 263 | tags.input.addEventListener('keydown', function (e) { 264 | var str = tags.input.value.trim(); 265 | 266 | if (!!~[9, 13, 188].indexOf(e.keyCode)) { 267 | tags.input.value = ""; 268 | if (str != "") tags.addTag(str); 269 | } 270 | }); 271 | } 272 | 273 | TagsInput.defaults = { 274 | selector: '', 275 | wrapperClass: 'tags-input-wrapper', 276 | tagClass: 'tag', 277 | max: null, 278 | duplicate: false 279 | }; 280 | window.TagsInput = TagsInput; 281 | } -------------------------------------------------------------------------------- /wappbot.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | window.WappBot = { 4 | configWappBot: { 5 | useApi: false, 6 | uriApi: "https://wapp-bot.herokuapp.com/message", 7 | ignoreChat: [], 8 | ignoreGroupChat: false, 9 | messageInitial: { 10 | text: "Hello I'm WappBot send a reply \n", 11 | image: null, 12 | }, 13 | messageIncorrect: "Incorrect option entered, we remind you that the options are: \n", 14 | messageOption: { 15 | "@Date": { 16 | text: new Date().toLocaleDateString(), 17 | image: null, 18 | }, 19 | "@Christmas": { 20 | text: (() => { 21 | let myDate = new Date(); 22 | let cmas = Date.parse("Dec 25, " + myDate.getFullYear()); 23 | let today = Date.parse(myDate); 24 | 25 | let daysToChristmas = Math.round((cmas - today) / (1000 * 60 * 60 * 24)); 26 | if (daysToChristmas == 0) return "Today is Christmas ... Merry Christmas!"; 27 | if (daysToChristmas < 0) return "Christmas was " + -1 * daysToChristmas + " days ago."; 28 | if (daysToChristmas > 0) return "There are " + daysToChristmas + " days to Christmas!"; 29 | })(), 30 | image: null, 31 | }, 32 | }, 33 | }, 34 | }; 35 | 36 | /* eslint-disable */ 37 | /** 38 | * This script contains WAPI functions that need to be run in the context of the webpage 39 | */ 40 | 41 | /** 42 | * Auto discovery the webpack object references of instances that contains all functions used by the WAPI 43 | * functions and creates the Store object. 44 | */ 45 | if (!window["webpackJsonp"]) { 46 | window.webpackJsonp = webpackJsonp; 47 | } 48 | 49 | if (!window.Store) { 50 | (function () { 51 | function getStore(modules) { 52 | let foundCount = 0; 53 | let neededObjects = [ 54 | { 55 | id: "Store", 56 | conditions: (module) => (module.default && module.default.Chat && module.default.Msg ? module.default : null), 57 | }, 58 | { 59 | id: "MediaCollection", 60 | conditions: (module) => 61 | module.default && module.default.prototype && module.default.prototype.processAttachments 62 | ? module.default 63 | : null, 64 | }, 65 | { id: "MediaProcess", conditions: (module) => (module.BLOB ? module : null) }, 66 | { id: "Wap", conditions: (module) => (module.createGroup ? module : null) }, 67 | { 68 | id: "ServiceWorker", 69 | conditions: (module) => (module.default && module.default.killServiceWorker ? module : null), 70 | }, 71 | { id: "State", conditions: (module) => (module.STATE && module.STREAM ? module : null) }, 72 | { 73 | id: "WapDelete", 74 | conditions: (module) => 75 | module.sendConversationDelete && module.sendConversationDelete.length == 2 ? module : null, 76 | }, 77 | { 78 | id: "Conn", 79 | conditions: (module) => 80 | module.default && module.default.ref && module.default.refTTL ? module.default : null, 81 | }, 82 | { 83 | id: "WapQuery", 84 | conditions: (module) => 85 | module.queryExist ? module : module.default && module.default.queryExist ? module.default : null, 86 | }, 87 | { id: "CryptoLib", conditions: (module) => (module.decryptE2EMedia ? module : null) }, 88 | { 89 | id: "OpenChat", 90 | conditions: (module) => 91 | module.default && module.default.prototype && module.default.prototype.openChat ? module.default : null, 92 | }, 93 | { 94 | id: "UserConstructor", 95 | conditions: (module) => 96 | module.default && 97 | module.default.prototype && 98 | module.default.prototype.isServer && 99 | module.default.prototype.isUser 100 | ? module.default 101 | : null, 102 | }, 103 | { 104 | id: "SendTextMsgToChat", 105 | conditions: (module) => (module.sendTextMsgToChat ? module.sendTextMsgToChat : null), 106 | }, 107 | { id: "SendSeen", conditions: (module) => (module.sendSeen ? module.sendSeen : null) }, 108 | { id: "sendDelete", conditions: (module) => (module.sendDelete ? module.sendDelete : null) }, 109 | ]; 110 | for (let idx in modules) { 111 | if (typeof modules[idx] === "object" && modules[idx] !== null) { 112 | let first = Object.values(modules[idx])[0]; 113 | if (typeof first === "object" && first.exports) { 114 | for (let idx2 in modules[idx]) { 115 | let module = modules(idx2); 116 | if (!module) { 117 | continue; 118 | } 119 | neededObjects.forEach((needObj) => { 120 | if (!needObj.conditions || needObj.foundedModule) return; 121 | let neededModule = needObj.conditions(module); 122 | if (neededModule !== null) { 123 | foundCount++; 124 | needObj.foundedModule = neededModule; 125 | } 126 | }); 127 | if (foundCount == neededObjects.length) { 128 | break; 129 | } 130 | } 131 | 132 | let neededStore = neededObjects.find((needObj) => needObj.id === "Store"); 133 | window.Store = neededStore.foundedModule ? neededStore.foundedModule : {}; 134 | neededObjects.splice(neededObjects.indexOf(neededStore), 1); 135 | neededObjects.forEach((needObj) => { 136 | if (needObj.foundedModule) { 137 | window.Store[needObj.id] = needObj.foundedModule; 138 | } 139 | }); 140 | window.Store.sendMessage = function (e) { 141 | return window.Store.SendTextMsgToChat(this, ...arguments); 142 | }; 143 | return window.Store; 144 | } 145 | } 146 | } 147 | } 148 | 149 | //webpackJsonp([], { 'parasite': (x, y, z) => getStore(z) }, ['parasite']); 150 | /* 151 | Code update 152 | */ 153 | if (typeof webpackJsonp === "function") { 154 | webpackJsonp([], { parasite: (x, y, z) => getStore(z) }, ["parasite"]); 155 | } else { 156 | webpackJsonp.push([ 157 | ["parasite"], 158 | { 159 | parasite: function (o, e, t) { 160 | getStore(t); 161 | }, 162 | }, 163 | [["parasite"]], 164 | ]); 165 | } 166 | })(); 167 | } 168 | 169 | window.WAPI = { 170 | lastRead: {}, 171 | }; 172 | 173 | window.WAPI._serializeRawObj = (obj) => { 174 | if (obj) { 175 | return obj.toJSON(); 176 | } 177 | return {}; 178 | }; 179 | 180 | /** 181 | * Serializes a chat object 182 | * 183 | * @param rawChat Chat object 184 | * @returns {{}} 185 | */ 186 | 187 | window.WAPI._serializeChatObj = (obj) => { 188 | if (obj == undefined) { 189 | return null; 190 | } 191 | 192 | return Object.assign(window.WAPI._serializeRawObj(obj), { 193 | kind: obj.kind, 194 | isGroup: obj.isGroup, 195 | contact: obj["contact"] ? window.WAPI._serializeContactObj(obj["contact"]) : null, 196 | groupMetadata: obj["groupMetadata"] ? window.WAPI._serializeRawObj(obj["groupMetadata"]) : null, 197 | presence: obj["presence"] ? window.WAPI._serializeRawObj(obj["presence"]) : null, 198 | msgs: null, 199 | }); 200 | }; 201 | 202 | window.WAPI._serializeContactObj = (obj) => { 203 | if (obj == undefined) { 204 | return null; 205 | } 206 | 207 | return Object.assign(window.WAPI._serializeRawObj(obj), { 208 | formattedName: obj.formattedName, 209 | isHighLevelVerified: obj.isHighLevelVerified, 210 | isMe: obj.isMe, 211 | isMyContact: obj.isMyContact, 212 | isPSA: obj.isPSA, 213 | isUser: obj.isUser, 214 | isVerified: obj.isVerified, 215 | isWAContact: obj.isWAContact, 216 | profilePicThumbObj: obj.profilePicThumb ? WAPI._serializeProfilePicThumb(obj.profilePicThumb) : {}, 217 | statusMute: obj.statusMute, 218 | msgs: null, 219 | }); 220 | }; 221 | 222 | window.WAPI._serializeMessageObj = (obj) => { 223 | if (obj == undefined) { 224 | return null; 225 | } 226 | 227 | return Object.assign(window.WAPI._serializeRawObj(obj), { 228 | id: obj.id._serialized, 229 | sender: obj["senderObj"] ? WAPI._serializeContactObj(obj["senderObj"]) : null, 230 | timestamp: obj["t"], 231 | content: obj["body"], 232 | isGroupMsg: obj.isGroupMsg, 233 | isLink: obj.isLink, 234 | isMMS: obj.isMMS, 235 | isMedia: obj.isMedia, 236 | isNotification: obj.isNotification, 237 | isPSA: obj.isPSA, 238 | type: obj.type, 239 | chat: WAPI._serializeChatObj(obj["chat"]), 240 | chatId: obj.id.remote, 241 | quotedMsgObj: WAPI._serializeMessageObj(obj["_quotedMsgObj"]), 242 | mediaData: window.WAPI._serializeRawObj(obj["mediaData"]), 243 | }); 244 | }; 245 | 246 | window.WAPI._serializeNumberStatusObj = (obj) => { 247 | if (obj == undefined) { 248 | return null; 249 | } 250 | 251 | return Object.assign( 252 | {}, 253 | { 254 | id: obj.jid, 255 | status: obj.status, 256 | isBusiness: obj.biz === true, 257 | canReceiveMessage: obj.status === 200, 258 | } 259 | ); 260 | }; 261 | 262 | window.WAPI._serializeProfilePicThumb = (obj) => { 263 | if (obj == undefined) { 264 | return null; 265 | } 266 | 267 | return Object.assign( 268 | {}, 269 | { 270 | eurl: obj.eurl, 271 | id: obj.id, 272 | img: obj.img, 273 | imgFull: obj.imgFull, 274 | raw: obj.raw, 275 | tag: obj.tag, 276 | } 277 | ); 278 | }; 279 | 280 | /** 281 | * Fetches chat object from store by ID 282 | * 283 | * @param id ID of chat 284 | * @returns {T|*} Chat object 285 | */ 286 | window.WAPI.getChat = function (id) { 287 | id = typeof id == "string" ? id : id._serialized; 288 | const found = window.Store.Chat.get(id); 289 | found.sendMessage = found.sendMessage 290 | ? found.sendMessage 291 | : function () { 292 | return window.Store.sendMessage.apply(this, arguments); 293 | }; 294 | return found; 295 | }; 296 | 297 | /** 298 | * Fetches all chat IDs from store 299 | * 300 | * @returns {Array|*} List of chat id's 301 | */ 302 | window.WAPI.getAllChatIds = function () { 303 | const chatIds = window.Store.Chat.map((chat) => chat.id._serialized || chat.id); 304 | return chatIds; 305 | }; 306 | 307 | window.WAPI.processMessageObj = function (messageObj, includeMe, includeNotifications) { 308 | if (messageObj.isNotification) { 309 | if (includeNotifications) return WAPI._serializeMessageObj(messageObj); 310 | else return; 311 | // System message 312 | // (i.e. "Messages you send to this chat and calls are now secured with end-to-end encryption...") 313 | } else if (messageObj.id.fromMe === false || includeMe) { 314 | return WAPI._serializeMessageObj(messageObj); 315 | } 316 | return; 317 | }; 318 | 319 | window.WAPI.sendImage = async function (imgBase64, chatid, filename, caption) { 320 | let id = chatid; 321 | if (!window.WAPI.getAllChatIds().find((chat) => chat == chatid)) 322 | id = new window.Store.UserConstructor(chatid, { intentionallyUsePrivateConstructor: true }); 323 | var chat = WAPI.getChat(id); 324 | // Ignore Group Chat 325 | if (WAPI._serializeChatObj(chat).isGroup && window.WappBot.configWappBot.ignoreGroupChat) return; 326 | // create new chat 327 | try { 328 | var mediaBlob = await window.WAPI.base64ImageToFile(imgBase64, filename); 329 | var mc = new Store.MediaCollection(chat); 330 | mc.processFiles([mediaBlob], chat, 1).then(() => { 331 | var media = mc.models[0]; 332 | media.sendToChat(chat, { caption: caption }); 333 | }); 334 | } catch (error) { 335 | if (window.Store.Chat.length === 0) return false; 336 | 337 | let firstChat = Store.Chat.models[0]; 338 | let originalID = firstChat.id; 339 | firstChat.id = 340 | typeof originalID === "string" 341 | ? id 342 | : new window.Store.UserConstructor(id, { intentionallyUsePrivateConstructor: true }); 343 | let mediaBlob = await window.WAPI.base64ImageToFile(imgBase64, filename); 344 | var mc = new Store.MediaCollection(chat); 345 | chat = WAPI.getChat(id); 346 | mc.processAttachments([{ file: mediaBlob }, 1], chat, 1).then(() => { 347 | let media = mc.models[0]; 348 | media.sendToChat(chat, { caption: caption }); 349 | }); 350 | return true; 351 | } 352 | }; 353 | 354 | window.WAPI.base64ImageToFile = function (image, filename) { 355 | return new Promise(async (resolve) => { 356 | if (!image.includes("base64")) { 357 | image = await window.WappBot.toDataURL("https://cors-anywhere.herokuapp.com/" + image); // convert url in base64 358 | } 359 | var arr = image.split(","), 360 | mime = arr[0].match(/:(.*?);/)[1], 361 | bstr = atob(arr[1]), 362 | n = bstr.length, 363 | u8arr = new Uint8Array(n); 364 | while (n--) { 365 | u8arr[n] = bstr.charCodeAt(n); 366 | } 367 | resolve(new File([u8arr], filename, { type: mime })); 368 | }); 369 | }; 370 | 371 | window.WAPI.sendMessage = function (idChat, message) { 372 | let id = idChat; 373 | if (!window.WAPI.getAllChatIds().find((chat) => chat == idChat)) 374 | id = new window.Store.UserConstructor(idChat, { intentionallyUsePrivateConstructor: true }); 375 | var chat = WAPI.getChat(id); 376 | if (WAPI._serializeChatObj(chat).isGroup && window.WappBot.configWappBot.ignoreGroupChat) return; 377 | try { 378 | // create new chat 379 | return chat.sendMessage(message); 380 | } catch (e) { 381 | if (window.Store.Chat.length === 0) return false; 382 | 383 | firstChat = Store.Chat.models[0]; 384 | var originalID = firstChat.id; 385 | firstChat.id = 386 | typeof originalID === "string" 387 | ? id 388 | : new window.Store.UserConstructor(id, { intentionallyUsePrivateConstructor: true }); 389 | var chat = WAPI.getChat(firstChat.id); 390 | chat.sendMessage(message); 391 | return true; 392 | } 393 | }; 394 | 395 | window.WappBot.toDataURL = (url) => { 396 | return new Promise((resolve) => { 397 | var xhr = new XMLHttpRequest(); 398 | xhr.onload = function () { 399 | var reader = new FileReader(); 400 | reader.onloadend = function () { 401 | resolve(reader.result); 402 | }; 403 | reader.readAsDataURL(xhr.response); 404 | }; 405 | xhr.open("GET", url); 406 | xhr.responseType = "blob"; 407 | xhr.send(); 408 | }); 409 | }; 410 | 411 | window.WappBot.sendByAPIWappBot = (newMessage, chatId) => { 412 | fetch(window.WappBot.configWappBot.uriApi, { 413 | method: "POST", 414 | headers: { 415 | Accept: "application/json", 416 | "Content-Type": "application/json", 417 | }, 418 | body: JSON.stringify({ messageText: newMessage }), 419 | }).then(function (response) { 420 | response.json().then((post) => { 421 | if (!post && !post.messageResponse) return; 422 | window.WAPI.sendMessage(chatId, post.messageResponse); 423 | }); 424 | }); 425 | }; 426 | 427 | window.WappBot.messageIncludeKey = (message, options) => { 428 | for (let i = 0; i < options.length; i++) { 429 | if (message.toUpperCase().includes(options[i].toUpperCase())) 430 | return window.WappBot.configWappBot.messageOption[options[i]]; 431 | } 432 | return false; 433 | }; 434 | 435 | window.WappBot.prepareMessageToSend = (chatId, options) => { 436 | let message = ""; 437 | if (window.WappBot.configWappBot.ignoreChat.indexOf(chatId) > -1) { 438 | message = `${window.WappBot.configWappBot.messageIncorrect}`; 439 | } else { 440 | message = `${window.WappBot.configWappBot.messageInitial.text}`; 441 | window.WappBot.configWappBot.ignoreChat.push(chatId); 442 | } 443 | for (let i = 0; i < options.length; i++) message += `\t ${options[i]} \n`; 444 | 445 | return message; 446 | }; 447 | 448 | window.WappBot.sendByLocalSetting = (newMessage, chatId) => { 449 | const options = Object.keys(window.WappBot.configWappBot.messageOption); 450 | const messageIncludeKey = window.WappBot.messageIncludeKey(newMessage, options); 451 | if (!messageIncludeKey) { 452 | const message = window.WappBot.prepareMessageToSend(chatId, options); 453 | if (!window.WappBot.configWappBot.messageInitial.image) window.WAPI.sendMessage(chatId, message); 454 | else window.WAPI.sendImage(window.WappBot.configWappBot.messageInitial.image, chatId, "image", message); 455 | } else { 456 | if (!messageIncludeKey.image) window.WAPI.sendMessage(chatId, messageIncludeKey.text); 457 | else window.WAPI.sendImage(messageIncludeKey.image, chatId, "image", messageIncludeKey.text); 458 | } 459 | }; 460 | 461 | /** 462 | * New messages observable functions. 463 | */ 464 | window.WAPI._newMessagesQueue = []; 465 | window.WAPI._newMessagesBuffer = 466 | sessionStorage.getItem("saved_msgs") != null ? JSON.parse(sessionStorage.getItem("saved_msgs")) : []; 467 | window.WAPI._newMessagesDebouncer = null; 468 | window.WAPI._newMessagesCallbacks = []; 469 | 470 | window.Store.Msg.off("add"); 471 | sessionStorage.removeItem("saved_msgs"); 472 | 473 | window.WAPI._newMessagesListener = window.Store.Msg.on("add", (newMessage) => { 474 | if (newMessage && newMessage.isNewMsg && !newMessage.isSentByMe) { 475 | let message = window.WAPI.processMessageObj(newMessage, false, false); 476 | if (message) { 477 | if (window.WappBot.configWappBot.useApi) 478 | window.WappBot.sendByAPIWappBot(message.body, message.chatId._serialized); 479 | else window.WappBot.sendByLocalSetting(message.body, message.chatId._serialized); 480 | } 481 | } 482 | }); 483 | 484 | window.WAPI._unloadInform = (event) => { 485 | // Save in the buffer the ungot unreaded messages 486 | window.WAPI._newMessagesBuffer.forEach((message) => { 487 | Object.keys(message).forEach((key) => (message[key] === undefined ? delete message[key] : "")); 488 | }); 489 | sessionStorage.setItem("saved_msgs", JSON.stringify(window.WAPI._newMessagesBuffer)); 490 | 491 | // Inform callbacks that the page will be reloaded. 492 | window.WAPI._newMessagesCallbacks.forEach(function (callbackObj) { 493 | if (callbackObj.callback !== undefined) { 494 | callbackObj.callback({ status: -1, message: "page will be reloaded, wait and register callback again." }); 495 | } 496 | }); 497 | }; 498 | 499 | window.addEventListener("unload", window.WAPI._unloadInform, false); 500 | window.addEventListener("beforeunload", window.WAPI._unloadInform, false); 501 | window.addEventListener("pageunload", window.WAPI._unloadInform, false); 502 | -------------------------------------------------------------------------------- /extension-chrome/script/wappbot.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | window.WappBot = { 4 | configWappBot: { 5 | useApi: false, 6 | uriApi: "https://wapp-bot.herokuapp.com/message", 7 | ignoreChat: [], 8 | ignoreGroupChat: false, 9 | messageInitial: { 10 | text: "Hello I'm WappBot send a reply \n", 11 | image: null, 12 | }, 13 | messageIncorrect: "Incorrect option entered, we remind you that the options are: \n", 14 | messageOption: { 15 | "@Date": { 16 | text: new Date().toLocaleDateString(), 17 | image: null, 18 | }, 19 | "@Christmas": { 20 | text: (() => { 21 | let myDate = new Date(); 22 | let cmas = Date.parse("Dec 25, " + myDate.getFullYear()); 23 | let today = Date.parse(myDate); 24 | 25 | let daysToChristmas = Math.round((cmas - today) / (1000 * 60 * 60 * 24)); 26 | if (daysToChristmas == 0) return "Today is Christmas ... Merry Christmas!"; 27 | if (daysToChristmas < 0) return "Christmas was " + -1 * daysToChristmas + " days ago."; 28 | if (daysToChristmas > 0) return "There are " + daysToChristmas + " days to Christmas!"; 29 | })(), 30 | image: null, 31 | }, 32 | }, 33 | }, 34 | }; 35 | 36 | /* eslint-disable */ 37 | /** 38 | * This script contains WAPI functions that need to be run in the context of the webpage 39 | */ 40 | 41 | /** 42 | * Auto discovery the webpack object references of instances that contains all functions used by the WAPI 43 | * functions and creates the Store object. 44 | */ 45 | if (!window["webpackJsonp"]) { 46 | window.webpackJsonp = webpackJsonp; 47 | } 48 | 49 | if (!window.Store) { 50 | (function () { 51 | function getStore(modules) { 52 | let foundCount = 0; 53 | let neededObjects = [ 54 | { 55 | id: "Store", 56 | conditions: (module) => (module.default && module.default.Chat && module.default.Msg ? module.default : null), 57 | }, 58 | { 59 | id: "MediaCollection", 60 | conditions: (module) => 61 | module.default && module.default.prototype && module.default.prototype.processAttachments 62 | ? module.default 63 | : null, 64 | }, 65 | { id: "MediaProcess", conditions: (module) => (module.BLOB ? module : null) }, 66 | { id: "Wap", conditions: (module) => (module.createGroup ? module : null) }, 67 | { 68 | id: "ServiceWorker", 69 | conditions: (module) => (module.default && module.default.killServiceWorker ? module : null), 70 | }, 71 | { id: "State", conditions: (module) => (module.STATE && module.STREAM ? module : null) }, 72 | { 73 | id: "WapDelete", 74 | conditions: (module) => 75 | module.sendConversationDelete && module.sendConversationDelete.length == 2 ? module : null, 76 | }, 77 | { 78 | id: "Conn", 79 | conditions: (module) => 80 | module.default && module.default.ref && module.default.refTTL ? module.default : null, 81 | }, 82 | { 83 | id: "WapQuery", 84 | conditions: (module) => 85 | module.queryExist ? module : module.default && module.default.queryExist ? module.default : null, 86 | }, 87 | { id: "CryptoLib", conditions: (module) => (module.decryptE2EMedia ? module : null) }, 88 | { 89 | id: "OpenChat", 90 | conditions: (module) => 91 | module.default && module.default.prototype && module.default.prototype.openChat ? module.default : null, 92 | }, 93 | { 94 | id: "UserConstructor", 95 | conditions: (module) => 96 | module.default && 97 | module.default.prototype && 98 | module.default.prototype.isServer && 99 | module.default.prototype.isUser 100 | ? module.default 101 | : null, 102 | }, 103 | { 104 | id: "SendTextMsgToChat", 105 | conditions: (module) => (module.sendTextMsgToChat ? module.sendTextMsgToChat : null), 106 | }, 107 | { id: "SendSeen", conditions: (module) => (module.sendSeen ? module.sendSeen : null) }, 108 | { id: "sendDelete", conditions: (module) => (module.sendDelete ? module.sendDelete : null) }, 109 | ]; 110 | for (let idx in modules) { 111 | if (typeof modules[idx] === "object" && modules[idx] !== null) { 112 | let first = Object.values(modules[idx])[0]; 113 | if (typeof first === "object" && first.exports) { 114 | for (let idx2 in modules[idx]) { 115 | let module = modules(idx2); 116 | if (!module) { 117 | continue; 118 | } 119 | neededObjects.forEach((needObj) => { 120 | if (!needObj.conditions || needObj.foundedModule) return; 121 | let neededModule = needObj.conditions(module); 122 | if (neededModule !== null) { 123 | foundCount++; 124 | needObj.foundedModule = neededModule; 125 | } 126 | }); 127 | if (foundCount == neededObjects.length) { 128 | break; 129 | } 130 | } 131 | 132 | let neededStore = neededObjects.find((needObj) => needObj.id === "Store"); 133 | window.Store = neededStore.foundedModule ? neededStore.foundedModule : {}; 134 | neededObjects.splice(neededObjects.indexOf(neededStore), 1); 135 | neededObjects.forEach((needObj) => { 136 | if (needObj.foundedModule) { 137 | window.Store[needObj.id] = needObj.foundedModule; 138 | } 139 | }); 140 | window.Store.sendMessage = function (e) { 141 | return window.Store.SendTextMsgToChat(this, ...arguments); 142 | }; 143 | return window.Store; 144 | } 145 | } 146 | } 147 | } 148 | 149 | //webpackJsonp([], { 'parasite': (x, y, z) => getStore(z) }, ['parasite']); 150 | /* 151 | Code update 152 | */ 153 | if (typeof webpackJsonp === "function") { 154 | webpackJsonp([], { parasite: (x, y, z) => getStore(z) }, ["parasite"]); 155 | } else { 156 | webpackJsonp.push([ 157 | ["parasite"], 158 | { 159 | parasite: function (o, e, t) { 160 | getStore(t); 161 | }, 162 | }, 163 | [["parasite"]], 164 | ]); 165 | } 166 | })(); 167 | } 168 | 169 | window.WAPI = { 170 | lastRead: {}, 171 | }; 172 | 173 | window.WAPI._serializeRawObj = (obj) => { 174 | if (obj) { 175 | return obj.toJSON(); 176 | } 177 | return {}; 178 | }; 179 | 180 | /** 181 | * Serializes a chat object 182 | * 183 | * @param rawChat Chat object 184 | * @returns {{}} 185 | */ 186 | 187 | window.WAPI._serializeChatObj = (obj) => { 188 | if (obj == undefined) { 189 | return null; 190 | } 191 | 192 | return Object.assign(window.WAPI._serializeRawObj(obj), { 193 | kind: obj.kind, 194 | isGroup: obj.isGroup, 195 | contact: obj["contact"] ? window.WAPI._serializeContactObj(obj["contact"]) : null, 196 | groupMetadata: obj["groupMetadata"] ? window.WAPI._serializeRawObj(obj["groupMetadata"]) : null, 197 | presence: obj["presence"] ? window.WAPI._serializeRawObj(obj["presence"]) : null, 198 | msgs: null, 199 | }); 200 | }; 201 | 202 | window.WAPI._serializeContactObj = (obj) => { 203 | if (obj == undefined) { 204 | return null; 205 | } 206 | 207 | return Object.assign(window.WAPI._serializeRawObj(obj), { 208 | formattedName: obj.formattedName, 209 | isHighLevelVerified: obj.isHighLevelVerified, 210 | isMe: obj.isMe, 211 | isMyContact: obj.isMyContact, 212 | isPSA: obj.isPSA, 213 | isUser: obj.isUser, 214 | isVerified: obj.isVerified, 215 | isWAContact: obj.isWAContact, 216 | profilePicThumbObj: obj.profilePicThumb ? WAPI._serializeProfilePicThumb(obj.profilePicThumb) : {}, 217 | statusMute: obj.statusMute, 218 | msgs: null, 219 | }); 220 | }; 221 | 222 | window.WAPI._serializeMessageObj = (obj) => { 223 | if (obj == undefined) { 224 | return null; 225 | } 226 | 227 | return Object.assign(window.WAPI._serializeRawObj(obj), { 228 | id: obj.id._serialized, 229 | sender: obj["senderObj"] ? WAPI._serializeContactObj(obj["senderObj"]) : null, 230 | timestamp: obj["t"], 231 | content: obj["body"], 232 | isGroupMsg: obj.isGroupMsg, 233 | isLink: obj.isLink, 234 | isMMS: obj.isMMS, 235 | isMedia: obj.isMedia, 236 | isNotification: obj.isNotification, 237 | isPSA: obj.isPSA, 238 | type: obj.type, 239 | chat: WAPI._serializeChatObj(obj["chat"]), 240 | chatId: obj.id.remote, 241 | quotedMsgObj: WAPI._serializeMessageObj(obj["_quotedMsgObj"]), 242 | mediaData: window.WAPI._serializeRawObj(obj["mediaData"]), 243 | }); 244 | }; 245 | 246 | window.WAPI._serializeNumberStatusObj = (obj) => { 247 | if (obj == undefined) { 248 | return null; 249 | } 250 | 251 | return Object.assign( 252 | {}, 253 | { 254 | id: obj.jid, 255 | status: obj.status, 256 | isBusiness: obj.biz === true, 257 | canReceiveMessage: obj.status === 200, 258 | } 259 | ); 260 | }; 261 | 262 | window.WAPI._serializeProfilePicThumb = (obj) => { 263 | if (obj == undefined) { 264 | return null; 265 | } 266 | 267 | return Object.assign( 268 | {}, 269 | { 270 | eurl: obj.eurl, 271 | id: obj.id, 272 | img: obj.img, 273 | imgFull: obj.imgFull, 274 | raw: obj.raw, 275 | tag: obj.tag, 276 | } 277 | ); 278 | }; 279 | 280 | /** 281 | * Fetches chat object from store by ID 282 | * 283 | * @param id ID of chat 284 | * @returns {T|*} Chat object 285 | */ 286 | window.WAPI.getChat = function (id) { 287 | id = typeof id == "string" ? id : id._serialized; 288 | const found = window.Store.Chat.get(id); 289 | found.sendMessage = found.sendMessage 290 | ? found.sendMessage 291 | : function () { 292 | return window.Store.sendMessage.apply(this, arguments); 293 | }; 294 | return found; 295 | }; 296 | 297 | /** 298 | * Fetches all chat IDs from store 299 | * 300 | * @returns {Array|*} List of chat id's 301 | */ 302 | window.WAPI.getAllChatIds = function () { 303 | const chatIds = window.Store.Chat.map((chat) => chat.id._serialized || chat.id); 304 | return chatIds; 305 | }; 306 | 307 | window.WAPI.processMessageObj = function (messageObj, includeMe, includeNotifications) { 308 | if (messageObj.isNotification) { 309 | if (includeNotifications) return WAPI._serializeMessageObj(messageObj); 310 | else return; 311 | // System message 312 | // (i.e. "Messages you send to this chat and calls are now secured with end-to-end encryption...") 313 | } else if (messageObj.id.fromMe === false || includeMe) { 314 | return WAPI._serializeMessageObj(messageObj); 315 | } 316 | return; 317 | }; 318 | 319 | window.WAPI.sendImage = async function (imgBase64, chatid, filename, caption) { 320 | let id = chatid; 321 | if (!window.WAPI.getAllChatIds().find((chat) => chat == chatid)) 322 | id = new window.Store.UserConstructor(chatid, { intentionallyUsePrivateConstructor: true }); 323 | var chat = WAPI.getChat(id); 324 | // Ignore Group Chat 325 | if (WAPI._serializeChatObj(chat).isGroup && window.WappBot.configWappBot.ignoreGroupChat) return; 326 | // create new chat 327 | try { 328 | var mediaBlob = await window.WAPI.base64ImageToFile(imgBase64, filename); 329 | var mc = new Store.MediaCollection(chat); 330 | mc.processFiles([mediaBlob], chat, 1).then(() => { 331 | var media = mc.models[0]; 332 | media.sendToChat(chat, { caption: caption }); 333 | }); 334 | } catch (error) { 335 | if (window.Store.Chat.length === 0) return false; 336 | 337 | let firstChat = Store.Chat.models[0]; 338 | let originalID = firstChat.id; 339 | firstChat.id = 340 | typeof originalID === "string" 341 | ? id 342 | : new window.Store.UserConstructor(id, { intentionallyUsePrivateConstructor: true }); 343 | let mediaBlob = await window.WAPI.base64ImageToFile(imgBase64, filename); 344 | var mc = new Store.MediaCollection(chat); 345 | chat = WAPI.getChat(id); 346 | mc.processAttachments([{ file: mediaBlob }, 1], chat, 1).then(() => { 347 | let media = mc.models[0]; 348 | media.sendToChat(chat, { caption: caption }); 349 | }); 350 | return true; 351 | } 352 | }; 353 | 354 | window.WAPI.base64ImageToFile = function (image, filename) { 355 | return new Promise(async (resolve) => { 356 | if (!image.includes("base64")) { 357 | image = await window.WappBot.toDataURL("https://cors-anywhere.herokuapp.com/" + image); // convert url in base64 358 | } 359 | var arr = image.split(","), 360 | mime = arr[0].match(/:(.*?);/)[1], 361 | bstr = atob(arr[1]), 362 | n = bstr.length, 363 | u8arr = new Uint8Array(n); 364 | while (n--) { 365 | u8arr[n] = bstr.charCodeAt(n); 366 | } 367 | resolve(new File([u8arr], filename, { type: mime })); 368 | }); 369 | }; 370 | 371 | window.WAPI.sendMessage = function (idChat, message) { 372 | let id = idChat; 373 | if (!window.WAPI.getAllChatIds().find((chat) => chat == idChat)) 374 | id = new window.Store.UserConstructor(idChat, { intentionallyUsePrivateConstructor: true }); 375 | var chat = WAPI.getChat(id); 376 | if (WAPI._serializeChatObj(chat).isGroup && window.WappBot.configWappBot.ignoreGroupChat) return; 377 | try { 378 | // create new chat 379 | return chat.sendMessage(message); 380 | } catch (e) { 381 | if (window.Store.Chat.length === 0) return false; 382 | 383 | firstChat = Store.Chat.models[0]; 384 | var originalID = firstChat.id; 385 | firstChat.id = 386 | typeof originalID === "string" 387 | ? id 388 | : new window.Store.UserConstructor(id, { intentionallyUsePrivateConstructor: true }); 389 | var chat = WAPI.getChat(firstChat.id); 390 | chat.sendMessage(message); 391 | return true; 392 | } 393 | }; 394 | 395 | window.WappBot.toDataURL = (url) => { 396 | return new Promise((resolve) => { 397 | var xhr = new XMLHttpRequest(); 398 | xhr.onload = function () { 399 | var reader = new FileReader(); 400 | reader.onloadend = function () { 401 | resolve(reader.result); 402 | }; 403 | reader.readAsDataURL(xhr.response); 404 | }; 405 | xhr.open("GET", url); 406 | xhr.responseType = "blob"; 407 | xhr.send(); 408 | }); 409 | }; 410 | 411 | window.WappBot.sendByAPIWappBot = (newMessage, chatId) => { 412 | fetch(window.WappBot.configWappBot.uriApi, { 413 | method: "POST", 414 | headers: { 415 | Accept: "application/json", 416 | "Content-Type": "application/json", 417 | }, 418 | body: JSON.stringify({ messageText: newMessage }), 419 | }).then(function (response) { 420 | response.json().then((post) => { 421 | if (!post && !post.messageResponse) return; 422 | window.WAPI.sendMessage(chatId, post.messageResponse); 423 | }); 424 | }); 425 | }; 426 | 427 | window.WappBot.messageIncludeKey = (message, options) => { 428 | for (let i = 0; i < options.length; i++) { 429 | if (message.toUpperCase().includes(options[i].toUpperCase())) 430 | return window.WappBot.configWappBot.messageOption[options[i]]; 431 | } 432 | return false; 433 | }; 434 | 435 | window.WappBot.prepareMessageToSend = (chatId, options) => { 436 | let message = ""; 437 | if (window.WappBot.configWappBot.ignoreChat.indexOf(chatId) > -1) { 438 | message = `${window.WappBot.configWappBot.messageIncorrect}`; 439 | } else { 440 | message = `${window.WappBot.configWappBot.messageInitial.text}`; 441 | window.WappBot.configWappBot.ignoreChat.push(chatId); 442 | } 443 | for (let i = 0; i < options.length; i++) message += `\t ${options[i]} \n`; 444 | 445 | return message; 446 | }; 447 | 448 | window.WappBot.sendByLocalSetting = (newMessage, chatId) => { 449 | const options = Object.keys(window.WappBot.configWappBot.messageOption); 450 | const messageIncludeKey = window.WappBot.messageIncludeKey(newMessage, options); 451 | if (!messageIncludeKey) { 452 | const message = window.WappBot.prepareMessageToSend(chatId, options); 453 | if (!window.WappBot.configWappBot.messageInitial.image) window.WAPI.sendMessage(chatId, message); 454 | else window.WAPI.sendImage(window.WappBot.configWappBot.messageInitial.image, chatId, "image", message); 455 | } else { 456 | if (!messageIncludeKey.image) window.WAPI.sendMessage(chatId, messageIncludeKey.text); 457 | else window.WAPI.sendImage(messageIncludeKey.image, chatId, "image", messageIncludeKey.text); 458 | } 459 | }; 460 | 461 | /** 462 | * New messages observable functions. 463 | */ 464 | window.WAPI._newMessagesQueue = []; 465 | window.WAPI._newMessagesBuffer = 466 | sessionStorage.getItem("saved_msgs") != null ? JSON.parse(sessionStorage.getItem("saved_msgs")) : []; 467 | window.WAPI._newMessagesDebouncer = null; 468 | window.WAPI._newMessagesCallbacks = []; 469 | 470 | window.Store.Msg.off("add"); 471 | sessionStorage.removeItem("saved_msgs"); 472 | 473 | window.WAPI._newMessagesListener = window.Store.Msg.on("add", (newMessage) => { 474 | if (newMessage && newMessage.isNewMsg && !newMessage.isSentByMe) { 475 | let message = window.WAPI.processMessageObj(newMessage, false, false); 476 | if (message) { 477 | if (window.WappBot.configWappBot.useApi) 478 | window.WappBot.sendByAPIWappBot(message.body, message.chatId._serialized); 479 | else window.WappBot.sendByLocalSetting(message.body, message.chatId._serialized); 480 | } 481 | } 482 | }); 483 | 484 | window.WAPI._unloadInform = (event) => { 485 | // Save in the buffer the ungot unreaded messages 486 | window.WAPI._newMessagesBuffer.forEach((message) => { 487 | Object.keys(message).forEach((key) => (message[key] === undefined ? delete message[key] : "")); 488 | }); 489 | sessionStorage.setItem("saved_msgs", JSON.stringify(window.WAPI._newMessagesBuffer)); 490 | 491 | // Inform callbacks that the page will be reloaded. 492 | window.WAPI._newMessagesCallbacks.forEach(function (callbackObj) { 493 | if (callbackObj.callback !== undefined) { 494 | callbackObj.callback({ status: -1, message: "page will be reloaded, wait and register callback again." }); 495 | } 496 | }); 497 | }; 498 | 499 | window.addEventListener("unload", window.WAPI._unloadInform, false); 500 | window.addEventListener("beforeunload", window.WAPI._unloadInform, false); 501 | window.addEventListener("pageunload", window.WAPI._unloadInform, false); 502 | -------------------------------------------------------------------------------- /newFullMethodWapi.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This script contains WAPI functions that need to be run in the context of the webpage 3 | */ 4 | 5 | /** 6 | * Auto discovery the webpack object references of instances that contains all functions used by the WAPI 7 | * functions and creates the Store object. 8 | */ 9 | if (!window.Store) { 10 | (function () { 11 | function getStore(modules) { 12 | let foundCount = 0; 13 | let neededObjects = [ 14 | { id: "Store", conditions: (module) => (module.default && module.default.Chat && module.default.Msg) ? module.default : null }, 15 | { id: "MediaCollection", conditions: (module) => (module.default && module.default.prototype && module.default.prototype.processAttachments) ? module.default : null }, 16 | { id: "MediaProcess", conditions: (module) => (module.BLOB) ? module : null }, 17 | { id: "Wap", conditions: (module) => (module.createGroup) ? module : null }, 18 | { id: "ServiceWorker", conditions: (module) => (module.default && module.default.killServiceWorker) ? module : null }, 19 | { id: "State", conditions: (module) => (module.STATE && module.STREAM) ? module : null }, 20 | { id: "WapDelete", conditions: (module) => (module.sendConversationDelete && module.sendConversationDelete.length == 2) ? module : null }, 21 | { id: "Conn", conditions: (module) => (module.default && module.default.ref && module.default.refTTL) ? module.default : null }, 22 | { id: "WapQuery", conditions: (module) => (module.queryExist) ? module : ((module.default && module.default.queryExist) ? module.default : null) }, 23 | { id: "CryptoLib", conditions: (module) => (module.decryptE2EMedia) ? module : null }, 24 | { id: "OpenChat", conditions: (module) => (module.default && module.default.prototype && module.default.prototype.openChat) ? module.default : null }, 25 | { id: "UserConstructor", conditions: (module) => (module.default && module.default.prototype && module.default.prototype.isServer && module.default.prototype.isUser) ? module.default : null }, 26 | { id: "SendTextMsgToChat", conditions: (module) => (module.sendTextMsgToChat) ? module.sendTextMsgToChat : null }, 27 | { id: "SendSeen", conditions: (module) => (module.sendSeen) ? module.sendSeen : null }, 28 | { id: "sendDelete", conditions: (module) => (module.sendDelete) ? module.sendDelete : null } 29 | ]; 30 | for (let idx in modules) { 31 | if ((typeof modules[idx] === "object") && (modules[idx] !== null)) { 32 | let first = Object.values(modules[idx])[0]; 33 | if ((typeof first === "object") && (first.exports)) { 34 | for (let idx2 in modules[idx]) { 35 | let module = modules(idx2); 36 | if (!module) { 37 | continue; 38 | } 39 | neededObjects.forEach((needObj) => { 40 | if (!needObj.conditions || needObj.foundedModule) 41 | return; 42 | let neededModule = needObj.conditions(module); 43 | if (neededModule !== null) { 44 | foundCount++; 45 | needObj.foundedModule = neededModule; 46 | } 47 | }); 48 | if (foundCount == neededObjects.length) { 49 | break; 50 | } 51 | } 52 | 53 | let neededStore = neededObjects.find((needObj) => needObj.id === "Store"); 54 | window.Store = neededStore.foundedModule ? neededStore.foundedModule : {}; 55 | neededObjects.splice(neededObjects.indexOf(neededStore), 1); 56 | neededObjects.forEach((needObj) => { 57 | if (needObj.foundedModule) { 58 | window.Store[needObj.id] = needObj.foundedModule; 59 | } 60 | }); 61 | window.Store.sendMessage = function (e) { 62 | return window.Store.SendTextMsgToChat(this, ...arguments); 63 | }; 64 | return window.Store; 65 | } 66 | } 67 | } 68 | } 69 | 70 | if (typeof webpackJsonp === 'function') { 71 | webpackJsonp([], {'parasite': (x, y, z) => getStore(z)}, ['parasite']); 72 | } else { 73 | webpackJsonp.push([ 74 | ['parasite'], 75 | { 76 | parasite: function (o, e, t) { 77 | getStore(t); 78 | } 79 | }, 80 | [['parasite']] 81 | ]); 82 | } 83 | 84 | })(); 85 | } 86 | 87 | window.WAPI = { 88 | lastRead: {} 89 | }; 90 | 91 | window.WAPI._serializeRawObj = (obj) => { 92 | if (obj) { 93 | return obj.toJSON(); 94 | } 95 | return {} 96 | }; 97 | 98 | /** 99 | * Serializes a chat object 100 | * 101 | * @param rawChat Chat object 102 | * @returns {{}} 103 | */ 104 | 105 | window.WAPI._serializeChatObj = (obj) => { 106 | if (obj == undefined) { 107 | return null; 108 | } 109 | 110 | return Object.assign(window.WAPI._serializeRawObj(obj), { 111 | kind : obj.kind, 112 | isGroup : obj.isGroup, 113 | contact : obj['contact'] ? window.WAPI._serializeContactObj(obj['contact']) : null, 114 | groupMetadata: obj["groupMetadata"] ? window.WAPI._serializeRawObj(obj["groupMetadata"]): null, 115 | presence : obj["presence"] ? window.WAPI._serializeRawObj(obj["presence"]) : null, 116 | msgs : null 117 | }); 118 | }; 119 | 120 | window.WAPI._serializeContactObj = (obj) => { 121 | if (obj == undefined) { 122 | return null; 123 | } 124 | 125 | return Object.assign(window.WAPI._serializeRawObj(obj), { 126 | formattedName : obj.formattedName, 127 | isHighLevelVerified: obj.isHighLevelVerified, 128 | isMe : obj.isMe, 129 | isMyContact : obj.isMyContact, 130 | isPSA : obj.isPSA, 131 | isUser : obj.isUser, 132 | isVerified : obj.isVerified, 133 | isWAContact : obj.isWAContact, 134 | profilePicThumbObj : obj.profilePicThumb ? WAPI._serializeProfilePicThumb(obj.profilePicThumb): {}, 135 | statusMute : obj.statusMute, 136 | msgs : null 137 | }); 138 | }; 139 | 140 | window.WAPI._serializeMessageObj = (obj) => { 141 | if (obj == undefined) { 142 | return null; 143 | } 144 | 145 | return Object.assign(window.WAPI._serializeRawObj(obj), { 146 | id : obj.id._serialized, 147 | sender : obj["senderObj"] ? WAPI._serializeContactObj(obj["senderObj"]): null, 148 | timestamp : obj["t"], 149 | content : obj["body"], 150 | isGroupMsg : obj.isGroupMsg, 151 | isLink : obj.isLink, 152 | isMMS : obj.isMMS, 153 | isMedia : obj.isMedia, 154 | isNotification: obj.isNotification, 155 | isPSA : obj.isPSA, 156 | type : obj.type, 157 | chat : WAPI._serializeChatObj(obj['chat']), 158 | chatId : obj.id.remote, 159 | quotedMsgObj : WAPI._serializeMessageObj(obj['_quotedMsgObj']), 160 | mediaData : window.WAPI._serializeRawObj(obj['mediaData']) 161 | }); 162 | }; 163 | 164 | window.WAPI._serializeNumberStatusObj = (obj) => { 165 | if (obj == undefined) { 166 | return null; 167 | } 168 | 169 | return Object.assign({}, { 170 | id : obj.jid, 171 | status : obj.status, 172 | isBusiness : (obj.biz === true), 173 | canReceiveMessage: (obj.status === 200) 174 | }); 175 | }; 176 | 177 | window.WAPI._serializeProfilePicThumb = (obj) => { 178 | if (obj == undefined) { 179 | return null; 180 | } 181 | 182 | return Object.assign({}, { 183 | eurl : obj.eurl, 184 | id : obj.id, 185 | img : obj.img, 186 | imgFull: obj.imgFull, 187 | raw : obj.raw, 188 | tag : obj.tag 189 | }); 190 | } 191 | 192 | window.WAPI.createGroup = function (name, contactsId) { 193 | if (!Array.isArray(contactsId)) { 194 | contactsId = [contactsId]; 195 | } 196 | 197 | return window.Store.Wap.createGroup(name, contactsId); 198 | }; 199 | 200 | window.WAPI.leaveGroup = function (groupId) { 201 | groupId = typeof groupId == "string" ? groupId : groupId._serialized; 202 | var group = WAPI.getChat(groupId); 203 | return group.sendExit() 204 | }; 205 | 206 | 207 | window.WAPI.getAllContacts = function (done) { 208 | const contacts = window.Store.Contact.map((contact) => WAPI._serializeContactObj(contact)); 209 | 210 | if (done !== undefined) done(contacts); 211 | return contacts; 212 | }; 213 | 214 | /** 215 | * Fetches all contact objects from store, filters them 216 | * 217 | * @param done Optional callback function for async execution 218 | * @returns {Array|*} List of contacts 219 | */ 220 | window.WAPI.getMyContacts = function (done) { 221 | const contacts = window.Store.Contact.filter((contact) => contact.isMyContact === true).map((contact) => WAPI._serializeContactObj(contact)); 222 | if (done !== undefined) done(contacts); 223 | return contacts; 224 | }; 225 | 226 | /** 227 | * Fetches contact object from store by ID 228 | * 229 | * @param id ID of contact 230 | * @param done Optional callback function for async execution 231 | * @returns {T|*} Contact object 232 | */ 233 | window.WAPI.getContact = function (id, done) { 234 | const found = window.Store.Contact.get(id); 235 | 236 | if (done !== undefined) done(window.WAPI._serializeContactObj(found)) 237 | return window.WAPI._serializeContactObj(found); 238 | }; 239 | 240 | /** 241 | * Fetches all chat objects from store 242 | * 243 | * @param done Optional callback function for async execution 244 | * @returns {Array|*} List of chats 245 | */ 246 | window.WAPI.getAllChats = function (done) { 247 | const chats = window.Store.Chat.map((chat) => WAPI._serializeChatObj(chat)); 248 | 249 | if (done !== undefined) done(chats); 250 | return chats; 251 | }; 252 | 253 | window.WAPI.haveNewMsg = function (chat) { 254 | return chat.unreadCount > 0; 255 | }; 256 | 257 | window.WAPI.getAllChatsWithNewMsg = function (done) { 258 | const chats = window.Store.Chat.filter(window.WAPI.haveNewMsg).map((chat) => WAPI._serializeChatObj(chat)); 259 | 260 | if (done !== undefined) done(chats); 261 | return chats; 262 | }; 263 | 264 | /** 265 | * Fetches all chat IDs from store 266 | * 267 | * @param done Optional callback function for async execution 268 | * @returns {Array|*} List of chat id's 269 | */ 270 | window.WAPI.getAllChatIds = function (done) { 271 | const chatIds = window.Store.Chat.map((chat) => chat.id._serialized || chat.id); 272 | 273 | if (done !== undefined) done(chatIds); 274 | return chatIds; 275 | }; 276 | 277 | /** 278 | * Fetches all groups objects from store 279 | * 280 | * @param done Optional callback function for async execution 281 | * @returns {Array|*} List of chats 282 | */ 283 | window.WAPI.getAllGroups = function (done) { 284 | const groups = window.Store.Chat.filter((chat) => chat.isGroup); 285 | 286 | if (done !== undefined) done(groups); 287 | return groups; 288 | }; 289 | 290 | /** 291 | * Fetches chat object from store by ID 292 | * 293 | * @param id ID of chat 294 | * @param done Optional callback function for async execution 295 | * @returns {T|*} Chat object 296 | */ 297 | window.WAPI.getChat = function (id, done) { 298 | id = typeof id == "string" ? id : id._serialized; 299 | const found = window.Store.Chat.get(id); 300 | found.sendMessage = (found.sendMessage) ? found.sendMessage : function () { return window.Store.sendMessage.apply(this, arguments); }; 301 | if (done !== undefined) done(found); 302 | return found; 303 | } 304 | 305 | window.WAPI.getChatByName = function (name, done) { 306 | const found = window.WAPI.getAllChats().find(val => val.name.includes(name)) 307 | if (done !== undefined) done(found); 308 | return found; 309 | }; 310 | 311 | window.WAPI.sendImageFromDatabasePicBot = function (picId, chatId, caption) { 312 | var chatDatabase = window.WAPI.getChatByName('DATABASEPICBOT'); 313 | var msgWithImg = chatDatabase.msgs.find((msg) => msg.caption == picId); 314 | 315 | if (msgWithImg === undefined) { 316 | return false; 317 | } 318 | var chatSend = WAPI.getChat(chatId); 319 | if (chatSend === undefined) { 320 | return false; 321 | } 322 | const oldCaption = msgWithImg.caption; 323 | 324 | msgWithImg.id.id = window.WAPI.getNewId(); 325 | msgWithImg.id.remote = chatId; 326 | msgWithImg.t = Math.ceil(new Date().getTime() / 1000); 327 | msgWithImg.to = chatId; 328 | 329 | if (caption !== undefined && caption !== '') { 330 | msgWithImg.caption = caption; 331 | } else { 332 | msgWithImg.caption = ''; 333 | } 334 | 335 | msgWithImg.collection.send(msgWithImg).then(function (e) { 336 | msgWithImg.caption = oldCaption; 337 | }); 338 | 339 | return true; 340 | }; 341 | 342 | window.WAPI.sendMessageWithThumb = function (thumb, url, title, description, text, chatId, done) { 343 | var chatSend = WAPI.getChat(chatId); 344 | if (chatSend === undefined) { 345 | if (done !== undefined) done(false); 346 | return false; 347 | } 348 | var linkPreview = { 349 | canonicalUrl: url, 350 | description : description, 351 | matchedText : url, 352 | title : title, 353 | thumbnail : thumb, 354 | compose: true 355 | }; 356 | chatSend.sendMessage(text, { linkPreview: linkPreview, 357 | mentionedJidList: [], 358 | quotedMsg: null, 359 | quotedMsgAdminGroupJid: null }); 360 | if (done !== undefined) done(true); 361 | return true; 362 | }; 363 | 364 | window.WAPI.getNewId = function () { 365 | var text = ""; 366 | var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 367 | 368 | for (var i = 0; i < 20; i++) 369 | text += possible.charAt(Math.floor(Math.random() * possible.length)); 370 | return text; 371 | }; 372 | 373 | window.WAPI.getChatById = function (id, done) { 374 | let found = WAPI.getChat(id); 375 | if (found) { 376 | found = WAPI._serializeChatObj(found); 377 | } else { 378 | found = false; 379 | } 380 | 381 | if (done !== undefined) done(found); 382 | return found; 383 | }; 384 | 385 | 386 | /** 387 | * I return all unread messages from an asked chat and mark them as read. 388 | * 389 | * :param id: chat id 390 | * :type id: string 391 | * 392 | * :param includeMe: indicates if user messages have to be included 393 | * :type includeMe: boolean 394 | * 395 | * :param includeNotifications: indicates if notifications have to be included 396 | * :type includeNotifications: boolean 397 | * 398 | * :param done: callback passed by selenium 399 | * :type done: function 400 | * 401 | * :returns: list of unread messages from asked chat 402 | * :rtype: object 403 | */ 404 | window.WAPI.getUnreadMessagesInChat = function (id, includeMe, includeNotifications, done) { 405 | // get chat and its messages 406 | let chat = WAPI.getChat(id); 407 | let messages = chat.msgs._models; 408 | 409 | // initialize result list 410 | let output = []; 411 | 412 | // look for unread messages, newest is at the end of array 413 | for (let i = messages.length - 1; i >= 0; i--) { 414 | // system message: skip it 415 | if (i === "remove") { 416 | continue; 417 | } 418 | 419 | // get message 420 | let messageObj = messages[i]; 421 | 422 | // found a read message: stop looking for others 423 | if (typeof (messageObj.isNewMsg) !== "boolean" || messageObj.isNewMsg === false) { 424 | continue; 425 | } else { 426 | messageObj.isNewMsg = false; 427 | // process it 428 | let message = WAPI.processMessageObj(messageObj, 429 | includeMe, 430 | includeNotifications); 431 | 432 | // save processed message on result list 433 | if (message) 434 | output.push(message); 435 | } 436 | } 437 | // callback was passed: run it 438 | if (done !== undefined) done(output); 439 | // return result list 440 | return output; 441 | } 442 | ; 443 | 444 | 445 | /** 446 | * Load more messages in chat object from store by ID 447 | * 448 | * @param id ID of chat 449 | * @param done Optional callback function for async execution 450 | * @returns None 451 | */ 452 | window.WAPI.loadEarlierMessages = function (id, done) { 453 | const found = WAPI.getChat(id); 454 | if (done !== undefined) { 455 | found.loadEarlierMsgs().then(function () { 456 | done() 457 | }); 458 | } else { 459 | found.loadEarlierMsgs(); 460 | } 461 | }; 462 | 463 | /** 464 | * Load more messages in chat object from store by ID 465 | * 466 | * @param id ID of chat 467 | * @param done Optional callback function for async execution 468 | * @returns None 469 | */ 470 | window.WAPI.loadAllEarlierMessages = function (id, done) { 471 | const found = WAPI.getChat(id); 472 | x = function () { 473 | if (!found.msgs.msgLoadState.noEarlierMsgs) { 474 | found.loadEarlierMsgs().then(x); 475 | } else if (done) { 476 | done(); 477 | } 478 | }; 479 | x(); 480 | }; 481 | 482 | window.WAPI.asyncLoadAllEarlierMessages = function (id, done) { 483 | done(); 484 | window.WAPI.loadAllEarlierMessages(id); 485 | }; 486 | 487 | window.WAPI.areAllMessagesLoaded = function (id, done) { 488 | const found = WAPI.getChat(id); 489 | if (!found.msgs.msgLoadState.noEarlierMsgs) { 490 | if (done) done(false); 491 | return false 492 | } 493 | if (done) done(true); 494 | return true 495 | }; 496 | 497 | /** 498 | * Load more messages in chat object from store by ID till a particular date 499 | * 500 | * @param id ID of chat 501 | * @param lastMessage UTC timestamp of last message to be loaded 502 | * @param done Optional callback function for async execution 503 | * @returns None 504 | */ 505 | 506 | window.WAPI.loadEarlierMessagesTillDate = function (id, lastMessage, done) { 507 | const found = WAPI.getChat(id); 508 | x = function () { 509 | if (found.msgs.models[0].t > lastMessage && !found.msgs.msgLoadState.noEarlierMsgs) { 510 | found.loadEarlierMsgs().then(x); 511 | } else { 512 | done(); 513 | } 514 | }; 515 | x(); 516 | }; 517 | 518 | 519 | /** 520 | * Fetches all group metadata objects from store 521 | * 522 | * @param done Optional callback function for async execution 523 | * @returns {Array|*} List of group metadata 524 | */ 525 | window.WAPI.getAllGroupMetadata = function (done) { 526 | const groupData = window.Store.GroupMetadata.map((groupData) => groupData.all); 527 | 528 | if (done !== undefined) done(groupData); 529 | return groupData; 530 | }; 531 | 532 | /** 533 | * Fetches group metadata object from store by ID 534 | * 535 | * @param id ID of group 536 | * @param done Optional callback function for async execution 537 | * @returns {T|*} Group metadata object 538 | */ 539 | window.WAPI.getGroupMetadata = async function (id, done) { 540 | let output = window.Store.GroupMetadata.get(id); 541 | 542 | if (output !== undefined) { 543 | if (output.stale) { 544 | await output.update(); 545 | } 546 | } 547 | 548 | if (done !== undefined) done(output); 549 | return output; 550 | 551 | }; 552 | 553 | 554 | /** 555 | * Fetches group participants 556 | * 557 | * @param id ID of group 558 | * @returns {Promise.<*>} Yields group metadata 559 | * @private 560 | */ 561 | window.WAPI._getGroupParticipants = async function (id) { 562 | const metadata = await WAPI.getGroupMetadata(id); 563 | return metadata.participants; 564 | }; 565 | 566 | /** 567 | * Fetches IDs of group participants 568 | * 569 | * @param id ID of group 570 | * @param done Optional callback function for async execution 571 | * @returns {Promise.} Yields list of IDs 572 | */ 573 | window.WAPI.getGroupParticipantIDs = async function (id, done) { 574 | const output = (await WAPI._getGroupParticipants(id)) 575 | .map((participant) => participant.id); 576 | 577 | if (done !== undefined) done(output); 578 | return output; 579 | }; 580 | 581 | window.WAPI.getGroupAdmins = async function (id, done) { 582 | const output = (await WAPI._getGroupParticipants(id)) 583 | .filter((participant) => participant.isAdmin) 584 | .map((admin) => admin.id); 585 | 586 | if (done !== undefined) done(output); 587 | return output; 588 | }; 589 | 590 | /** 591 | * Gets object representing the logged in user 592 | * 593 | * @returns {Array|*|$q.all} 594 | */ 595 | window.WAPI.getMe = function (done) { 596 | const rawMe = window.Store.Contact.get(window.Store.Conn.me); 597 | 598 | if (done !== undefined) done(rawMe.all); 599 | return rawMe.all; 600 | }; 601 | 602 | window.WAPI.isLoggedIn = function (done) { 603 | // Contact always exists when logged in 604 | const isLogged = window.Store.Contact && window.Store.Contact.checksum !== undefined; 605 | 606 | if (done !== undefined) done(isLogged); 607 | return isLogged; 608 | }; 609 | 610 | window.WAPI.isConnected = function (done) { 611 | // Phone Disconnected icon appears when phone is disconnected from the tnternet 612 | const isConnected = document.querySelector('*[data-icon="alert-phone"]') !== null ? false : true; 613 | 614 | if (done !== undefined) done(isConnected); 615 | return isConnected; 616 | }; 617 | 618 | window.WAPI.processMessageObj = function (messageObj, includeMe, includeNotifications) { 619 | if (messageObj.isNotification) { 620 | if (includeNotifications) 621 | return WAPI._serializeMessageObj(messageObj); 622 | else 623 | return; 624 | // System message 625 | // (i.e. "Messages you send to this chat and calls are now secured with end-to-end encryption...") 626 | } else if (messageObj.id.fromMe === false || includeMe) { 627 | return WAPI._serializeMessageObj(messageObj); 628 | } 629 | return; 630 | }; 631 | 632 | window.WAPI.getAllMessagesInChat = function (id, includeMe, includeNotifications, done) { 633 | const chat = WAPI.getChat(id); 634 | let output = []; 635 | const messages = chat.msgs._models; 636 | 637 | for (const i in messages) { 638 | if (i === "remove") { 639 | continue; 640 | } 641 | const messageObj = messages[i]; 642 | 643 | let message = WAPI.processMessageObj(messageObj, includeMe, includeNotifications) 644 | if (message) 645 | output.push(message); 646 | } 647 | if (done !== undefined) done(output); 648 | return output; 649 | }; 650 | 651 | window.WAPI.getAllMessageIdsInChat = function (id, includeMe, includeNotifications, done) { 652 | const chat = WAPI.getChat(id); 653 | let output = []; 654 | const messages = chat.msgs._models; 655 | 656 | for (const i in messages) { 657 | if ((i === "remove") 658 | || (!includeMe && messages[i].isMe) 659 | || (!includeNotifications && messages[i].isNotification)) { 660 | continue; 661 | } 662 | output.push(messages[i].id._serialized); 663 | } 664 | if (done !== undefined) done(output); 665 | return output; 666 | }; 667 | 668 | window.WAPI.getMessageById = function (id, done) { 669 | let result = false; 670 | try { 671 | let msg = window.Store.Msg.get(id); 672 | if (msg) { 673 | result = WAPI.processMessageObj(msg, true, true); 674 | } 675 | } catch (err) { } 676 | 677 | if (done !== undefined) { 678 | done(result); 679 | } else { 680 | return result; 681 | } 682 | }; 683 | 684 | window.WAPI.ReplyMessage = function (idMessage, message, done) { 685 | var messageObject = window.Store.Msg.get(idMessage); 686 | if (messageObject === undefined) { 687 | if (done !== undefined) done(false); 688 | return false; 689 | } 690 | messageObject = messageObject.value(); 691 | 692 | const chat = WAPI.getChat(messageObject.chat.id) 693 | if (chat !== undefined) { 694 | if (done !== undefined) { 695 | chat.sendMessage(message, null, messageObject).then(function () { 696 | function sleep(ms) { 697 | return new Promise(resolve => setTimeout(resolve, ms)); 698 | } 699 | 700 | var trials = 0; 701 | 702 | function check() { 703 | for (let i = chat.msgs.models.length - 1; i >= 0; i--) { 704 | let msg = chat.msgs.models[i]; 705 | 706 | if (!msg.senderObj.isMe || msg.body != message) { 707 | continue; 708 | } 709 | done(WAPI._serializeMessageObj(msg)); 710 | return True; 711 | } 712 | trials += 1; 713 | console.log(trials); 714 | if (trials > 30) { 715 | done(true); 716 | return; 717 | } 718 | sleep(500).then(check); 719 | } 720 | check(); 721 | }); 722 | return true; 723 | } else { 724 | chat.sendMessage(message, null, messageObject); 725 | return true; 726 | } 727 | } else { 728 | if (done !== undefined) done(false); 729 | return false; 730 | } 731 | }; 732 | 733 | window.WAPI.sendMessageToID = function (id, message, done) { 734 | try { 735 | window.getContact = (id) => { 736 | return Store.WapQuery.queryExist(id); 737 | } 738 | window.getContact(id).then(contact => { 739 | if (contact.status === 404) { 740 | done(true); 741 | } else { 742 | Store.Chat.find(contact.jid).then(chat => { 743 | chat.sendMessage(message); 744 | return true; 745 | }).catch(reject => { 746 | if (WAPI.sendMessage(id, message)) { 747 | done(true); 748 | return true; 749 | }else{ 750 | done(false); 751 | return false; 752 | } 753 | }); 754 | } 755 | }); 756 | } catch (e) { 757 | if (window.Store.Chat.length === 0) 758 | return false; 759 | 760 | firstChat = Store.Chat.models[0]; 761 | var originalID = firstChat.id; 762 | firstChat.id = typeof originalID === "string" ? id : new window.Store.UserConstructor(id, { intentionallyUsePrivateConstructor: true }); 763 | if (done !== undefined) { 764 | firstChat.sendMessage(message).then(function () { 765 | firstChat.id = originalID; 766 | done(true); 767 | }); 768 | return true; 769 | } else { 770 | firstChat.sendMessage(message); 771 | firstChat.id = originalID; 772 | return true; 773 | } 774 | } 775 | if (done !== undefined) done(false); 776 | return false; 777 | } 778 | 779 | window.WAPI.sendMessage = function (id, message, done) { 780 | var chat = WAPI.getChat(id); 781 | if (chat !== undefined) { 782 | if (done !== undefined) { 783 | chat.sendMessage(message).then(function () { 784 | function sleep(ms) { 785 | return new Promise(resolve => setTimeout(resolve, ms)); 786 | } 787 | 788 | var trials = 0; 789 | 790 | function check() { 791 | for (let i = chat.msgs.models.length - 1; i >= 0; i--) { 792 | let msg = chat.msgs.models[i]; 793 | 794 | if (!msg.senderObj.isMe || msg.body != message) { 795 | continue; 796 | } 797 | done(WAPI._serializeMessageObj(msg)); 798 | return True; 799 | } 800 | trials += 1; 801 | console.log(trials); 802 | if (trials > 30) { 803 | done(true); 804 | return; 805 | } 806 | sleep(500).then(check); 807 | } 808 | check(); 809 | }); 810 | return true; 811 | } else { 812 | chat.sendMessage(message); 813 | return true; 814 | } 815 | } else { 816 | if (done !== undefined) done(false); 817 | return false; 818 | } 819 | }; 820 | 821 | window.WAPI.sendMessage2 = function (id, message, done) { 822 | var chat = WAPI.getChat(id); 823 | if (chat !== undefined) { 824 | try { 825 | if (done !== undefined) { 826 | chat.sendMessage(message).then(function () { 827 | done(true); 828 | }); 829 | } else { 830 | chat.sendMessage(message); 831 | } 832 | return true; 833 | } catch (error) { 834 | if (done !== undefined) done(false) 835 | return false; 836 | } 837 | } 838 | if (done !== undefined) done(false) 839 | return false; 840 | }; 841 | 842 | window.WAPI.sendSeen = function (id, done) { 843 | var chat = window.WAPI.getChat(id); 844 | if (chat !== undefined) { 845 | if (done !== undefined) { 846 | if (chat.getLastMsgKeyForAction === undefined) 847 | chat.getLastMsgKeyForAction = function () { }; 848 | Store.SendSeen(chat, false).then(function () { 849 | done(true); 850 | }); 851 | return true; 852 | } else { 853 | Store.SendSeen(chat, false); 854 | return true; 855 | } 856 | } 857 | if (done !== undefined) done(); 858 | return false; 859 | }; 860 | 861 | function isChatMessage(message) { 862 | if (message.isSentByMe) { 863 | return false; 864 | } 865 | if (message.isNotification) { 866 | return false; 867 | } 868 | if (!message.isUserCreatedType) { 869 | return false; 870 | } 871 | return true; 872 | } 873 | 874 | 875 | window.WAPI.getUnreadMessages = function (includeMe, includeNotifications, use_unread_count, done) { 876 | const chats = window.Store.Chat.models; 877 | let output = []; 878 | 879 | for (let chat in chats) { 880 | if (isNaN(chat)) { 881 | continue; 882 | } 883 | 884 | let messageGroupObj = chats[chat]; 885 | let messageGroup = WAPI._serializeChatObj(messageGroupObj); 886 | 887 | messageGroup.messages = []; 888 | 889 | const messages = messageGroupObj.msgs._models; 890 | for (let i = messages.length - 1; i >= 0; i--) { 891 | let messageObj = messages[i]; 892 | if (typeof (messageObj.isNewMsg) != "boolean" || messageObj.isNewMsg === false) { 893 | continue; 894 | } else { 895 | messageObj.isNewMsg = false; 896 | let message = WAPI.processMessageObj(messageObj, includeMe, includeNotifications); 897 | if (message) { 898 | messageGroup.messages.push(message); 899 | } 900 | } 901 | } 902 | 903 | if (messageGroup.messages.length > 0) { 904 | output.push(messageGroup); 905 | } else { // no messages with isNewMsg true 906 | if (use_unread_count) { 907 | let n = messageGroupObj.unreadCount; // will use unreadCount attribute to fetch last n messages from sender 908 | for (let i = messages.length - 1; i >= 0; i--) { 909 | let messageObj = messages[i]; 910 | if (n > 0) { 911 | if (!messageObj.isSentByMe) { 912 | let message = WAPI.processMessageObj(messageObj, includeMe, includeNotifications); 913 | messageGroup.messages.unshift(message); 914 | n -= 1; 915 | } 916 | } else if (n === -1) { // chat was marked as unread so will fetch last message as unread 917 | if (!messageObj.isSentByMe) { 918 | let message = WAPI.processMessageObj(messageObj, includeMe, includeNotifications); 919 | messageGroup.messages.unshift(message); 920 | break; 921 | } 922 | } else { // unreadCount = 0 923 | break; 924 | } 925 | } 926 | if (messageGroup.messages.length > 0) { 927 | messageGroupObj.unreadCount = 0; // reset unread counter 928 | output.push(messageGroup); 929 | } 930 | } 931 | } 932 | } 933 | if (done !== undefined) { 934 | done(output); 935 | } 936 | return output; 937 | }; 938 | 939 | window.WAPI.getGroupOwnerID = async function (id, done) { 940 | const output = (await WAPI.getGroupMetadata(id)).owner.id; 941 | if (done !== undefined) { 942 | done(output); 943 | } 944 | return output; 945 | 946 | }; 947 | 948 | window.WAPI.getCommonGroups = async function (id, done) { 949 | let output = []; 950 | 951 | groups = window.WAPI.getAllGroups(); 952 | 953 | for (let idx in groups) { 954 | try { 955 | participants = await window.WAPI.getGroupParticipantIDs(groups[idx].id); 956 | if (participants.filter((participant) => participant == id).length) { 957 | output.push(groups[idx]); 958 | } 959 | } catch (err) { 960 | console.log("Error in group:"); 961 | console.log(groups[idx]); 962 | console.log(err); 963 | } 964 | } 965 | 966 | if (done !== undefined) { 967 | done(output); 968 | } 969 | return output; 970 | }; 971 | 972 | 973 | window.WAPI.getProfilePicSmallFromId = function (id, done) { 974 | window.Store.ProfilePicThumb.find(id).then(function (d) { 975 | if (d.img !== undefined) { 976 | window.WAPI.downloadFileWithCredentials(d.img, done); 977 | } else { 978 | done(false); 979 | } 980 | }, function (e) { 981 | done(false); 982 | }) 983 | }; 984 | 985 | window.WAPI.getProfilePicFromId = function (id, done) { 986 | window.Store.ProfilePicThumb.find(id).then(function (d) { 987 | if (d.imgFull !== undefined) { 988 | window.WAPI.downloadFileWithCredentials(d.imgFull, done); 989 | } else { 990 | done(false); 991 | } 992 | }, function (e) { 993 | done(false); 994 | }) 995 | }; 996 | 997 | window.WAPI.downloadFileWithCredentials = function (url, done) { 998 | let xhr = new XMLHttpRequest(); 999 | 1000 | xhr.onload = function () { 1001 | if (xhr.readyState == 4) { 1002 | if (xhr.status == 200) { 1003 | let reader = new FileReader(); 1004 | reader.readAsDataURL(xhr.response); 1005 | reader.onload = function (e) { 1006 | done(reader.result.substr(reader.result.indexOf(',') + 1)) 1007 | }; 1008 | } else { 1009 | console.error(xhr.statusText); 1010 | } 1011 | } else { 1012 | console.log(err); 1013 | done(false); 1014 | } 1015 | }; 1016 | 1017 | xhr.open("GET", url, true); 1018 | xhr.withCredentials = true; 1019 | xhr.responseType = 'blob'; 1020 | xhr.send(null); 1021 | }; 1022 | 1023 | 1024 | window.WAPI.downloadFile = function (url, done) { 1025 | let xhr = new XMLHttpRequest(); 1026 | 1027 | 1028 | xhr.onload = function () { 1029 | if (xhr.readyState == 4) { 1030 | if (xhr.status == 200) { 1031 | let reader = new FileReader(); 1032 | reader.readAsDataURL(xhr.response); 1033 | reader.onload = function (e) { 1034 | done(reader.result.substr(reader.result.indexOf(',') + 1)) 1035 | }; 1036 | } else { 1037 | console.error(xhr.statusText); 1038 | } 1039 | } else { 1040 | console.log(err); 1041 | done(false); 1042 | } 1043 | }; 1044 | 1045 | xhr.open("GET", url, true); 1046 | xhr.responseType = 'blob'; 1047 | xhr.send(null); 1048 | }; 1049 | 1050 | window.WAPI.getBatteryLevel = function (done) { 1051 | if (window.Store.Conn.plugged) { 1052 | if (done !== undefined) { 1053 | done(100); 1054 | } 1055 | return 100; 1056 | } 1057 | output = window.Store.Conn.battery; 1058 | if (done !== undefined) { 1059 | done(output); 1060 | } 1061 | return output; 1062 | }; 1063 | 1064 | window.WAPI.deleteConversation = function (chatId, done) { 1065 | let userId = new window.Store.UserConstructor(chatId, {intentionallyUsePrivateConstructor: true}); 1066 | let conversation = WAPI.getChat(userId); 1067 | 1068 | if (!conversation) { 1069 | if (done !== undefined) { 1070 | done(false); 1071 | } 1072 | return false; 1073 | } 1074 | 1075 | window.Store.sendDelete(conversation, false).then(() => { 1076 | if (done !== undefined) { 1077 | done(true); 1078 | } 1079 | }).catch(() => { 1080 | if (done !== undefined) { 1081 | done(false); 1082 | } 1083 | }); 1084 | 1085 | return true; 1086 | }; 1087 | 1088 | window.WAPI.deleteMessage = function (chatId, messageArray, revoke=false, done) { 1089 | let userId = new window.Store.UserConstructor(chatId, {intentionallyUsePrivateConstructor: true}); 1090 | let conversation = WAPI.getChat(userId); 1091 | 1092 | if(!conversation) { 1093 | if(done !== undefined) { 1094 | done(false); 1095 | } 1096 | return false; 1097 | } 1098 | 1099 | if (!Array.isArray(messageArray)) { 1100 | messageArray = [messageArray]; 1101 | } 1102 | let messagesToDelete = messageArray.map(msgId => window.Store.Msg.get(msgId)); 1103 | 1104 | if (revoke) { 1105 | conversation.sendRevokeMsgs(messagesToDelete, conversation); 1106 | } else { 1107 | conversation.sendDeleteMsgs(messagesToDelete, conversation); 1108 | } 1109 | 1110 | 1111 | if (done !== undefined) { 1112 | done(true); 1113 | } 1114 | 1115 | return true; 1116 | }; 1117 | 1118 | window.WAPI.checkNumberStatus = function (id, done) { 1119 | window.Store.WapQuery.queryExist(id).then((result) => { 1120 | if( done !== undefined) { 1121 | if (result.jid === undefined) throw 404; 1122 | done(window.WAPI._serializeNumberStatusObj(result)); 1123 | } 1124 | }).catch((e) => { 1125 | if (done !== undefined) { 1126 | done(window.WAPI._serializeNumberStatusObj({ 1127 | status: e, 1128 | jid : id 1129 | })); 1130 | } 1131 | }); 1132 | 1133 | return true; 1134 | }; 1135 | 1136 | /** 1137 | * New messages observable functions. 1138 | */ 1139 | window.WAPI._newMessagesQueue = []; 1140 | window.WAPI._newMessagesBuffer = (sessionStorage.getItem('saved_msgs') != null) ? JSON.parse(sessionStorage.getItem('saved_msgs')) : []; 1141 | window.WAPI._newMessagesDebouncer = null; 1142 | window.WAPI._newMessagesCallbacks = []; 1143 | 1144 | window.Store.Msg.off('add'); 1145 | sessionStorage.removeItem('saved_msgs'); 1146 | 1147 | window.WAPI._newMessagesListener = window.Store.Msg.on('add', (newMessage) => { 1148 | if (newMessage && newMessage.isNewMsg && !newMessage.isSentByMe) { 1149 | let message = window.WAPI.processMessageObj(newMessage, false, false); 1150 | if (message) { 1151 | window.WAPI._newMessagesQueue.push(message); 1152 | window.WAPI._newMessagesBuffer.push(message); 1153 | } 1154 | 1155 | // Starts debouncer time to don't call a callback for each message if more than one message arrives 1156 | // in the same second 1157 | if (!window.WAPI._newMessagesDebouncer && window.WAPI._newMessagesQueue.length > 0) { 1158 | window.WAPI._newMessagesDebouncer = setTimeout(() => { 1159 | let queuedMessages = window.WAPI._newMessagesQueue; 1160 | 1161 | window.WAPI._newMessagesDebouncer = null; 1162 | window.WAPI._newMessagesQueue = []; 1163 | 1164 | let removeCallbacks = []; 1165 | 1166 | window.WAPI._newMessagesCallbacks.forEach(function (callbackObj) { 1167 | if (callbackObj.callback !== undefined) { 1168 | callbackObj.callback(queuedMessages); 1169 | } 1170 | if (callbackObj.rmAfterUse === true) { 1171 | removeCallbacks.push(callbackObj); 1172 | } 1173 | }); 1174 | 1175 | // Remove removable callbacks. 1176 | removeCallbacks.forEach(function (rmCallbackObj) { 1177 | let callbackIndex = window.WAPI._newMessagesCallbacks.indexOf(rmCallbackObj); 1178 | window.WAPI._newMessagesCallbacks.splice(callbackIndex, 1); 1179 | }); 1180 | }, 1000); 1181 | } 1182 | } 1183 | }); 1184 | 1185 | window.WAPI._unloadInform = (event) => { 1186 | // Save in the buffer the ungot unreaded messages 1187 | window.WAPI._newMessagesBuffer.forEach((message) => { 1188 | Object.keys(message).forEach(key => message[key] === undefined ? delete message[key] : ''); 1189 | }); 1190 | sessionStorage.setItem("saved_msgs", JSON.stringify(window.WAPI._newMessagesBuffer)); 1191 | 1192 | // Inform callbacks that the page will be reloaded. 1193 | window.WAPI._newMessagesCallbacks.forEach(function (callbackObj) { 1194 | if (callbackObj.callback !== undefined) { 1195 | callbackObj.callback({ status: -1, message: 'page will be reloaded, wait and register callback again.' }); 1196 | } 1197 | }); 1198 | }; 1199 | 1200 | window.addEventListener("unload", window.WAPI._unloadInform, false); 1201 | window.addEventListener("beforeunload", window.WAPI._unloadInform, false); 1202 | window.addEventListener("pageunload", window.WAPI._unloadInform, false); 1203 | 1204 | /** 1205 | * Registers a callback to be called when a new message arrives the WAPI. 1206 | * @param rmCallbackAfterUse - Boolean - Specify if the callback need to be executed only once 1207 | * @param done - function - Callback function to be called when a new message arrives. 1208 | * @returns {boolean} 1209 | */ 1210 | window.WAPI.waitNewMessages = function (rmCallbackAfterUse = true, done) { 1211 | window.WAPI._newMessagesCallbacks.push({ callback: done, rmAfterUse: rmCallbackAfterUse }); 1212 | return true; 1213 | }; 1214 | 1215 | /** 1216 | * Reads buffered new messages. 1217 | * @param done - function - Callback function to be called contained the buffered messages. 1218 | * @returns {Array} 1219 | */ 1220 | window.WAPI.getBufferedNewMessages = function (done) { 1221 | let bufferedMessages = window.WAPI._newMessagesBuffer; 1222 | window.WAPI._newMessagesBuffer = []; 1223 | if (done !== undefined) { 1224 | done(bufferedMessages); 1225 | } 1226 | return bufferedMessages; 1227 | }; 1228 | /** End new messages observable functions **/ 1229 | 1230 | window.WAPI.sendImage = function (imgBase64, chatid, filename, caption, done) { 1231 | //var idUser = new window.Store.UserConstructor(chatid); 1232 | var idUser = new window.Store.UserConstructor(chatid, { intentionallyUsePrivateConstructor: true }); 1233 | // create new chat 1234 | return Store.Chat.find(idUser).then((chat) => { 1235 | var mediaBlob = window.WAPI.base64ImageToFile(imgBase64, filename); 1236 | var mc = new Store.MediaCollection(chat); 1237 | mc.processAttachments([{file: mediaBlob}, 1], chat, 1).then(() => { 1238 | var media = mc.models[0]; 1239 | media.sendToChat(chat, { caption: caption }); 1240 | if (done !== undefined) done(true); 1241 | }); 1242 | }); 1243 | } 1244 | 1245 | window.WAPI.base64ImageToFile = function (b64Data, filename) { 1246 | var arr = b64Data.split(','); 1247 | var mime = arr[0].match(/:(.*?);/)[1]; 1248 | var bstr = atob(arr[1]); 1249 | var n = bstr.length; 1250 | var u8arr = new Uint8Array(n); 1251 | 1252 | while (n--) { 1253 | u8arr[n] = bstr.charCodeAt(n); 1254 | } 1255 | 1256 | return new File([u8arr], filename, {type: mime}); 1257 | }; 1258 | 1259 | /** 1260 | * Send contact card to a specific chat using the chat ids 1261 | * 1262 | * @param {string} to '000000000000@c.us' 1263 | * @param {string|array} contact '111111111111@c.us' | ['222222222222@c.us', '333333333333@c.us, ... 'nnnnnnnnnnnn@c.us'] 1264 | */ 1265 | window.WAPI.sendContact = function (to, contact) { 1266 | if (!Array.isArray(contact)) { 1267 | contact = [contact]; 1268 | } 1269 | contact = contact.map((c) => { 1270 | return WAPI.getChat(c).__x_contact; 1271 | }); 1272 | 1273 | if (contact.length > 1) { 1274 | window.WAPI.getChat(to).sendContactList(contact); 1275 | } else if (contact.length === 1) { 1276 | window.WAPI.getChat(to).sendContact(contact[0]); 1277 | } 1278 | }; 1279 | 1280 | /** 1281 | * Create an chat ID based in a cloned one 1282 | * 1283 | * @param {string} chatId '000000000000@c.us' 1284 | */ 1285 | window.WAPI.getNewMessageId = function (chatId) { 1286 | var newMsgId = Store.Msg.models[0].__x_id.clone(); 1287 | 1288 | newMsgId.fromMe = true; 1289 | newMsgId.id = WAPI.getNewId().toUpperCase(); 1290 | newMsgId.remote = chatId; 1291 | newMsgId._serialized = `${newMsgId.fromMe}_${newMsgId.remote}_${newMsgId.id}` 1292 | 1293 | return newMsgId; 1294 | }; 1295 | 1296 | /** 1297 | * Send Customized VCard without the necessity of contact be a Whatsapp Contact 1298 | * 1299 | * @param {string} chatId '000000000000@c.us' 1300 | * @param {object|array} vcard { displayName: 'Contact Name', vcard: 'BEGIN:VCARD\nVERSION:3.0\nN:;Contact Name;;;\nEND:VCARD' } | [{ displayName: 'Contact Name 1', vcard: 'BEGIN:VCARD\nVERSION:3.0\nN:;Contact Name 1;;;\nEND:VCARD' }, { displayName: 'Contact Name 2', vcard: 'BEGIN:VCARD\nVERSION:3.0\nN:;Contact Name 2;;;\nEND:VCARD' }] 1301 | */ 1302 | window.WAPI.sendVCard = function (chatId, vcard) { 1303 | var chat = Store.Chat.get(chatId); 1304 | var tempMsg = Object.create(Store.Msg.models.filter(msg => msg.__x_isSentByMe)[0]); 1305 | var newId = window.WAPI.getNewMessageId(chatId); 1306 | 1307 | var extend = { 1308 | ack : 0, 1309 | id : newId, 1310 | local : !0, 1311 | self : "out", 1312 | t : parseInt(new Date().getTime() / 1000), 1313 | to : chatId, 1314 | isNewMsg: !0, 1315 | }; 1316 | 1317 | if (Array.isArray(vcard)) { 1318 | Object.assign(extend, { 1319 | type : "multi_vcard", 1320 | vcardList: vcard 1321 | }); 1322 | 1323 | delete extend.body; 1324 | } else { 1325 | Object.assign(extend, { 1326 | type : "vcard", 1327 | subtype: vcard.displayName, 1328 | body : vcard.vcard 1329 | }); 1330 | 1331 | delete extend.vcardList; 1332 | } 1333 | 1334 | Object.assign(tempMsg, extend); 1335 | 1336 | chat.addAndSendMsg(tempMsg); 1337 | }; 1338 | /** 1339 | * Block contact 1340 | * @param {string} id '000000000000@c.us' 1341 | * @param {*} done - function - Callback function to be called when a new message arrives. 1342 | */ 1343 | window.WAPI.contactBlock = function (id, done) { 1344 | const contact = window.Store.Contact.get(id); 1345 | if (contact !== undefined) { 1346 | contact.setBlock(!0); 1347 | done(true); 1348 | return true; 1349 | } 1350 | done(false); 1351 | return false; 1352 | } 1353 | /** 1354 | * unBlock contact 1355 | * @param {string} id '000000000000@c.us' 1356 | * @param {*} done - function - Callback function to be called when a new message arrives. 1357 | */ 1358 | window.WAPI.contactUnblock = function (id, done) { 1359 | const contact = window.Store.Contact.get(id); 1360 | if (contact !== undefined) { 1361 | contact.setBlock(!1); 1362 | done(true); 1363 | return true; 1364 | } 1365 | done(false); 1366 | return false; 1367 | } 1368 | 1369 | /** 1370 | * Remove participant of Group 1371 | * @param {*} idGroup '0000000000-00000000@g.us' 1372 | * @param {*} idParticipant '000000000000@c.us' 1373 | * @param {*} done - function - Callback function to be called when a new message arrives. 1374 | */ 1375 | window.WAPI.removeParticipantGroup = function (idGroup, idParticipant, done) { 1376 | window.Store.WapQuery.removeParticipants(idGroup, [idParticipant]).then(() => { 1377 | const metaDataGroup = window.Store.GroupMetadata.get(id) 1378 | checkParticipant = metaDataGroup.participants._index[idParticipant]; 1379 | if (checkParticipant === undefined) { 1380 | done(true); return true; 1381 | } 1382 | }) 1383 | } 1384 | 1385 | /** 1386 | * Promote Participant to Admin in Group 1387 | * @param {*} idGroup '0000000000-00000000@g.us' 1388 | * @param {*} idParticipant '000000000000@c.us' 1389 | * @param {*} done - function - Callback function to be called when a new message arrives. 1390 | */ 1391 | window.WAPI.promoteParticipantAdminGroup = function (idGroup, idParticipant, done) { 1392 | window.Store.WapQuery.promoteParticipants(idGroup, [idParticipant]).then(() => { 1393 | const metaDataGroup = window.Store.GroupMetadata.get(id) 1394 | checkParticipant = metaDataGroup.participants._index[idParticipant]; 1395 | if (checkParticipant !== undefined && checkParticipant.isAdmin) { 1396 | done(true); return true; 1397 | } 1398 | done(false); return false; 1399 | }) 1400 | } 1401 | 1402 | /** 1403 | * Demote Admin of Group 1404 | * @param {*} idGroup '0000000000-00000000@g.us' 1405 | * @param {*} idParticipant '000000000000@c.us' 1406 | * @param {*} done - function - Callback function to be called when a new message arrives. 1407 | */ 1408 | window.WAPI.demoteParticipantAdminGroup = function (idGroup, idParticipant, done) { 1409 | window.Store.WapQuery.demoteParticipants(idGroup, [idParticipant]).then(() => { 1410 | const metaDataGroup = window.Store.GroupMetadata.get(id) 1411 | if (metaDataGroup === undefined) { 1412 | done(false); return false; 1413 | } 1414 | checkParticipant = metaDataGroup.participants._index[idParticipant]; 1415 | if (checkParticipant !== undefined && checkParticipant.isAdmin) { 1416 | done(false); return false; 1417 | } 1418 | done(true); return true; 1419 | }) 1420 | } 1421 | 1422 | 1423 | /* code from Mikkel-era 1424 | * NOTE: Updated/fixed after merge with upstream etc. 1425 | */ 1426 | 1427 | window.WAPI.getAllMessagesAfter = function(unixTimestamp, includeMe, includeNotifications, done) { 1428 | let messageObjs = window.Store.Msg.models.filter((msg) => msg.__x_t > unixTimestamp); 1429 | var output = []; 1430 | for (const i in messageObjs) { 1431 | if (i === "remove") 1432 | continue; 1433 | const messageObj = messageObjs[i]; 1434 | let message = WAPI.processMessageObj(messageObj, includeMe, includeNotifications); 1435 | if (message) 1436 | output.push(message); 1437 | } 1438 | if (done !== undefined) { 1439 | done(output); 1440 | } 1441 | return output 1442 | }; --------------------------------------------------------------------------------