├── .gitignore ├── README.md ├── config ├── config.json ├── proxies.txt ├── sites.txt └── webhooks.txt ├── package-lock.json ├── package.json ├── src ├── class │ └── monitor.js └── index.js └── utils ├── tools.js └── webhook.js /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/node 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=node 4 | 5 | ### Node ### 6 | # Logs 7 | logs 8 | *.log 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | lerna-debug.log* 13 | 14 | # Diagnostic reports (https://nodejs.org/api/report.html) 15 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 16 | 17 | # Runtime data 18 | pids 19 | *.pid 20 | *.seed 21 | *.pid.lock 22 | 23 | # Directory for instrumented libs generated by jscoverage/JSCover 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | coverage 28 | *.lcov 29 | 30 | # nyc test coverage 31 | .nyc_output 32 | 33 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 34 | .grunt 35 | 36 | # Bower dependency directory (https://bower.io/) 37 | bower_components 38 | 39 | # node-waf configuration 40 | .lock-wscript 41 | 42 | # Compiled binary addons (https://nodejs.org/api/addons.html) 43 | build/Release 44 | 45 | # Dependency directories 46 | node_modules/ 47 | jspm_packages/ 48 | 49 | # TypeScript v1 declaration files 50 | typings/ 51 | 52 | # TypeScript cache 53 | *.tsbuildinfo 54 | 55 | # Optional npm cache directory 56 | .npm 57 | 58 | # Optional eslint cache 59 | .eslintcache 60 | 61 | # Microbundle cache 62 | .rpt2_cache/ 63 | .rts2_cache_cjs/ 64 | .rts2_cache_es/ 65 | .rts2_cache_umd/ 66 | 67 | # Optional REPL history 68 | .node_repl_history 69 | 70 | # Output of 'npm pack' 71 | *.tgz 72 | 73 | # Yarn Integrity file 74 | .yarn-integrity 75 | 76 | # dotenv environment variables file 77 | .env 78 | .env.test 79 | 80 | # parcel-bundler cache (https://parceljs.org/) 81 | .cache 82 | 83 | # Next.js build output 84 | .next 85 | 86 | # Nuxt.js build / generate output 87 | .nuxt 88 | dist 89 | 90 | # Gatsby files 91 | .cache/ 92 | # Comment in the public line in if your project uses Gatsby and not Next.js 93 | # https://nextjs.org/blog/next-9-1#public-directory-support 94 | # public 95 | 96 | # vuepress build output 97 | .vuepress/dist 98 | 99 | # Serverless directories 100 | .serverless/ 101 | 102 | # FuseBox cache 103 | .fusebox/ 104 | 105 | # DynamoDB Local files 106 | .dynamodb/ 107 | 108 | # TernJS port file 109 | .tern-port 110 | 111 | # Stores VSCode versions used for testing VSCode extensions 112 | .vscode-test 113 | 114 | # End of https://www.toptal.com/developers/gitignore/api/node -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Shopify Monitor 2 | Hello, I am [Rock](https://www.twitter.com/rockkdev) from the team at [StormeIO](https://www.twitter.com/stormeio) and I have decided to open-source a project of mine. It scans and waits for any changes on any Shopify site. Any product added/restocked will be sent to a webhook provided in the *config.json* file. I kindly ask you to not steal the code and rebrand it, at minimum keep the footer message. 3 | 4 | ## Example Images 5 | 6 | ![Unable to Load Preview.](https://i.imgur.com/qO9RBAu.png) 7 | ![Unable to Load Preview.](https://i.imgur.com/ZC0oMB2.png) 8 | 9 | 10 | ## Installation & Usage 11 | 12 | > **How do I setup and use this thing?** 13 | > 14 | > 1. First, you must install NodeJS, which can be installed here (pick LTS): 15 | >> https://nodejs.org/en/download/ 16 | > 17 | > 2. Next install Git from here: 18 | >> https://git-scm.com/downloads 19 | > 20 | > 3. Next, you need to open your terminal/command prompt. On windows press *Windows Key* and then type `cmd` and click enter. On Mac open Terminal *(its pre-installed)*. Now type this: 21 | >> `git clone https://github.com/aarock1234/shopify-monitor` 22 | > 23 | > 4. After it has completed you must type this: 24 | >> `cd shopify-monitor` 25 | > 26 | > 5. Now you can open up the file explorer using the command `explorer` on Windows and `open .` on a Mac *(don't forget the `.` after open)*. 27 | > 6. Now navigate to the user folder. 28 | > 7. Now you must navigate to the config folder and add proxies into the *proxies.txt* file, edit the Discord webhook URL inside of *webhooks.txt*, add your sites in *sites.txt*, and finally edit your delay in *config.json*. You can edit this with any standard text editor. 29 | > 8. Now that everything is set up go back to your command prompt/terminal and type this: 30 | >> `npm install` 31 | > 32 | > 9. Now, wait for it to install completely. 33 | > 10. Now you can type this command: 34 | >> `npm start` 35 | > 36 | > 🎉 DONE! 🥳 You are now monitoring Shopify. Please report any issues in the issues tab. 37 | 38 | ## Extra 39 | 40 | ### Planned Additions 41 | * Filter for keywords 42 | * Support for user pinging based on keywords 43 | * Slack webhook support 44 | 45 | ### Development 46 | 47 | This is really easy to edit if you are proficient in NodeJS. I encourage any devs to make a fork and add any features they would like. -------------------------------------------------------------------------------- /config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "delay": 2000 3 | } -------------------------------------------------------------------------------- /config/proxies.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aarock1234/shopify-monitor/186c4bdd2fda7a91a8412c10047cf6c1a48073f5/config/proxies.txt -------------------------------------------------------------------------------- /config/sites.txt: -------------------------------------------------------------------------------- 1 | https://www.kith.com/ 2 | https://www.xhibition.co/ 3 | https://wishatl.com/ 4 | https://www.dtlr.com/ 5 | https://sneakerpolitics.com/ 6 | https://kawsone.com/ 7 | https://nrml.ca/ 8 | https://hlorenzo.com/ 9 | https://checkout.funko.com/ 10 | https://privatesneakers.com/ 11 | https://www.topsandbottomsusa.com/ 12 | https://www.jimmyjazz.com/ 13 | https://www.saintalfred.com/ 14 | https://store.unionlosangeles.com/ 15 | https://shop.havenshop.com/ 16 | https://fearofgod.com/ 17 | https://www.apbstore.com/ 18 | https://www.a-ma-maniere.com/ 19 | https://burnrubbersneakers.com/ 20 | https://undefeated.com/ 21 | https://noirfonce.eu/ 22 | https://www.stussy.com/ -------------------------------------------------------------------------------- /config/webhooks.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aarock1234/shopify-monitor/186c4bdd2fda7a91a8412c10047cf6c1a48073f5/config/webhooks.txt -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shopify-monitor", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ajv": { 8 | "version": "6.12.6", 9 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 10 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 11 | "requires": { 12 | "fast-deep-equal": "^3.1.1", 13 | "fast-json-stable-stringify": "^2.0.0", 14 | "json-schema-traverse": "^0.4.1", 15 | "uri-js": "^4.2.2" 16 | } 17 | }, 18 | "ansi-regex": { 19 | "version": "2.1.1", 20 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 21 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" 22 | }, 23 | "ansi-styles": { 24 | "version": "2.2.1", 25 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", 26 | "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" 27 | }, 28 | "array-find-index": { 29 | "version": "1.0.2", 30 | "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", 31 | "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=" 32 | }, 33 | "asn1": { 34 | "version": "0.2.4", 35 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", 36 | "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", 37 | "requires": { 38 | "safer-buffer": "~2.1.0" 39 | } 40 | }, 41 | "assert-plus": { 42 | "version": "1.0.0", 43 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 44 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 45 | }, 46 | "asynckit": { 47 | "version": "0.4.0", 48 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 49 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" 50 | }, 51 | "aws-sign2": { 52 | "version": "0.7.0", 53 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", 54 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" 55 | }, 56 | "aws4": { 57 | "version": "1.11.0", 58 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", 59 | "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" 60 | }, 61 | "bcrypt-pbkdf": { 62 | "version": "1.0.2", 63 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", 64 | "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", 65 | "requires": { 66 | "tweetnacl": "^0.14.3" 67 | } 68 | }, 69 | "bluebird": { 70 | "version": "3.7.2", 71 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", 72 | "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" 73 | }, 74 | "camelcase": { 75 | "version": "2.1.1", 76 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", 77 | "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" 78 | }, 79 | "camelcase-keys": { 80 | "version": "2.1.0", 81 | "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", 82 | "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", 83 | "requires": { 84 | "camelcase": "^2.0.0", 85 | "map-obj": "^1.0.0" 86 | } 87 | }, 88 | "caseless": { 89 | "version": "0.12.0", 90 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", 91 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" 92 | }, 93 | "chalk": { 94 | "version": "1.1.3", 95 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", 96 | "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", 97 | "requires": { 98 | "ansi-styles": "^2.2.1", 99 | "escape-string-regexp": "^1.0.2", 100 | "has-ansi": "^2.0.0", 101 | "strip-ansi": "^3.0.0", 102 | "supports-color": "^2.0.0" 103 | } 104 | }, 105 | "combined-stream": { 106 | "version": "1.0.8", 107 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 108 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 109 | "requires": { 110 | "delayed-stream": "~1.0.0" 111 | } 112 | }, 113 | "console-stamp": { 114 | "version": "0.2.9", 115 | "resolved": "https://registry.npmjs.org/console-stamp/-/console-stamp-0.2.9.tgz", 116 | "integrity": "sha512-jtgd1Fx3Im+pWN54mF269ptunkzF5Lpct2LBTbtyNoK2A4XjcxLM+TQW+e+XE/bLwLQNGRqPqlxm9JMixFntRA==", 117 | "requires": { 118 | "chalk": "^1.1.1", 119 | "dateformat": "^1.0.11", 120 | "merge": "^1.2.0" 121 | } 122 | }, 123 | "core-util-is": { 124 | "version": "1.0.2", 125 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 126 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 127 | }, 128 | "currently-unhandled": { 129 | "version": "0.4.1", 130 | "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", 131 | "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", 132 | "requires": { 133 | "array-find-index": "^1.0.1" 134 | } 135 | }, 136 | "dashdash": { 137 | "version": "1.14.1", 138 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 139 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", 140 | "requires": { 141 | "assert-plus": "^1.0.0" 142 | } 143 | }, 144 | "dateformat": { 145 | "version": "1.0.12", 146 | "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", 147 | "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", 148 | "requires": { 149 | "get-stdin": "^4.0.1", 150 | "meow": "^3.3.0" 151 | } 152 | }, 153 | "decamelize": { 154 | "version": "1.2.0", 155 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 156 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" 157 | }, 158 | "delayed-stream": { 159 | "version": "1.0.0", 160 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 161 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" 162 | }, 163 | "ecc-jsbn": { 164 | "version": "0.1.2", 165 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", 166 | "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", 167 | "requires": { 168 | "jsbn": "~0.1.0", 169 | "safer-buffer": "^2.1.0" 170 | } 171 | }, 172 | "error-ex": { 173 | "version": "1.3.2", 174 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", 175 | "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", 176 | "requires": { 177 | "is-arrayish": "^0.2.1" 178 | } 179 | }, 180 | "escape-string-regexp": { 181 | "version": "1.0.5", 182 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 183 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 184 | }, 185 | "events": { 186 | "version": "3.2.0", 187 | "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", 188 | "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==" 189 | }, 190 | "extend": { 191 | "version": "3.0.2", 192 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 193 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" 194 | }, 195 | "extsprintf": { 196 | "version": "1.3.0", 197 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", 198 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" 199 | }, 200 | "fast-deep-equal": { 201 | "version": "3.1.3", 202 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 203 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" 204 | }, 205 | "fast-json-stable-stringify": { 206 | "version": "2.1.0", 207 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 208 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" 209 | }, 210 | "find-up": { 211 | "version": "1.1.2", 212 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", 213 | "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", 214 | "requires": { 215 | "path-exists": "^2.0.0", 216 | "pinkie-promise": "^2.0.0" 217 | } 218 | }, 219 | "forever-agent": { 220 | "version": "0.6.1", 221 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", 222 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" 223 | }, 224 | "form-data": { 225 | "version": "2.3.3", 226 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", 227 | "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", 228 | "requires": { 229 | "asynckit": "^0.4.0", 230 | "combined-stream": "^1.0.6", 231 | "mime-types": "^2.1.12" 232 | } 233 | }, 234 | "fs": { 235 | "version": "0.0.1-security", 236 | "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", 237 | "integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ=" 238 | }, 239 | "function-bind": { 240 | "version": "1.1.1", 241 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 242 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" 243 | }, 244 | "get-stdin": { 245 | "version": "4.0.1", 246 | "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", 247 | "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=" 248 | }, 249 | "getpass": { 250 | "version": "0.1.7", 251 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", 252 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", 253 | "requires": { 254 | "assert-plus": "^1.0.0" 255 | } 256 | }, 257 | "graceful-fs": { 258 | "version": "4.2.4", 259 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", 260 | "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" 261 | }, 262 | "har-schema": { 263 | "version": "2.0.0", 264 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", 265 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" 266 | }, 267 | "har-validator": { 268 | "version": "5.1.5", 269 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", 270 | "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", 271 | "requires": { 272 | "ajv": "^6.12.3", 273 | "har-schema": "^2.0.0" 274 | } 275 | }, 276 | "has": { 277 | "version": "1.0.3", 278 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 279 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 280 | "requires": { 281 | "function-bind": "^1.1.1" 282 | } 283 | }, 284 | "has-ansi": { 285 | "version": "2.0.0", 286 | "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", 287 | "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", 288 | "requires": { 289 | "ansi-regex": "^2.0.0" 290 | } 291 | }, 292 | "hosted-git-info": { 293 | "version": "2.8.9", 294 | "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", 295 | "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" 296 | }, 297 | "http-signature": { 298 | "version": "1.2.0", 299 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", 300 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", 301 | "requires": { 302 | "assert-plus": "^1.0.0", 303 | "jsprim": "^1.2.2", 304 | "sshpk": "^1.7.0" 305 | } 306 | }, 307 | "indent-string": { 308 | "version": "2.1.0", 309 | "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", 310 | "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", 311 | "requires": { 312 | "repeating": "^2.0.0" 313 | } 314 | }, 315 | "is-arrayish": { 316 | "version": "0.2.1", 317 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", 318 | "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" 319 | }, 320 | "is-core-module": { 321 | "version": "2.1.0", 322 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz", 323 | "integrity": "sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==", 324 | "requires": { 325 | "has": "^1.0.3" 326 | } 327 | }, 328 | "is-finite": { 329 | "version": "1.1.0", 330 | "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", 331 | "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==" 332 | }, 333 | "is-typedarray": { 334 | "version": "1.0.0", 335 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 336 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" 337 | }, 338 | "is-utf8": { 339 | "version": "0.2.1", 340 | "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", 341 | "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" 342 | }, 343 | "isstream": { 344 | "version": "0.1.2", 345 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 346 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" 347 | }, 348 | "jsbn": { 349 | "version": "0.1.1", 350 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 351 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" 352 | }, 353 | "json-schema": { 354 | "version": "0.2.3", 355 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", 356 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" 357 | }, 358 | "json-schema-traverse": { 359 | "version": "0.4.1", 360 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 361 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" 362 | }, 363 | "json-stringify-safe": { 364 | "version": "5.0.1", 365 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 366 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" 367 | }, 368 | "jsprim": { 369 | "version": "1.4.1", 370 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", 371 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", 372 | "requires": { 373 | "assert-plus": "1.0.0", 374 | "extsprintf": "1.3.0", 375 | "json-schema": "0.2.3", 376 | "verror": "1.10.0" 377 | } 378 | }, 379 | "load-json-file": { 380 | "version": "1.1.0", 381 | "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", 382 | "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", 383 | "requires": { 384 | "graceful-fs": "^4.1.2", 385 | "parse-json": "^2.2.0", 386 | "pify": "^2.0.0", 387 | "pinkie-promise": "^2.0.0", 388 | "strip-bom": "^2.0.0" 389 | } 390 | }, 391 | "lodash": { 392 | "version": "4.17.21", 393 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 394 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 395 | }, 396 | "loud-rejection": { 397 | "version": "1.6.0", 398 | "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", 399 | "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", 400 | "requires": { 401 | "currently-unhandled": "^0.4.1", 402 | "signal-exit": "^3.0.0" 403 | } 404 | }, 405 | "map-obj": { 406 | "version": "1.0.1", 407 | "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", 408 | "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=" 409 | }, 410 | "meow": { 411 | "version": "3.7.0", 412 | "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", 413 | "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", 414 | "requires": { 415 | "camelcase-keys": "^2.0.0", 416 | "decamelize": "^1.1.2", 417 | "loud-rejection": "^1.0.0", 418 | "map-obj": "^1.0.1", 419 | "minimist": "^1.1.3", 420 | "normalize-package-data": "^2.3.4", 421 | "object-assign": "^4.0.1", 422 | "read-pkg-up": "^1.0.1", 423 | "redent": "^1.0.0", 424 | "trim-newlines": "^1.0.0" 425 | } 426 | }, 427 | "merge": { 428 | "version": "1.2.1", 429 | "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz", 430 | "integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==" 431 | }, 432 | "mime-db": { 433 | "version": "1.44.0", 434 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", 435 | "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" 436 | }, 437 | "mime-types": { 438 | "version": "2.1.27", 439 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", 440 | "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", 441 | "requires": { 442 | "mime-db": "1.44.0" 443 | } 444 | }, 445 | "minimist": { 446 | "version": "1.2.5", 447 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 448 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" 449 | }, 450 | "normalize-package-data": { 451 | "version": "2.5.0", 452 | "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", 453 | "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", 454 | "requires": { 455 | "hosted-git-info": "^2.1.4", 456 | "resolve": "^1.10.0", 457 | "semver": "2 || 3 || 4 || 5", 458 | "validate-npm-package-license": "^3.0.1" 459 | } 460 | }, 461 | "oauth-sign": { 462 | "version": "0.9.0", 463 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", 464 | "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" 465 | }, 466 | "object-assign": { 467 | "version": "4.1.1", 468 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 469 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 470 | }, 471 | "parse-json": { 472 | "version": "2.2.0", 473 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", 474 | "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", 475 | "requires": { 476 | "error-ex": "^1.2.0" 477 | } 478 | }, 479 | "path-exists": { 480 | "version": "2.1.0", 481 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", 482 | "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", 483 | "requires": { 484 | "pinkie-promise": "^2.0.0" 485 | } 486 | }, 487 | "path-parse": { 488 | "version": "1.0.6", 489 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 490 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" 491 | }, 492 | "path-type": { 493 | "version": "1.1.0", 494 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", 495 | "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", 496 | "requires": { 497 | "graceful-fs": "^4.1.2", 498 | "pify": "^2.0.0", 499 | "pinkie-promise": "^2.0.0" 500 | } 501 | }, 502 | "performance-now": { 503 | "version": "2.1.0", 504 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", 505 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" 506 | }, 507 | "pify": { 508 | "version": "2.3.0", 509 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 510 | "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" 511 | }, 512 | "pinkie": { 513 | "version": "2.0.4", 514 | "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", 515 | "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" 516 | }, 517 | "pinkie-promise": { 518 | "version": "2.0.1", 519 | "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", 520 | "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", 521 | "requires": { 522 | "pinkie": "^2.0.0" 523 | } 524 | }, 525 | "psl": { 526 | "version": "1.8.0", 527 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", 528 | "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" 529 | }, 530 | "punycode": { 531 | "version": "2.1.1", 532 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 533 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" 534 | }, 535 | "qs": { 536 | "version": "6.5.2", 537 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 538 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" 539 | }, 540 | "querystring": { 541 | "version": "0.2.0", 542 | "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", 543 | "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" 544 | }, 545 | "read-pkg": { 546 | "version": "1.1.0", 547 | "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", 548 | "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", 549 | "requires": { 550 | "load-json-file": "^1.0.0", 551 | "normalize-package-data": "^2.3.2", 552 | "path-type": "^1.0.0" 553 | } 554 | }, 555 | "read-pkg-up": { 556 | "version": "1.0.1", 557 | "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", 558 | "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", 559 | "requires": { 560 | "find-up": "^1.0.0", 561 | "read-pkg": "^1.0.0" 562 | } 563 | }, 564 | "redent": { 565 | "version": "1.0.0", 566 | "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", 567 | "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", 568 | "requires": { 569 | "indent-string": "^2.1.0", 570 | "strip-indent": "^1.0.1" 571 | } 572 | }, 573 | "repeating": { 574 | "version": "2.0.1", 575 | "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", 576 | "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", 577 | "requires": { 578 | "is-finite": "^1.0.0" 579 | } 580 | }, 581 | "request": { 582 | "version": "2.88.2", 583 | "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", 584 | "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", 585 | "requires": { 586 | "aws-sign2": "~0.7.0", 587 | "aws4": "^1.8.0", 588 | "caseless": "~0.12.0", 589 | "combined-stream": "~1.0.6", 590 | "extend": "~3.0.2", 591 | "forever-agent": "~0.6.1", 592 | "form-data": "~2.3.2", 593 | "har-validator": "~5.1.3", 594 | "http-signature": "~1.2.0", 595 | "is-typedarray": "~1.0.0", 596 | "isstream": "~0.1.2", 597 | "json-stringify-safe": "~5.0.1", 598 | "mime-types": "~2.1.19", 599 | "oauth-sign": "~0.9.0", 600 | "performance-now": "^2.1.0", 601 | "qs": "~6.5.2", 602 | "safe-buffer": "^5.1.2", 603 | "tough-cookie": "~2.5.0", 604 | "tunnel-agent": "^0.6.0", 605 | "uuid": "^3.3.2" 606 | } 607 | }, 608 | "request-promise": { 609 | "version": "4.2.6", 610 | "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.6.tgz", 611 | "integrity": "sha512-HCHI3DJJUakkOr8fNoCc73E5nU5bqITjOYFMDrKHYOXWXrgD/SBaC7LjwuPymUprRyuF06UK7hd/lMHkmUXglQ==", 612 | "requires": { 613 | "bluebird": "^3.5.0", 614 | "request-promise-core": "1.1.4", 615 | "stealthy-require": "^1.1.1", 616 | "tough-cookie": "^2.3.3" 617 | } 618 | }, 619 | "request-promise-core": { 620 | "version": "1.1.4", 621 | "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", 622 | "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", 623 | "requires": { 624 | "lodash": "^4.17.19" 625 | } 626 | }, 627 | "resolve": { 628 | "version": "1.19.0", 629 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", 630 | "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", 631 | "requires": { 632 | "is-core-module": "^2.1.0", 633 | "path-parse": "^1.0.6" 634 | } 635 | }, 636 | "safe-buffer": { 637 | "version": "5.2.1", 638 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 639 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 640 | }, 641 | "safer-buffer": { 642 | "version": "2.1.2", 643 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 644 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 645 | }, 646 | "semver": { 647 | "version": "5.7.1", 648 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 649 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" 650 | }, 651 | "signal-exit": { 652 | "version": "3.0.3", 653 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", 654 | "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" 655 | }, 656 | "spdx-correct": { 657 | "version": "3.1.1", 658 | "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", 659 | "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", 660 | "requires": { 661 | "spdx-expression-parse": "^3.0.0", 662 | "spdx-license-ids": "^3.0.0" 663 | } 664 | }, 665 | "spdx-exceptions": { 666 | "version": "2.3.0", 667 | "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", 668 | "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" 669 | }, 670 | "spdx-expression-parse": { 671 | "version": "3.0.1", 672 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", 673 | "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", 674 | "requires": { 675 | "spdx-exceptions": "^2.1.0", 676 | "spdx-license-ids": "^3.0.0" 677 | } 678 | }, 679 | "spdx-license-ids": { 680 | "version": "3.0.6", 681 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz", 682 | "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==" 683 | }, 684 | "sshpk": { 685 | "version": "1.16.1", 686 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", 687 | "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", 688 | "requires": { 689 | "asn1": "~0.2.3", 690 | "assert-plus": "^1.0.0", 691 | "bcrypt-pbkdf": "^1.0.0", 692 | "dashdash": "^1.12.0", 693 | "ecc-jsbn": "~0.1.1", 694 | "getpass": "^0.1.1", 695 | "jsbn": "~0.1.0", 696 | "safer-buffer": "^2.0.2", 697 | "tweetnacl": "~0.14.0" 698 | } 699 | }, 700 | "stealthy-require": { 701 | "version": "1.1.1", 702 | "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", 703 | "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" 704 | }, 705 | "strip-ansi": { 706 | "version": "3.0.1", 707 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 708 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 709 | "requires": { 710 | "ansi-regex": "^2.0.0" 711 | } 712 | }, 713 | "strip-bom": { 714 | "version": "2.0.0", 715 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", 716 | "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", 717 | "requires": { 718 | "is-utf8": "^0.2.0" 719 | } 720 | }, 721 | "strip-indent": { 722 | "version": "1.0.1", 723 | "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", 724 | "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", 725 | "requires": { 726 | "get-stdin": "^4.0.1" 727 | } 728 | }, 729 | "supports-color": { 730 | "version": "2.0.0", 731 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", 732 | "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" 733 | }, 734 | "tough-cookie": { 735 | "version": "2.5.0", 736 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", 737 | "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", 738 | "requires": { 739 | "psl": "^1.1.28", 740 | "punycode": "^2.1.1" 741 | } 742 | }, 743 | "trim-newlines": { 744 | "version": "1.0.0", 745 | "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", 746 | "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=" 747 | }, 748 | "tunnel-agent": { 749 | "version": "0.6.0", 750 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 751 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 752 | "requires": { 753 | "safe-buffer": "^5.0.1" 754 | } 755 | }, 756 | "tweetnacl": { 757 | "version": "0.14.5", 758 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 759 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" 760 | }, 761 | "uri-js": { 762 | "version": "4.4.0", 763 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", 764 | "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", 765 | "requires": { 766 | "punycode": "^2.1.0" 767 | } 768 | }, 769 | "url": { 770 | "version": "0.11.0", 771 | "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", 772 | "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", 773 | "requires": { 774 | "punycode": "1.3.2", 775 | "querystring": "0.2.0" 776 | }, 777 | "dependencies": { 778 | "punycode": { 779 | "version": "1.3.2", 780 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", 781 | "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" 782 | } 783 | } 784 | }, 785 | "uuid": { 786 | "version": "3.4.0", 787 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", 788 | "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" 789 | }, 790 | "validate-npm-package-license": { 791 | "version": "3.0.4", 792 | "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", 793 | "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", 794 | "requires": { 795 | "spdx-correct": "^3.0.0", 796 | "spdx-expression-parse": "^3.0.0" 797 | } 798 | }, 799 | "verror": { 800 | "version": "1.10.0", 801 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", 802 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", 803 | "requires": { 804 | "assert-plus": "^1.0.0", 805 | "core-util-is": "1.0.2", 806 | "extsprintf": "^1.2.0" 807 | } 808 | } 809 | } 810 | } 811 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shopify-monitor", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node src/index.js" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "console-stamp": "^0.2.9", 14 | "events": "^3.2.0", 15 | "fs": "0.0.1-security", 16 | "request": "^2.88.2", 17 | "request-promise": "^4.2.6", 18 | "url": "^0.11.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/class/monitor.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36'; 4 | const safeHeaders = { 5 | 'pragma': 'no-cache', 6 | 'cache-control': 'no-cache', 7 | 'upgrade-insecure-requests': '1', 8 | 'user-agent': userAgent, 9 | 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', 10 | 'sec-fetch-site': 'none', 11 | 'sec-fetch-mode': 'navigate', 12 | 'sec-fetch-user': '?1', 13 | 'sec-fetch-dest': 'document', 14 | 'accept-language': 'en-US,en;q=0.9' 15 | }; 16 | 17 | const request = require('request-promise').defaults({ 18 | simple: false, 19 | gzip: true, 20 | resolveWithFullResponse: true, 21 | maxRedirects: 0, 22 | followRedirect: false, 23 | headers: safeHeaders 24 | }); 25 | const events = require('events'); 26 | require('console-stamp')(console, 'HH:MM:ss.l'); 27 | 28 | const { 29 | sleep, 30 | formatProxy, 31 | getRandomArbitrary 32 | } = require('../../utils/tools.js'); 33 | const config = require('../../config/config.json'); 34 | 35 | class Monitor extends events { 36 | constructor(props) { 37 | super(); 38 | 39 | Object.keys(props).forEach((key) => this[key] = props[key]); 40 | 41 | this.previousProducts = []; 42 | this.currentProducts = []; 43 | 44 | this.site = new URL(this.site).origin; 45 | 46 | setInterval(() => { 47 | console.log('60m Checkup: ' + this.site); 48 | }, 3600000); 49 | 50 | this.initMonitor(); 51 | } 52 | 53 | randomProxy = () => { 54 | return formatProxy(this.proxies[Math.floor(Math.random() * this.proxies.length)]); 55 | } 56 | 57 | initMonitor = async () => { 58 | let response; 59 | 60 | try { 61 | response = await request.get({ 62 | url: this.site + '/products.json', 63 | json: true, 64 | followRedirect: true, 65 | proxy: this.randomProxy(), 66 | qs: { 67 | limit: getRandomArbitrary(250, 9999) 68 | } 69 | }) 70 | 71 | if (response.statusCode == 401) { 72 | throw new Error('Password up on ' + this.site); 73 | } 74 | 75 | this.previousProducts = response.body.products; 76 | } catch (initError) { 77 | console.error(`INIT ERR @ ${this.site}: ${initError.message}`); 78 | await sleep(config.delay); 79 | return this.initMonitor(); 80 | } 81 | 82 | this.monitorLoop(1); 83 | } 84 | 85 | monitorLoop = async () => { 86 | let response; 87 | 88 | try { 89 | response = await request.get({ 90 | url: this.site + '/products.json', 91 | json: true, 92 | followRedirect: true, 93 | proxy: this.randomProxy(), 94 | qs: { 95 | limit: getRandomArbitrary(250, 9999) 96 | } 97 | }) 98 | 99 | if (response.statusCode == 401) { 100 | throw new Error('Password up on ' + this.site); 101 | } 102 | 103 | this.currentProducts = response.body.products; 104 | let _currentProducts = [ ...this.currentProducts ]; 105 | 106 | let matchedProductIndex, matchedProduct; 107 | 108 | this.previousProducts.forEach(product => { 109 | matchedProductIndex = this.currentProducts.findIndex((_product) => _product.id == product.id); 110 | matchedProduct = this.currentProducts[matchedProductIndex]; 111 | 112 | if (matchedProduct && product.updated_at != matchedProduct.updated_at) { 113 | this.checkRestocks(this.currentProducts[matchedProductIndex], product); 114 | } 115 | }); 116 | 117 | this.previousProducts.forEach(product => { 118 | matchedProductIndex = _currentProducts.findIndex((_product) => _product.id == product.id); 119 | matchedProduct = _currentProducts[matchedProductIndex]; 120 | if (matchedProduct) _currentProducts.splice(matchedProductIndex, 1); 121 | }) 122 | 123 | if (_currentProducts.length) { 124 | _currentProducts.forEach((product) => { 125 | let productDetails = { 126 | site: this.site, 127 | product: product, 128 | restockedVariants: product.variants 129 | } 130 | // @DEBUG: console.log(productDetails); 131 | this.emit('newProduct', productDetails); 132 | }) 133 | } 134 | 135 | this.previousProducts = [ ...this.currentProducts ]; 136 | } catch (monitorError) { 137 | console.error(`MON ERR @ ${this.site}: ${monitorError.message}`); 138 | await sleep(config.delay); 139 | return this.monitorLoop(); 140 | } 141 | 142 | await sleep(config.delay); 143 | return this.monitorLoop(); 144 | } 145 | 146 | checkRestocks = async (product, oldProduct) => { 147 | let restockDetails = { 148 | site: this.site, 149 | product, 150 | restockedVariants: [] 151 | } 152 | 153 | product.variants.forEach((variant) => { 154 | if (variant.available && !oldProduct.variants.find((_variant) => _variant.id == variant.id).available) { 155 | restockDetails.restockedVariants.push(variant); 156 | // @DEBUG: console.log(restockDetails.restockedVariants); 157 | } 158 | }) 159 | 160 | if (restockDetails.restockedVariants.length) { 161 | // @DEBUG: console.log(restockDetails); 162 | this.emit('restockedProduct', restockDetails); 163 | } 164 | } 165 | } 166 | 167 | module.exports = Monitor; -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const Monitor = require('./class/monitor.js'); 2 | const { sendWebhook } = require('../utils/webhook.js'); 3 | 4 | const fs = require('fs'); 5 | 6 | let proxies = [], 7 | sites = [], 8 | webhooks = []; 9 | 10 | fs.readFileSync(__dirname + '/../config/proxies.txt', 'utf-8') 11 | .split(/\r?\n/).forEach(line => proxies.push(line)); 12 | fs.readFileSync(__dirname + '/../config/sites.txt', 'utf-8') 13 | .split(/\r?\n/).forEach(line => sites.push(line)); 14 | fs.readFileSync(__dirname + '/../config/webhooks.txt', 'utf-8') 15 | .split(/\r?\n/).forEach(line => { 16 | line = line.replace(/\s/g, ''); 17 | if (line != '') webhooks.push(line); 18 | }); 19 | 20 | sites.forEach(site => { 21 | const currentMonitor = new Monitor({ 22 | site, 23 | proxies 24 | }); 25 | 26 | console.log('Monitor Started for ' + site); 27 | 28 | currentMonitor.on('newProduct', productDetails => { 29 | for (let i = 0; i < webhooks.length; i++) { 30 | sendWebhook(webhooks[i], 1305395, 'New Product', productDetails); 31 | } 32 | console.log('New Product @ ' + productDetails.site + ': ' + productDetails.product.title) 33 | }); 34 | 35 | currentMonitor.on('restockedProduct', restockDetails => { 36 | for (let i = 0; i < webhooks.length; i++) { 37 | sendWebhook(webhooks[i], 242172, 'Product Restock', restockDetails); 38 | } 39 | console.log('Restock @ ' + restockDetails.site + ': ' + restockDetails.product.title) 40 | }); 41 | }) -------------------------------------------------------------------------------- /utils/tools.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | /** 3 | * Sleep 4 | * @param {Number} ms 5 | * 6 | * @returns timeout 7 | */ 8 | sleep: (ms) => { 9 | return new Promise(resolve => { 10 | setTimeout(resolve, ms); 11 | }); 12 | }, 13 | 14 | /** 15 | * randomString 16 | * 17 | * @param { String } Len 18 | * @param { String } An 19 | * 20 | * @returns random string of desired length 21 | */ 22 | randomString: (len, an) => { 23 | an = an && an.toLowerCase(); 24 | var str = "", 25 | i = 0, 26 | min = an == "a" ? 10 : 0, 27 | max = an == "n" ? 10 : 62; 28 | while (len--) { 29 | var r = Math.random() * (max - min) + min << 0; 30 | str += String.fromCharCode(r += r > 9 ? r < 36 ? 55 : 61 : 48); 31 | } 32 | return str; 33 | }, 34 | 35 | /** 36 | * getRandom 37 | * 38 | * @param {Array} 39 | * 40 | * @returns random value from array 41 | */ 42 | getRandomArray: (array) => { 43 | return array[Math.floor(Math.random() * array.length)]; 44 | }, 45 | 46 | /** 47 | * formatProxy 48 | * 49 | * @param {String} Proxy 50 | * 51 | * @returns proxy in node format 52 | */ 53 | formatProxy: (proxy) => { 54 | if (!proxy || proxy.replace(/\s/g, '') == "") 55 | return; 56 | 57 | let proxySplit = proxy.split(":"); 58 | 59 | if (proxySplit.length > 2) { 60 | return 'http://' + proxySplit[2] + ':' + proxySplit[3] + '@' + proxySplit[0] + ':' + proxySplit[1]; 61 | } else { 62 | return 'http://' + proxySplit[0] + ':' + proxySplit[1]; 63 | }; 64 | }, 65 | 66 | /** 67 | * getRandomArbitrary 68 | * 69 | * @param {Number} 70 | * @param {Number} 71 | * 72 | * @returns random arbitrary number 73 | */ 74 | getRandomArbitrary: (min, max) => { 75 | /** 76 | * Returns a random arbitrary number between min (inclusive) and max (exclusive). 77 | */ 78 | 79 | return Math.round(Math.random() * (max - min) + min); 80 | }, 81 | 82 | formatKeywords: (keywords) => { 83 | if (keywords === null) 84 | return []; 85 | const lowerCaseKeywords = []; 86 | const array = keywords.split(","); 87 | array.forEach(async keyword => { 88 | if (keyword !== "" && keyword !== " " && keyword !== undefined) { 89 | lowerCaseKeywords.push(keyword.toLowerCase().trim()); 90 | }; 91 | }); 92 | return lowerCaseKeywords; 93 | }, 94 | 95 | searchKeywords: async (string, positive, negative) => { 96 | if (positive === undefined || string === undefined || negative === undefined) return false; 97 | const productName = string.toLowerCase().replace(/["'™®]/g, "").replace(/[/]/g, " "); 98 | for (let x = 0; x < negative.length; x++) { 99 | if (productName.indexOf(negative[x]) !== -1) return false; 100 | }; 101 | 102 | for (let x = 0; x < positive.length; x++) { 103 | if (productName.indexOf(positive[x]) === -1) return false; 104 | 105 | }; 106 | 107 | return true; 108 | } 109 | }; -------------------------------------------------------------------------------- /utils/webhook.js: -------------------------------------------------------------------------------- 1 | const request = require('request'); 2 | 3 | module.exports = { 4 | sendWebhook: async (webhookURL, color, title, productDetails) => { 5 | try { 6 | const embed = { 7 | embeds: [{ 8 | author: { 9 | name: `${title} @ ${productDetails.site}`, 10 | url: productDetails.site 11 | }, 12 | color: color, 13 | title: productDetails.product.title, 14 | url: `${productDetails.site}/products/${productDetails.product.handle}`, 15 | thumbnail: { 16 | "url": productDetails.product.images[0] ? productDetails.product.images[0].src : 'https://i.imgur.com/NO25iZV.png' 17 | }, 18 | footer: { 19 | icon_url: "https://cdn.iconscout.com/icon/free/png-256/shopify-226579.png", 20 | text: "Shopify Monitor by Rock @ StormeIO" 21 | }, 22 | type: 'rich', 23 | fields: productDetails.restockedVariants.map((variant) => { 24 | return { 25 | name: (variant.available) ? `${variant.title}: ${(variant.inventory_quantity) ? variant.inventory_quantity : '1+'} Stock` : `${variant.title}: OOS`, 26 | value: (variant.available) ? `[ATC](${productDetails.site}/cart/${variant.id}:1)` : `${variant.id}`, 27 | inline: true 28 | } 29 | }), 30 | timestamp: new Date().toISOString() 31 | }] 32 | } 33 | // @DEBUG: console.log(embed); 34 | 35 | request.post({ 36 | url: webhookURL, 37 | followAllRedirects: true, 38 | simple: false, 39 | resolveWithFullResponse: true, 40 | headers: { 41 | 'content-type': 'application/json', 42 | }, 43 | body: JSON.stringify(embed) 44 | }) 45 | } catch (webhookError) { 46 | console.error('WEBHOOK: ' + webhookError.message); 47 | await new Promise((resolve) => setTimeout(() => resolve(), 5000)); 48 | return module.exports.sendWebhook(webhookURL, color, title, productDetails); 49 | } 50 | } 51 | } --------------------------------------------------------------------------------