Skip to content

Instantly share code, notes, and snippets.

@cjmaxik
Last active May 20, 2024 18:34
Show Gist options
  • Save cjmaxik/7ce493d08958eecd56a78c01482e49fa to your computer and use it in GitHub Desktop.
Save cjmaxik/7ce493d08958eecd56a78c01482e49fa to your computer and use it in GitHub Desktop.
Перевод валюты в рубли в Steam

Перевод валюты в рубли в Steam

Поддерживаются тенге, лиры, песо и гривны. Курс обновляется раз в 12 часов

Изменения

  • 0.9 - добавил гривны. Привет ПДФ
  • 0.8 - исправил обновление текущего курса валюты
  • 0.7 - исправил отображение баланса кошелька и изменил стиль оригинальной стоимости
  • 0.6 - добавил аргентинское песо, исправил ошибки
  • 0.5 - включил автообновление скрипта, исправил обновление курса валют
  • 0.4 - заменил источник курса (ближе к реальному), исправил перевод на страницах бандлов
  • 0.3 - исправил перевод в других элементах со скидкой, добавил перевод в окне поиска
  • 0.2 - исправил перевод в элементах со скидкой, добавил перевод на страницах распродаж, добавил инструкцию для Firefox
  • 0.1 - первая версия

Порядок установки

  1. Устанавливаем Tampermonkey
  1. Нажимаем на ссылку, Tampermonkey должен предложить установку/обновление скрипта - https://gist.github.com/cjmaxik/7ce493d08958eecd56a78c01482e49fa/raw/script.user.js

Порядок обновления с версии 0.5

Скрипт обновляется автоматически.

Порядок обновления с версии 0.4 до 0.5

Нажимаем на ссылку установки сверху, Tampermonkey предложить обновиться.


image

// ==UserScript==
// @name Steam valute to RUB convertation
// @namespace https://store.steampowered.com/
// @version 0.9
// @description for StopGame boosty chat
// @author CJMAXiK
// @homepage https://dtf.ru/u/3920-cjmaxik/2582934-perevod-valyuty-steam-dlya-puteshestvennikov
// @downloadURL https://gist.github.com/cjmaxik/7ce493d08958eecd56a78c01482e49fa/raw/script.user.js
// @updateURL https://gist.github.com/cjmaxik/7ce493d08958eecd56a78c01482e49fa/raw/script.user.js
// @match https://store.steampowered.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=steampowered.com
// @grant GM_xmlhttpRequest
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_addStyle
// @connect cdn.jsdelivr.net
// ==/UserScript==
const signToValute = {
'₸': 'KZT',
'TL': 'TRY',
'€': 'EUR',
'£': 'GBP',
'ARS$': 'ARS',
'₴': 'UAH',
}
let valute = null
let valuteSign = null
let currency = null
let rate = null
const makeRequest = (url) => {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: 'GET',
url,
headers: {
'Content-Type': 'application/json',
},
onload: function (response) {
resolve(response.responseText)
},
onerror: function (error) {
reject(error)
},
})
})
}
const updateRates = async () => {
const randomNumber = Math.random()
const url = `https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies/rub.json?${randomNumber}`
const data = await makeRequest(url)
const value = JSON.parse(data)
// 12-hour timeout for the value
GM_setValue('timeout_v2', Date.now() + 12 * 3600 * 1000)
GM_setValue('currency_v2', value)
// Clear v0.1 - v0.3 cache
GM_deleteValue('timeout', null)
GM_deleteValue('currency', null)
console.log('updateCurrency', value)
return value
}
const getRates = async () => {
if (currency) return
const timeout = GM_getValue('timeout_v2', null)
const cache = GM_getValue('currency_v2', null)
const rateDate = cache ? Date.parse(cache.date) : Date.now()
console.log('getCurrency CACHE', timeout, cache)
// No cache OR no timeout OR timeout is after the current date OR rate internal date is after 2 days from now (failsafe)
if (
!cache ||
!timeout ||
timeout <= Date.now() ||
rateDate + 48 * 3600 * 1000 <= Date.now()
) {
currency = await updateRates()
console.log('getCurrency NEW', currency)
return
}
currency = cache
console.log('getCurrency CACHED', currency)
}
const injectPrice = async (element) => {
if (element?.id === 'header_wallet_balance') return
const classList = element.classList.value
if (!element.parentElement.innerText.includes(valuteSign)) return
if (classList.includes('discount_original_price')) return
if (classList.includes('done') && element.innerText.includes('₽')) return
let price
if (element.dataset && element.dataset.priceFinal) {
price = element.dataset.priceFinal / 100
} else {
const removeDiscountedPrice = element.querySelector('span > strike')
if (removeDiscountedPrice) {
removeDiscountedPrice.remove()
element.classList.remove('discounted')
element.style.color = '#BEEE11'
}
price = Number(
element.innerText
.replace(/[^0-9.,-]+/g, '')
.replace('.', '')
.replace(',', '.')
)
}
if (!price) return
let convertedPrice = Math.ceil(price / rate)
convertedPrice = GStoreItemData.fnFormatCurrency(convertedPrice * 100, true)
.replace(valuteSign, '')
.replace(' ', '')
.trim()
element.innerHTML = element.innerText.replace('ARS$ ', '$')
element.innerHTML =
`<span style="font-size: 11px;">` +
element.innerText.replace(' ', '&nbsp;').trim() +
`</span><span style="padding-left: 5px;">≈${convertedPrice}&nbsp;₽</span>`
element.classList.add('done')
}
const grabCurrentCurrency = () => {
if (valute) return
let currency = document.querySelector('meta[itemprop="priceCurrency"]')
console.log(currency)
if (currency && currency.content) {
valute = currency.content
valuteSign = Object.keys(signToValute).find(
(key) => signToValute[key] === valute
)
return
}
try {
// eslint-disable-next-line no-undef
const valuteFromPrice = GStoreItemData.fnFormatCurrency(12345)
.replace('123,45', '')
.trim()
valute = signToValute[valuteFromPrice]
valuteSign = valuteFromPrice
} catch (e) {
console.warn(e)
return
}
}
const globalClasses = [
'.game_purchase_price',
'.discount_final_price:not(:has(> .your_price_label))',
'.discount_final_price > div:not([class])',
'.search_price',
'.price:not(.spotlight_body):not(.similar_grid_price)',
'#header_wallet_balance',
'div[class*=StoreSalePriceBox]',
'.match_subtitle',
'.game_area_dlc_price:not(:has(> *))',
'.savings.bundle_savings',
// eslint-disable-next-line no-return-assign
]
.map((x) => (x += ':not(.done)'))
.join(', ')
const observerInject = async () => {
const prices = document.querySelectorAll(globalClasses)
if (!prices) return
for (const price of prices) await injectPrice(price)
}
const main = async () => {
'use strict'
// Updating currency
await getRates()
console.log('Current rates', currency)
// Grabbing
await grabCurrentCurrency()
console.log('Current valute', valute)
console.log('Current valute sign', valuteSign)
if (valute === 'RUB') {
console.log('Привет, земляк ;)')
return
}
if (!valute) throw new Error('No valute detected!')
// Grabbing the rate
rate = Math.round(currency.rub[valute.toLowerCase()] * 100) / 100
console.log('Effective rate', rate)
if (!currency || !valute || !rate) return
// Add styles
let css =
'.tab_item_discount { width: 160px !important; } .tab_item_discount .discount_prices { width: 100% !important; } .tab_item_discount .discount_final_price { padding: 0 !important; }'
css +=
'.home_marketing_message.small .discount_block { height: auto !important; } .discount_block_inline { white-space: nowrap !important; }'
GM_addStyle(css)
// Injecting prices for the first time
await observerInject()
// Dynamically inject prices
const observer = new MutationObserver(async (mutations, _observer) => {
try {
await observerInject()
for (const mutation of mutations) {
// Checking added elements
for (const node of mutation.addedNodes) {
if (node.nodeType === Node.TEXT_NODE) {
if (!node.parentElement?.innerText.includes(valuteSign)) continue
await injectPrice(node.parentElement)
}
}
}
} catch (error) {
console.log(error)
}
})
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
})
}
window.onload = main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment