Загрузка...

JS
TAMPERMONKEY - Script for market.csgo

Thread in Your projects created by етернити Dec 26, 2025. 145 views

  1. етернити
    етернити Topic starter Dec 26, 2025 Banned 8,297 May 12, 2022
    TAMPERMONKEY - Скрипт для market.csgo

    Описание применения скрипта:
    На главной странице у карточек предметов добавляются новые цены. Выглядит это следующим образом

    Стандарт цена Цена автобая (примерная)
    Цена автобая после комиссий ( то есть если продадим по цене автобая примерной, вычитаем комиссии и столько получим денег )

    На странице предметов появляются везде цены за вычетом комиссий ( красные ) а также около название предмета - цена по стиму (могут быть погрешности 1-2$ изза курсов валют


    [IMG]
    [IMG]
    JS
            // ==UserScript==
    // @name Market CSGO Price Calculator
    // @namespace http://tampermonkey.net/
    // @version 1.2
    // @description Добавляет расчет цен с учетом комиссий на market.csgo.com и цены из Steam
    // @author You
    // @match https://market.csgo.com/ru/*
    // @grant GM_xmlhttpRequest
    // @connect steamcommunity.com
    // @connect api.exchangerate-api.com
    // @connect csgobackpack.net
    // ==/UserScript==

    (function() {
    'use strict';

    // Функция для парсинга цены из строки (формат: "18,591$" или "9,507$")
    function parsePrice(priceText) {
    if (!priceText) return null;
    // Убираем символы валюты, пробелы и специальные символы (≤ и т.д.), заменяем запятую на точку
    const cleaned = priceText.replace(/[≤$\s]/g, '').replace(/,/g, '.');
    const price = parseFloat(cleaned);
    return isNaN(price) ? null : price;
    }

    // Функция для форматирования цены обратно (с запятой и $)
    function formatPrice(price) {
    return price.toFixed(3).replace('.', ',') + '$';
    }

    // Функция для расчета цены после вычета комиссий (два раза по 5%)
    function calculateAfterCommissions(price) {
    return price * 0.95 * 0.95; // = price * 0.9025
    }

    // Функция для получения курса валюты USD к RUB
    async function getExchangeRate() {
    try {
    const response = await fetch('https://api.exchangerate-api.com/v4/latest/USD');
    const data = await response.json();
    return data.rates.RUB || 95; // Fallback к примерному курсу
    } catch (error) {
    console.warn('Не удалось получить курс валюты, используем примерный курс:', error);
    return 95; // Примерный курс USD к RUB
    }
    }

    // Функция для парсинга цены из Steam (формат "$1.23" или "1,23 руб" или "$1,234.56")
    function parseSteamPrice(priceText) {
    if (!priceText) return null;
    // Убираем символы валюты, пробелы, запятые (которые используются как разделители тысяч)
    let cleaned = priceText.replace(/[$\sруб₽]/gi, '');
    // Заменяем запятую на точку, но только если это десятичный разделитель
    // Если есть точка, то запятая - это разделитель тысяч, убираем её
    if (cleaned.includes('.')) {
    cleaned = cleaned.replace(/,/g, '');
    } else if (cleaned.includes(',')) {
    // Если запятая последняя или перед ней меньше 3 цифр, это десятичный разделитель
    const parts = cleaned.split(',');
    if (parts.length === 2 && parts[1].length <= 2) {
    cleaned = cleaned.replace(',', '.');
    } else {
    // Иначе это разделитель тысяч, убираем
    cleaned = cleaned.replace(/,/g, '');
    }
    }
    const price = parseFloat(cleaned);
    return isNaN(price) ? null : price;
    }

    // Функция для определения валюты из цены Steam
    function getSteamCurrency(priceText) {
    if (!priceText) return 'USD';
    if (priceText.toLowerCase().includes('руб') || priceText.includes('₽')) {
    return 'RUB';
    }
    return 'USD';
    }

    // Функция для получения цены через GM_xmlhttpRequest (обход CORS)
    function getSteamPriceViaGM(marketHashName) {
    return new Promise((resolve) => {
    const url = `https://steamcommunity.com/market/priceoverview/?appid=730&currency=1&market_hash_name=${encodeURIComponent(marketHashName)}`;

    GM_xmlhttpRequest({
    method: 'GET',
    url: url,
    headers: {
    'Accept': 'application/json',
    'Referer': 'https://steamcommunity.com/'
    },
    onload: function(response) {
    try {
    const data = JSON.parse(response.responseText);
    if (data.success && data.lowest_price) {
    const currency = getSteamCurrency(data.lowest_price);
    let price = parseSteamPrice(data.lowest_price);

    if (price) {
    // Если цена в рублях, конвертируем в доллары
    if (currency === 'RUB') {
    getExchangeRate().then(exchangeRate => {
    price = price / exchangeRate;
    resolve({
    price: price,
    currency: 'USD',
    originalPrice: data.lowest_price,
    originalCurrency: currency
    });
    }).catch(() => {
    // Используем примерный курс
    price = price / 95;
    resolve({
    price: price,
    currency: 'USD',
    originalPrice: data.lowest_price,
    originalCurrency: currency
    });
    });
    } else {
    resolve({
    price: price,
    currency: 'USD',
    originalPrice: data.lowest_price,
    originalCurrency: currency
    });
    }
    } else {
    resolve(null);
    }
    } else {
    resolve(null);
    }
    } catch (error) {
    console.error('Ошибка парсинга ответа Steam:', error);
    resolve(null);
    }
    },
    onerror: function(error) {
    console.error('Ошибка запроса к Steam:', error);
    resolve(null);
    },
    timeout: 10000
    });
    });
    }

    // Функция для получения цены через парсинг HTML страницы Steam Market
    function getSteamPriceFromHTML(marketHashName) {
    return new Promise((resolve) => {
    const url = `https://steamcommunity.com/market/listings/730/${encodeURIComponent(marketHashName)}`;

    GM_xmlhttpRequest({
    method: 'GET',
    url: url,
    headers: {
    'Accept': 'text/html',
    'Referer': 'https://steamcommunity.com/'
    },
    onload: function(response) {
    try {
    // Ищем цену в HTML (обычно в формате "$XX.XX USD" или "XX,XX руб")
    const html = response.responseText;

    // Ищем паттерн с ценой в USD
    const usdMatch = html.match(/\$([\d,]+\.?\d*)\s*USD/i);
    if (usdMatch) {
    const price = parseSteamPrice(usdMatch[0]);
    if (price) {
    resolve({
    price: price,
    currency: 'USD',
    originalPrice: usdMatch[0],
    originalCurrency: 'USD'
    });
    return;
    }
    }

    // Ищем паттерн с ценой в рублях
    const rubMatch = html.match(/([\d,]+\.?\d*)\s*руб/i);
    if (rubMatch) {
    let price = parseSteamPrice(rubMatch[0]);
    if (price) {
    getExchangeRate().then(exchangeRate => {
    price = price / exchangeRate;
    resolve({
    price: price,
    currency: 'USD',
    originalPrice: rubMatch[0] + ' руб',
    originalCurrency: 'RUB'
    });
    }).catch(() => {
    price = price / 95;
    resolve({
    price: price,
    currency: 'USD',
    originalPrice: rubMatch[0] + ' руб',
    originalCurrency: 'RUB'
    });
    });
    return;
    }
    }

    resolve(null);
    } catch (error) {
    console.error('Ошибка парсинга HTML Steam:', error);
    resolve(null);
    }
    },
    onerror: function() {
    resolve(null);
    },
    timeout: 10000
    });
    });
    }

    // Основная функция для получения цены из Steam Market
    async function getSteamPrice(marketHashName) {
    // Сначала пробуем через GM_xmlhttpRequest API (обход CORS)
    const steamPrice = await getSteamPriceViaGM(marketHashName);
    if (steamPrice && steamPrice.price) {
    return steamPrice;
    }

    // Если не получилось, пробуем парсинг HTML страницы
    const htmlPrice = await getSteamPriceFromHTML(marketHashName);
    if (htmlPrice && htmlPrice.price) {
    return htmlPrice;
    }

    return null;
    }

    // SVG логотип Steam (упрощенный)
    function getSteamIconSVG() {
    return `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="vertical-align: middle; display: inline-block;">
    <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z" fill="#171a21"/>
    <path d="M12 5c-3.87 0-7 3.13-7 7s3.13 7 7 7 7-3.13 7-7-3.13-7-7-7z" fill="#66c0f4"/>
    <circle cx="9" cy="12" r="1.5" fill="#171a21"/>
    <circle cx="15" cy="12" r="1.5" fill="#171a21"/>
    </svg>`;
    }

    // Функция для обработки страницы предмета и добавления цены Steam
    async function processSteamPrice() {
    const itemBaseInfo = document.querySelector('.item-base-info');
    if (!itemBaseInfo) return;

    // Проверяем, не обработан ли уже этот элемент
    if (itemBaseInfo.dataset.steamPriceProcessed === 'true') return;

    // Ищем блок brif-info для вставки рядом
    const brifInfo = itemBaseInfo.querySelector('.brif-info');
    if (!brifInfo) return;

    // Проверяем, нет ли уже добавленного блока с ценой Steam
    if (itemBaseInfo.querySelector('.steam-price-block')) return;

    // Получаем market hash name
    const marketHashNameSpan = itemBaseInfo.querySelector('.market-hash-name span');
    if (!marketHashNameSpan) return;

    const marketHashName = marketHashNameSpan.textContent.trim();
    if (!marketHashName) return;

    // Создаем блок для отображения цены Steam
    const steamPriceBlock = document.createElement('div');
    steamPriceBlock.className = 'type steam-price-block';
    steamPriceBlock.style.cssText = `
    display: inline-flex;
    align-items: center;
    gap: 4px;
    `;

    const steamPriceSpan = document.createElement('span');
    steamPriceSpan.innerHTML = getSteamIconSVG() + '<span style="margin-left: 2px;">Загрузка...</span>';
    steamPriceBlock.appendChild(steamPriceSpan);

    // Вставляем блок в brif-info
    brifInfo.appendChild(steamPriceBlock);

    // Получаем цену из Steam
    const steamData = await getSteamPrice(marketHashName);

    if (steamData && steamData.price) {
    const formattedPrice = steamData.price.toFixed(2).replace('.', ',') + '$';
    steamPriceSpan.innerHTML = getSteamIconSVG() + `<span style="margin-left: 2px;">Steam: ${formattedPrice}</span>`;
    steamPriceBlock.style.color = '#66c0f4'; // Цвет Steam
    } else {
    steamPriceSpan.innerHTML = getSteamIconSVG() + '<span style="margin-left: 2px; color: #999;">Цена недоступна</span>';
    }

    // Помечаем как обработанный
    itemBaseInfo.dataset.steamPriceProcessed = 'true';
    }

    // Функция для обработки цен на главной странице (список скинов)
    function processMainPagePrices() {
    // Находим все элементы с ценами на главной странице
    const priceElements = document.querySelectorAll('app-base-item .price span:first-child');

    priceElements.forEach(priceSpan => {
    // Проверяем, не обработан ли уже этот элемент
    if (priceSpan.dataset.processed === 'true') return;

    // Проверяем, нет ли уже добавленных элементов
    const parent = priceSpan.parentElement;
    if (parent.querySelector('.calculated-result')) return;

    const priceText = priceSpan.textContent.trim();
    const originalPrice = parsePrice(priceText);

    if (originalPrice === null) return;

    // Вычисляем итог: цена - 10%
    const result = originalPrice * 0.9;

    // Вычисляем цену после комиссий: итог - 5% - 5%
    const afterCommissions = calculateAfterCommissions(result);

    // Настраиваем родительский контейнер для flex-отображения
    if (!parent.style.display || parent.style.display === '') {
    parent.style.display = 'flex';
    parent.style.flexWrap = 'wrap';
    parent.style.gap = '0px';
    parent.style.alignItems = 'flex-start';
    parent.style.lineHeight = '1.2';
    }

    // Добавляем итог
    const resultSpan = document.createElement('span');
    resultSpan.className = 'calculated-result';
    resultSpan.style.marginLeft = '4px';
    resultSpan.style.fontWeight = 'bold';
    resultSpan.style.fontSize = '0.85em';
    resultSpan.textContent = formatPrice(result);

    // Добавляем цену после комиссий (красным мелким шрифтом, на новой строке)
    const commissionSpan = document.createElement('span');
    commissionSpan.className = 'commission-price';
    commissionSpan.style.display = 'block';
    commissionSpan.style.width = '100%';
    commissionSpan.style.marginLeft = '4px';
    commissionSpan.style.marginTop = '0px';
    commissionSpan.style.lineHeight = '1';
    commissionSpan.style.color = '#ff0000';
    commissionSpan.style.fontSize = '0.75em';
    commissionSpan.textContent = `(${formatPrice(afterCommissions)})`;

    // Вставляем после оригинальной цены
    priceSpan.after(resultSpan);
    resultSpan.after(commissionSpan);

    // Помечаем как обработанный
    priceSpan.dataset.processed = 'true';
    });
    }

    // Функция для обработки цен на странице предмета (запросы на автопокупку)
    function processItemPagePrices() {
    // Находим все блоки с запросами на автопокупку
    const autoBuyBlocks = document.querySelectorAll('item-order .auto-buying .block .price');

    autoBuyBlocks.forEach(priceDiv => {
    // Проверяем, не обработан ли уже этот элемент
    if (priceDiv.dataset.processed === 'true') return;

    // Проверяем, нет ли уже добавленного элемента с комиссией
    if (priceDiv.querySelector('.commission-price')) return;

    // Получаем текст цены (может быть с символом ≤)
    // Ищем span с ценой или берем весь текст
    let priceText = '';
    const priceSpan = priceDiv.querySelector('span');
    if (priceSpan && priceSpan.textContent.trim()) {
    priceText = priceDiv.textContent.trim();
    } else {
    priceText = priceDiv.textContent.trim();
    }

    const originalPrice = parsePrice(priceText);

    if (originalPrice === null) return;

    // Вычисляем цену после комиссий: цена - 5% - 5%
    const afterCommissions = calculateAfterCommissions(originalPrice);

    // Добавляем цену после комиссий (красным мелким шрифтом)
    const commissionSpan = document.createElement('span');
    commissionSpan.className = 'commission-price';
    commissionSpan.style.marginLeft = '4px';
    commissionSpan.style.color = '#ff0000';
    commissionSpan.style.fontSize = '0.75em';
    commissionSpan.textContent = `(${formatPrice(afterCommissions)})`;

    // Вставляем после цены
    priceDiv.appendChild(commissionSpan);

    // Помечаем как обработанный
    priceDiv.dataset.processed = 'true';
    });
    }

    // Функция для обработки цен в app-page-inventory-price (основная цена и лучшее предложение)
    function processInventoryPrices() {
    // Обрабатываем основную цену
    const mainPriceSpans = document.querySelectorAll('app-page-inventory-price .price span');

    mainPriceSpans.forEach(priceSpan => {
    // Проверяем, не обработан ли уже этот элемент
    if (priceSpan.dataset.processed === 'true') return;

    // Проверяем, нет ли уже добавленного элемента с комиссией
    if (priceSpan.parentElement.querySelector('.commission-price')) return;

    const priceText = priceSpan.textContent.trim();
    const originalPrice = parsePrice(priceText);

    if (originalPrice === null) return;

    // Вычисляем цену после комиссий: цена - 5% - 5%
    const afterCommissions = calculateAfterCommissions(originalPrice);

    // Добавляем цену после комиссий (красным мелким шрифтом)
    const commissionSpan = document.createElement('span');
    commissionSpan.className = 'commission-price';
    commissionSpan.style.marginLeft = '4px';
    commissionSpan.style.color = '#ff0000';
    commissionSpan.style.fontSize = '0.75em';
    commissionSpan.textContent = `(${formatPrice(afterCommissions)})`;

    // Вставляем после цены
    priceSpan.after(commissionSpan);

    // Помечаем как обработанный
    priceSpan.dataset.processed = 'true';
    });

    // Обрабатываем цену в "Лучшее предложение"
    const bestOfferPriceSpans = document.querySelectorAll('best-offers .best-offer span:last-child');

    bestOfferPriceSpans.forEach(priceSpan => {
    // Проверяем, не обработан ли уже этот элемент
    if (priceSpan.dataset.processed === 'true') return;

    // Проверяем, нет ли уже добавленного элемента с комиссией рядом
    if (priceSpan.nextElementSibling && priceSpan.nextElementSibling.classList.contains('commission-price')) return;

    const priceText = priceSpan.textContent.trim();
    const originalPrice = parsePrice(priceText);

    if (originalPrice === null) return;

    // Вычисляем цену после комиссий: цена - 5% - 5%
    const afterCommissions = calculateAfterCommissions(originalPrice);

    // Добавляем цену после комиссий (красным мелким шрифтом)
    const commissionSpan = document.createElement('span');
    commissionSpan.className = 'commission-price';
    commissionSpan.style.marginLeft = '4px';
    commissionSpan.style.color = '#ff0000';
    commissionSpan.style.fontSize = '0.75em';
    commissionSpan.textContent = `(${formatPrice(afterCommissions)})`;

    // Вставляем после цены
    priceSpan.after(commissionSpan);

    // Помечаем как обработанный
    priceSpan.dataset.processed = 'true';
    });
    }

    // Основная функция обработки
    function processPrices() {
    processMainPagePrices();
    processItemPagePrices();
    processInventoryPrices();
    processSteamPrice();
    }

    // Используем MutationObserver для отслеживания динамического контента Angular
    const observer = new MutationObserver(function(mutations) {
    let shouldProcess = false;

    mutations.forEach(function(mutation) {
    if (mutation.addedNodes.length > 0) {
    shouldProcess = true;
    }
    });

    if (shouldProcess) {
    // Небольшая задержка для того, чтобы Angular успел отрендерить контент
    setTimeout(processPrices, 100);
    }
    });

    // Запускаем обработку при загрузке страницы
    if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', function() {
    processPrices();
    observer.observe(document.body, {
    childList: true,
    subtree: true
    })
     
Loading...