eshop EUR prices with API

9/11/2022, 4:27:46 PM

// ==UserScript==
// @name        eshop EUR prices with API
// @namespace   Violentmonkey Scripts
// @include     https://deathangels.shop*
// @include     https://www.blue-tomato.com*
// @include     https://doomer.shop*
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_xmlhttpRequest
// @version     3.2
// @license     MIT
// @author      KraXen72
// @description 9/11/2022, 4:27:46 PM
// @icon        https://cdn.shopify.com/s/files/1/0569/8407/6311/files/23.png?crop=center&height=32&v=1660122641&width=32
// ==/UserScript==


function injectCSS(css) {
  const styleTag = document.createElement("style")
  styleTag.innerHTML = css
  document.head.appendChild(styleTag)
}

function prependText(text, pelem, parent) {
  const elem = document.createElement("span")
  elem.textContent = text
  parent.insertBefore(elem, pelem)
}

function checkNumber(num) {
  return typeof num === 'number' && !isNaN(num)
}


function sameDay(d1, d2) {
  // console.log("sameday", d1, d2, {year: [d1.getFullYear(), d2.getFullYear()], month: [d1.getUTCMonth(), d2.getUTCMonth()], date: [d1.getUTCDate(), d2.getUTCDate()]})
  return d1.getFullYear() === d2.getFullYear() &&
    d1.getMonth() === d2.getMonth() &&
    d1.getDate() === d2.getDate();
}

let thisDate = new Date(Date.now())
let lastDate = new Date(GM_getValue("lastDate", new Date().setDate(new Date().getDate() - 1)))

const defaultPriceRegex = /\d*[,.]*\d{1,}/gi
let conversionTableEURto = {}
if (!(typeof lastDate.getMonth === 'function')) lastDate = new Date(lastDate)

function getStuffFromApi() {
  console.log("getting stuff from api")
  const apiKey = GM_getValue("apiKey", false)
  if (!apiKey) throw "set API key for exchangerate-api.com in values"
  const req = GM_xmlhttpRequest({
    url: `https://v6.exchangerate-api.com/v6/${apiKey}/latest/EUR`,
    method: "GET",
    responseType: "json",
    onload: (responseBody) => {
      const data = JSON.parse(responseBody.responseText)
      console.log("!!!got api response", thisDate, lastDate, data)
      GM_setValue("lastDate", thisDate.toDateString())
      GM_setValue("conversionJSON",data["conversion_rates"])
      window.location.reload()
    }
  })
}

if (GM_getValue("conversionJSON", false) !== false) {
  console.log("getvalue", GM_getValue("conversionJSON", false), sameDay(thisDate, lastDate))
  if (sameDay(thisDate, lastDate)) {
    conversionTableEURto = GM_getValue("conversionJSON",{})
    console.log("had cached stuff:", conversionTableEURto)
  } else {
    getStuffFromApi()
  }
} else {
  getStuffFromApi()
}

// console.log("date sanity check", thisDate, lastDate, sameDay(lastDate, thisDate), GM_getValue("lastDate"))

function applyPrice(conversionConstant, initialPrice, element, mode = "normal", parentElem = element.parentElement) {
  if (initialPrice === null) return
  const eurPrice = (initialPrice / conversionConstant).toFixed(2)
  // console.log({ eurPrice, initialPrice, conversionConstant})

  element.title = `${eurPrice}€`
  element.innerHTML += ` = ${eurPrice}€`
}

function getPrice(element, regex = defaultPriceRegex, deleteComma = true, debug = false) {
  const text = element.innerText.trim()
  let res = text.match(regex)
  if (res === null) return res

  if (checkNumber(res)) return res
  if (Array.isArray(res)) res = res[0]
  if (typeof res === "string" && res.includes(",")) {
    if (deleteComma) {
      res = res.replaceAll(",", "")
    } else {
      res = res.replaceAll(",", ".")
    }
  }

  if (debug) console.log("[regex]", text, res);
  if (!isNaN(parseInt(res))) {
    if (res.includes(".")) {
      return parseFloat(res)
    } else {
      return parseInt(res)
    }
  } else {
    console.warn(`failed to parse ${res} as a number. it's ${parseInt(res)} on text "${text}"`, element)
    return res
  }
}

if (window.location.hostname === "deathangels.shop") {
  const prices = [...document.querySelectorAll(".price__regular .price-item.price-item--regular, .cart-item .price.price--end")]

  prices.forEach(priceElem => {
    const price = getPrice(priceElem)
    applyPrice(conversionTableEURto["JPY"], price, priceElem)
  })
} else if (window.location.hostname === "www.blue-tomato.com") {

  const prices = [... document.querySelectorAll(".productdesc .price")]
  prices.forEach(priceElem => {
    // console.log(priceElem, priceElem.children.length)

    let price = getPrice(priceElem)
    applyPrice(conversionTableEURto["GBP"], price, priceElem)
  })

  if (document.querySelector(".js-price > .c-details-box__price-current") !== null) {
    console.log("detail view found")
    const detailHolder =document.querySelector(".js-price > .c-details-box__price-current").parentElement

    const callback = (mutationList, observer) => {
      for (const mutation of mutationList) {
        if (mutation.type === 'childList') {
          console.log('mo: childList modification. refreshing', mutation);
          observer.disconnect();

          [...document.querySelectorAll(".js-price > .c-details-box__price-current")].forEach(priceElem => {
            const price = getPrice(priceElem)
            applyPrice(conversionTableEURto["GBP"], price, priceElem)
          })
        }
      }
    };
    const config = { attributes: false, childList: true, subtree: true };
    const priceObserver = new MutationObserver(callback);

    priceObserver.observe(detailHolder, config);
  } else {
    console.log("no detail view found")
  }


} else if (window.location.hostname === "doomer.shop") {
  const prices = [...document.querySelectorAll(".price-item.price-item--regular")]

  prices.forEach(priceElem => {
    const price = getPrice(priceElem)
    if (price !== "") {
      applyPrice(conversionTableEURto["USD"], price, priceElem)
    }
  })
}