SHOW:
|
|
- or go back to the newest paste.
1 | const rp = require("request-promise") | |
2 | ||
3 | const getPookyUrl = (region) => { | |
4 | let usPooky = "https://pooky.dev:1337/gen?token=h20-44a81-s28dn-8ad9s" | |
5 | let euPooky = "https://pooky.dev:1337/gen?token=h20-44a81-s28dn-8ad9s®ion=eu" | |
6 | return region === 'SupremeUS' ? usPooky : euPooky | |
7 | } | |
8 | ||
9 | const iPhone6 = { | |
10 | 'name': 'iPhone 6', | |
11 | 'userAgent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1', | |
12 | 'viewport': { | |
13 | 'width': 375, | |
14 | 'height': 667, | |
15 | 'deviceScaleFactor': 2, | |
16 | 'isMobile': true, | |
17 | 'hasTouch': true, | |
18 | 'isLandscape': false | |
19 | } | |
20 | }; | |
21 | ||
22 | function sleep(ms) { | |
23 | return new Promise(resolve => { | |
24 | setTimeout(resolve, ms) | |
25 | }) | |
26 | } | |
27 | ||
28 | ||
29 | const encodeVariants = (variants) => { | |
30 | let varObj = {} | |
31 | variants.forEach(v => varObj[v] = 1) | |
32 | return encodeURIComponent(JSON.stringify(varObj).replace(/'/g, '"')) | |
33 | } | |
34 | ||
35 | const getCheckoutData = (region, variants) => { | |
36 | const usCheckoutData = { | |
37 | 'store_credit_id': '', | |
38 | 'from_mobile': '1', | |
39 | 'cookie-sub': encodeVariants(variants), | |
40 | 'same_as_billing_address': '1', | |
41 | 'scerkhaj': 'CKCRSUJHXH', | |
42 | 'order[bn]': "Daniel Young", | |
43 | 'order[billing_name]': "", | |
44 | 'order[email]': "[email protected]", | |
45 | 'order[tel]': "2155123271", | |
46 | 'order[billing_address]': "3561 Meadowlark Drive", | |
47 | 'order[billing_address_2]': "", | |
48 | 'order[billing_zip]': "19006", | |
49 | 'order[billing_city]': "Huntingdon Valley", | |
50 | 'order[billing_state]': "Pennsylvania", | |
51 | 'order[billing_country]': "United States", | |
52 | 'riearmxa': "4478874829206427", | |
53 | 'credit_card[month]': "02", | |
54 | 'credit_card[year]': "2020", | |
55 | 'rand': '', | |
56 | 'credit_card[meknk]': "717", | |
57 | 'order[terms]': 0, | |
58 | 'order[terms]': 1, | |
59 | 'g-recaptcha-response': "03AOLTBLRb_A0LF2utI5YYdYjkrIM2FZgxO4dLjQpwsGVSMuGAwdkK03VN-2Sq5r1G3BG6xFQeoxU-6ZWcwfsJaE-qrU4fFtlU2oyMb37_NtgPPUbsGE4rJxDRv3hKNZez1BTIQehk2taTXhFe9vtk0-9ro41486uTgk_9SxRSDMbpuU8ckeju1tXWzfNVetDOLEH66U7mROk3NpZYYUj8-A6wu7J9HAC1PGiVDlfDVAH8RmQQXEyYRQWSMfbsdeTN9OlypInmcRWHN59iXi9SieCdzH6Q1W35tOurTPzvU8LKHfjEUwN9b2wsMnato0zQlgui0sGTkCGPeXbQNTYcCiiZKwbjWuhckUGd_3OMfKPRpnctCrPAdxQ55aogYjjtHQCmj7-E4Y10dZIdZFQoEypqXWqwCC343w" | |
60 | } | |
61 | ||
62 | return region === 'SupremeUS' ? usCheckoutData : euCheckoutData | |
63 | } | |
64 | ||
65 | class SupremeClient { | |
66 | ||
67 | constructor(task) { | |
68 | this.task = task | |
69 | ||
70 | ||
71 | //keep track | |
72 | this.pookyString; | |
73 | ||
74 | //TODO: implement stop | |
75 | this.stopped = false; | |
76 | this.canStart = true; | |
77 | ||
78 | this.rp = rp.defaults({ jar: true, json: true }); | |
79 | ||
80 | } | |
81 | ||
82 | ||
83 | async start() { | |
84 | console.log("Starting task") | |
85 | this.stopped = false; | |
86 | this.canStart = false; | |
87 | ||
88 | } | |
89 | ||
90 | ||
91 | async stop() { | |
92 | console.log(`Stopping task`) | |
93 | ||
94 | this.canStart = true; | |
95 | this.stopped = true; | |
96 | } | |
97 | ||
98 | async getItem() { | |
99 | if (!this.stopped) { | |
100 | const category = this.task.category; | |
101 | const keywords = this.task.keywords; | |
102 | ||
103 | console.log("keywords:", keywords) | |
104 | ||
105 | const time = (new Date()).getTime(); | |
106 | let options = { | |
107 | url: `https://www.supremenewyork.com/shop.json?_=${time}`, | |
108 | method: 'get', | |
109 | headers: { | |
110 | 'User-Agent': iPhone6.userAgent | |
111 | } | |
112 | }; | |
113 | ||
114 | console.log(options) | |
115 | ||
116 | try { | |
117 | let response = await this.rp(options) | |
118 | ||
119 | const parsedProducts = response['products_and_categories'] | |
120 | ||
121 | var parsedNewProducts = [] | |
122 | ||
123 | //TODO: modify depending on values in html. | |
124 | if (category === 'All') { | |
125 | for (let category of Object.values(parsedProducts)) { | |
126 | for (let product of category) { | |
127 | parsedNewProducts.push(product) | |
128 | } | |
129 | } | |
130 | } else { | |
131 | if (category === 'New') | |
132 | parsedNewProducts = parsedProducts['new']; | |
133 | else | |
134 | parsedNewProducts = parsedProducts[category]; | |
135 | } | |
136 | ||
137 | const positiveKeywords = keywords.split(',').filter(keyword => keyword.includes('+')).map(keyword => keyword.replace('+', '').trim()); | |
138 | const negativeKeywords = keywords.split(',').filter(keyword => keyword.includes('-')).map(keyword => keyword.replace('-', '').trim()); | |
139 | console.log(positiveKeywords, negativeKeywords) | |
140 | for (let product of parsedNewProducts) { | |
141 | let productTitle = product.name.replace(/[\u{0080}-\u{FFFF}]/gu, "") | |
142 | let productId = product.id | |
143 | var posNum = 0 | |
144 | var negNum = 0 | |
145 | for (let posKw of positiveKeywords) { | |
146 | if (productTitle.toLowerCase().includes(posKw.toLowerCase().trim())) { | |
147 | posNum++ | |
148 | } | |
149 | } | |
150 | for (let negKw of negativeKeywords) { | |
151 | if (productTitle.toLowerCase().includes(negKw.toLowerCase().trim())) { | |
152 | negNum++ | |
153 | } | |
154 | } | |
155 | if (posNum === positiveKeywords.length && negNum === 0) { | |
156 | // productTitle , productId | |
157 | //TODO: add error handling if no product is found | |
158 | console.log("found product", productTitle, productId) | |
159 | return await this.getSizes(productId) | |
160 | } | |
161 | } | |
162 | } catch (err) { | |
163 | console.log(`Error getting item: ${err.message}`) | |
164 | await this.getItem() | |
165 | } | |
166 | } | |
167 | ||
168 | } | |
169 | ||
170 | async getSizes(productId) { | |
171 | if (!this.stopped) { | |
172 | console.log("Finding Size") | |
173 | let productColorPref = this.task.color; | |
174 | let productSizePref = this.task.size; | |
175 | let options = { | |
176 | url: `https://www.supremenewyork.com/shop/${productId}.json`, | |
177 | method: 'get', | |
178 | headers: { | |
179 | 'User-Agent': iPhone6.userAgent | |
180 | } | |
181 | }; | |
182 | ||
183 | try { | |
184 | let response = await this.rp(options) | |
185 | const styles = response["styles"] | |
186 | ||
187 | ||
188 | ||
189 | console.log(styles) | |
190 | ||
191 | let colorId; | |
192 | let sizeId; | |
193 | ||
194 | for (let style of styles) { | |
195 | if (style["name"].toLowerCase() === productColorPref.toLowerCase()) { | |
196 | colorId = style["id"] | |
197 | for (let size of style["sizes"]) { | |
198 | if (productSizePref === "None") { | |
199 | sizeId = size['id'] | |
200 | } else if (size["name"].toLowerCase() === productSizePref.toLowerCase()) { | |
201 | sizeId = size["id"]; | |
202 | ||
203 | break; | |
204 | } | |
205 | } | |
206 | ||
207 | break; | |
208 | } | |
209 | } | |
210 | ||
211 | //TODO: change to status message, maybe add retry? | |
212 | if (!colorId || !sizeId) { | |
213 | console.log(colorId, sizeId) | |
214 | return undefined; | |
215 | } else { | |
216 | console.log("got colorid and sizeid", colorId, sizeId) | |
217 | return await this.atc(colorId, sizeId, productId) | |
218 | } | |
219 | ||
220 | } catch (err) { | |
221 | this.sendStatus("Error Getting Sizes") | |
222 | await getSizes(productId) | |
223 | console.log(`Error getting sizes: ${err}`) | |
224 | } | |
225 | ||
226 | } | |
227 | ||
228 | } | |
229 | ||
230 | async atc(colorId, sizeId, productId, formStyle, formSize) { | |
231 | if (!this.stopped) { | |
232 | console.log("Adding To Cart") | |
233 | ||
234 | let parsedPooky, msBefore | |
235 | if (this.task.pooky === "on") { | |
236 | msBefore = Date.now() | |
237 | parsedPooky = await this.getPooky(false) | |
238 | ||
239 | if (!parsedPooky) { | |
240 | console.log("Could not get pooky cookies... stopping.") | |
241 | return this.stopped = true; | |
242 | } | |
243 | } | |
244 | ||
245 | ||
246 | //TODO: check region, proxy support | |
247 | let options = { | |
248 | url: `https://www.supremenewyork.com/shop/${productId}/add.json`, | |
249 | method: 'POST', | |
250 | headers: { | |
251 | 'Host': 'www.supremenewyork.com', | |
252 | 'Accept': 'application/json', | |
253 | "Content-Type": "application/x-www-form-urlencoded", | |
254 | 'X-Requested-With': 'XMLHttpRequest', | |
255 | 'Pragma': 'no-cache', | |
256 | 'Cache-Control': 'no-cache', | |
257 | 'Accept-Language': 'en-us', | |
258 | 'Origin': 'https://www.supremenewyork.com', | |
259 | 'User-Agent': "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1", | |
260 | 'Referer': 'https://www.supremenewyork.com/mobile', | |
261 | 'Connection': 'keep-alive', | |
262 | }, | |
263 | form: { | |
264 | "qty": "1" | |
265 | }, | |
266 | proxy: this.task.proxy | |
267 | }; | |
268 | ||
269 | if (this.task.site === "SupremeUS") { | |
270 | options["form"]["s"] = sizeId | |
271 | options["form"]["st"] = colorId | |
272 | } else { | |
273 | options["form"]["size"] = sizeId | |
274 | options["form"]["style"] = colorId | |
275 | } | |
276 | ||
277 | if (this.task.pooky === "on") { | |
278 | options["headers"]["cookie"] = `lastid=${msBefore}; ${parsedPooky}` | |
279 | } | |
280 | console.log(options["form"]) | |
281 | try { | |
282 | let response = await this.rp(options) | |
283 | ||
284 | console.log("atc response:", response) | |
285 | ||
286 | //TODO: check | |
287 | if (response[0].in_stock === true) { | |
288 | console.log("Added to cart") | |
289 | return await this.checkout(sizeId) | |
290 | } else { | |
291 | console.log("Error") | |
292 | sleep(1000) | |
293 | return this.atc(colorId, sizeId, productId) | |
294 | } | |
295 | ||
296 | } catch (err) { | |
297 | console.log(`Error adding to cart: ${err.message}`) | |
298 | } | |
299 | } | |
300 | } | |
301 | ||
302 | async checkout(variant) { | |
303 | if (!this.stopped) { | |
304 | let parsedPooky, msBefore | |
305 | if (this.task.pooky === "on") { | |
306 | msBefore = Date.now() | |
307 | parsedPooky = await this.getPooky(true) | |
308 | ||
309 | if (!parsedPooky) { | |
310 | console.log("Could not get pooky cookies... stopping.") | |
311 | return this.stopped = true; | |
312 | } | |
313 | } | |
314 | ||
315 | //TODO: use promise.all or something to get captcha and pooky at same time.. | |
316 | ||
317 | ||
318 | let options = { | |
319 | url: `https://www.supremenewyork.com/checkout.json`, | |
320 | method: 'post', | |
321 | headers: { | |
322 | 'Accept': 'application/json', | |
323 | 'accept-encoding': 'gzip, deflate, br', | |
324 | 'Accept-Language': 'en-US,en;q=0.9', | |
325 | 'Origin': 'https://www.supremenewyork.com', | |
326 | 'Referer': 'https://www.supremenewyork.com/mobile/', | |
327 | "Content-Type": "application/x-www-form-urlencoded", | |
328 | 'User-Agent': "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1", | |
329 | 'X-Requested-With': 'XMLHttpRequest', | |
330 | 'sec-fetch-mode': 'cors', | |
331 | 'sec-fetch-site': 'same-origin' | |
332 | }, | |
333 | formData: getCheckoutData(this.task.site, [variant]), | |
334 | // proxy: this.task.proxy, | |
335 | resolveWithFullResponse: true | |
336 | }; | |
337 | ||
338 | if (this.task.pooky === "on") { | |
339 | options["headers"]["cookie"] = `lastid=${msBefore}; ${parsedPooky}` | |
340 | } | |
341 | ||
342 | ||
343 | try { | |
344 | ||
345 | console.log("Checking Out") | |
346 | await sleep(3) | |
347 | let response = await this.rp(options) | |
348 | ||
349 | console.log(response.body) | |
350 | ||
351 | if (response.status === 'failed') { | |
352 | console.log('Failed to checkout') | |
353 | } else if (response.status === 'outOfStock') { | |
354 | console.log("Item OOS") | |
355 | } else if (response.status === 'queued') { | |
356 | console.log('Queued...') | |
357 | console.log(response.slug) | |
358 | } | |
359 | ||
360 | ||
361 | } catch (err) { | |
362 | console.log(`Error checking out: ${err.message}`) | |
363 | return this.stopped = true; | |
364 | } | |
365 | } | |
366 | } | |
367 | ||
368 | /*async checkSlug(slug) { | |
369 | try { | |
370 | const res = await this.rp(`https://www.supremenewyork.com/checkout/${slug}/status.json`) | |
371 | if (res.data.status === 'queued') { | |
372 | console.log('In queue.. Checking again in 1s') | |
373 | await sleep(1000) | |
374 | return this.checkStatus(slug) | |
375 | } else if (res.data.status === 'failed') { | |
376 | console.log(res.data) | |
377 | console.log('Failed to checkout') | |
378 | ||
379 | } else if (res.data.status === "success") { | |
380 | console.log("Checkout Success") | |
381 | } | |
382 | } catch (err) { | |
383 | console.log("Error checking slug", err.message) | |
384 | } | |
385 | }*/ | |
386 | ||
387 | async getPooky(isCheckout) { | |
388 | let pookyUrl = (isCheckout) ? `${getPookyUrl(this.task.site)}&pookyuuid=${this.pookyString}` : getPookyUrl(this.task.site); | |
389 | ||
390 | let options = { | |
391 | url: pookyUrl, | |
392 | method: 'get' | |
393 | }; | |
394 | ||
395 | console.log(options) | |
396 | ||
397 | try { | |
398 | let response = await this.rp(options) | |
399 | console.log("pooky response", response) | |
400 | if (response["status"] === "successful") { | |
401 | if (!isCheckout) | |
402 | this.pookyString = response["cookie"][0].split("=")[1] | |
403 | ||
404 | let pookyString = ""; | |
405 | for (let cookie of response["cookie"]) { | |
406 | pookyString += `${cookie}; ` | |
407 | } | |
408 | console.log("RETURNING POOKY STRING", pookyString) | |
409 | return pookyString; | |
410 | } else { | |
411 | return undefined; | |
412 | } | |
413 | ||
414 | } catch (err) { | |
415 | console.log("Error getting pooky ", err.message) | |
416 | } | |
417 | } | |
418 | ||
419 | } | |
420 | ||
421 | ||
422 | var array = {"category": "Accessories", "keywords": "+hanes,+socks", "color": "Red", "size": "N/A", "pooky": "on", "site": "SupremeUS", "timer": ""} | |
423 | var Supreme = new SupremeClient(array) | |
424 | Supreme.getItem() |