Last active
July 7, 2023 15:42
-
-
Save AliMilani/2f122d36faa91db6baae9ca9e04ce396 to your computer and use it in GitHub Desktop.
torob
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const axios = require("axios"); | |
require("dotenv").config(); | |
const { toToman } = require("./helpers"); | |
class TorobProduct { | |
/** | |
* @type {[Object]} | |
*/ | |
#url; | |
/** | |
* @type {[Object]} | |
*/ | |
#offers; | |
/** | |
* @type {Object} | |
* @property {Number} lowPrice | |
* @property {Number} highPrice | |
* @property {Number} offersCount | |
*/ | |
#offersInfo; | |
/** | |
* @param {String} url | |
*/ | |
constructor(url) { | |
this.#validateUrl(url); | |
this.#url = url; | |
} | |
/** | |
* @param {String} url | |
*/ | |
#validateUrl(url) { | |
if (!url) throw new Error("Url is required to create torob product"); | |
if (url.includes("http://")) | |
throw new Error(`Please use "https" in your url`); | |
if (!url.startsWith("https://torob.com/p/")) | |
throw new Error( | |
"Url is not valid / should be start with https://torob.com/p/" | |
); | |
} | |
async init() { | |
const product = await this.#loadProduct(); | |
const productOffers = product.offers; | |
this.#offers = this.#generateOffers(productOffers.offers); | |
this.#offersInfo = this.#generateOffersInfo(productOffers); | |
return this; | |
} | |
/** | |
* @returns {Promise<Object>} | |
*/ | |
async #loadProduct() { | |
if (this.#offers) | |
throw new Error("Product is already loaded and initialized"); | |
let pageHtml = ""; | |
try { | |
// const { data } = await axios.get({ | |
// url: this.#url, | |
// method: "get", | |
// maxBodyLength: Infinity, | |
// headers: { | |
// authority: "torob.com", | |
// 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.7", | |
// "accept-language": "en-US,en;q=0.9,fa;q=0.8", | |
// "cache-control": "max-age=0", | |
// cookie: | |
// "is_torob_user_logged_in=False; send_amplitude_events_v3=False; search_session=jklwnxmpdhtsbmnqyktkpvbysvcrmuif; returning_user=true; _gcl_au=1.1.1967373111.1682147733; _gid=GA1.2.340567467.1682147734; _clck=c34r4s|1|faz|0; _ym_uid=1682147734564296583; _ym_d=1682147734; _ym_isad=2; _ga=GA1.1.1872906749.1682147734; _ga_CF4KGKM3PG=GS1.1.1682147733.1.1.1682147736.0.0.0; _ym_visorc=b", | |
// "sec-ch-ua": | |
// '"Chromium";v="112", "Google Chrome";v="112", "Not:A-Brand";v="99"', | |
// "sec-ch-ua-mobile": "?0", | |
// "sec-ch-ua-platform": '"Windows"', | |
// "sec-fetch-dest": "document", | |
// "sec-fetch-mode": "navigate", | |
// "sec-fetch-site": "same-origin", | |
// "sec-fetch-user": "?1", | |
// "upgrade-insecure-requests": "1", | |
// "user-agent": | |
// "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36", | |
// }, | |
// }); | |
// use fetch insted | |
const data = await (await fetch(this.#url)).text() | |
if (data) pageHtml = data; | |
} catch (error) { | |
// if (error.response.status === 404) { | |
if (error?.response?.data?.includes("صفحه یافت نشد")) { | |
throw new Error(`Product with url ${this.#url} not found`); | |
} | |
if (error.code == "ENOTFOUND") { | |
throw new Error( | |
`May be you have a problem with your internet connection or site is offline` | |
); | |
} | |
throw error; | |
} | |
const productJson = this.#selectJsonFromHtml(pageHtml); | |
return JSON.parse(productJson); | |
} | |
/** | |
* | |
* @param {String} html | |
* @returns Product json | |
*/ | |
#selectJsonFromHtml(html) { | |
const startStr = '<script type="application/ld+json">'; | |
const endStr = '</script><script type="application/ld+json">'; | |
const startIndex = html.indexOf(startStr) + startStr.length; | |
const endIndex = html.indexOf(endStr); | |
const selectedText = html.substring(startIndex, endIndex); | |
return selectedText; | |
} | |
/** | |
* | |
* @param {Array} offers | |
* @returns {Array} | |
*/ | |
#generateOffers(offers) { | |
return offers.map((offer) => { | |
return { | |
name: offer.name, | |
price: toToman(offer.price), | |
isAdv: this.#isAdv(offer), | |
shopRank: this.#getShopRank(offer), | |
name: offer.name, | |
}; | |
}); | |
} | |
/** | |
* @param {[Object]} offer | |
* @returns {[Object]} | |
*/ | |
#generateOffersInfo(productOffers) { | |
const offersInfo = { ...productOffers }; | |
return { | |
lowPrice: toToman(offersInfo.lowPrice), | |
highPrice: toToman(offersInfo.highPrice), | |
shopsCount: this.offers().length, | |
}; | |
} | |
/** | |
* low price in toman | |
* @returns {Number} | |
*/ | |
lowPrice() { | |
return this.#offersInfo.lowPrice; | |
} | |
/** | |
* high price in toman | |
* @returns {Number} | |
*/ | |
highPrice() { | |
return this.#offersInfo.lowPrice; | |
} | |
/** | |
* @returns {Number} | |
*/ | |
offerCount() { | |
return this.#offers.length; | |
} | |
/** | |
* @returns {Number} | |
*/ | |
avragePrice() { | |
const prices = this.#offers.map((offer) => offer.price); | |
let sum = prices.reduce((acc, val) => acc + parseInt(val), 0); | |
let avg = sum / prices.length; | |
return Math.floor(avg); | |
} | |
/** | |
* @returns {[Object]} | |
*/ | |
offers() { | |
return this.#offers; | |
} | |
/** | |
* @returns {[Object]} | |
*/ | |
offersWitoutAdv() { | |
return this.#offers.filter((offer) => offer.isAdv === false); | |
} | |
/** | |
* @param {Object} offer | |
* @returns {Boolean} | |
*/ | |
#isAdv(offer) { | |
return offer.url.includes("is_adv=True"); | |
} | |
/** | |
* @param {Object} offer | |
* @returns {Number | null} | |
*/ | |
#getShopRank(offer) { | |
return offer.url.match(/shop_rank=(\d+)/)?.[1] ?? null; | |
} | |
} | |
// async function main() { | |
// const torobProduct = await new TorobProduct( | |
// "https://torob.com/p/b0b6b7b3-d1d9-4cfb-b0d8-8cc0a95b02f3/%DA%AF%D9%88%D8%B4%DB%8C-%D8%B3%D8%A7%D9%85%D8%B3%D9%88%D9%86%DA%AF-a23-%D8%AD%D8%A7%D9%81%D8%B8%D9%87-128-%D8%B1%D9%85-4-%DA%AF%DB%8C%DA%AF%D8%A7%D8%A8%D8%A7%DB%8C%D8%AA/" | |
// ).init(); | |
// console.log(torobProduct.init()); | |
// } | |
// main(); | |
module.exports = TorobProduct; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment