Skip to content

Instantly share code, notes, and snippets.

@AliMilani
Last active July 7, 2023 15:42
Show Gist options
  • Save AliMilani/2f122d36faa91db6baae9ca9e04ce396 to your computer and use it in GitHub Desktop.
Save AliMilani/2f122d36faa91db6baae9ca9e04ce396 to your computer and use it in GitHub Desktop.
torob
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