Raw Source
DIdi / Currency Calculator

// ==UserScript==
// @name         Currency Calculator
// @namespace    https://openuserjs.org/
// @version      2.6.3
// @description  Currency calculator with API integration
// @author       Didi
// @match        https://cbr.ru/*
// @grant        none
// @run-at       document-end
// @require      https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js
// @license      MIT
// @updateURL     https://openuserjs.org/meta/Didi/currency_calculator.meta.js
// ==/UserScript==


    (function() {
        if (typeof document.addEventListener !== 'function') {
            console.log('Ваш браузер устарел. Пожалуйста, обновите его.');
            return;
        }

        document.addEventListener('DOMContentLoaded', function() { // Гарантируем загрузку DOM
    
    'use strict';

    // Проверяем, что скрипт запущен на нужном сайте
    if (window.location.hostname !== 'cbr.ru') {
        return;
     + ' + 

    // Проверка наличия API ключа в localStorage
    var apiKey = 'ВАШ_API_КЛЮЧ_ЗДЕСЬ';

    if (!apiKey) {
        // Если ключа нет, создаем форму для ввода
        var apiKeyForm = document.createElement('div');
        apiKeyForm.innerHTML = ' + 
            <h1>Введите ваш API ключ от Open Exchange Rates:</h1>
            <input type="text" id="apiKeyInput" placeholder="Введите API ключ">
            <button id="apiKeySubmit">Сохранить</button>
        ';
        document.body.appendChild(apiKeyForm);

        document.getElementById('apiKeySubmit').addEventListener('click', () => {
            apiKey = document.getElementById('apiKeyInput').value;
            localStorage.setItem('openExchangeRatesApiKey', apiKey);
            location.reload(); // Перезагружаем страницу после сохранения ключа
         + ');
        return; // Прерываем выполнение, пока ключ не введен
     + ' + 

    // Создаем основной контейнер
    var calculatorContainer = document.createElement('div');
    calculatorContainer.style.position = 'fixed';
    calculatorContainer.style.top = '10px';
    calculatorContainer.style.right = '10px';
    calculatorContainer.style.backgroundColor = 'white';
    calculatorContainer.style.border = '1px solid black';
    calculatorContainer.style.padding = '8px';
    calculatorContainer.style.zIndex = '10000';
    calculatorContainer.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.2)';
    calculatorContainer.style.width = '300px';
    calculatorContainer.style.maxHeight = '75vh';
    calculatorContainer.style.overflowY = 'auto';
    calculatorContainer.style.overflowX = 'hidden';
    calculatorContainer.style.fontSize = '0.85em';
    calculatorContainer.style.lineHeight = '1.2';

    // API и настройки кэширования
    var apiUrl = 'https://openexchangerates.org/api/latest.json?app_id=' + apiKey + '&symbols=RUB,EUR';
    var storageKey = 'cachedExchangeRate';
    var storageDateKey = 'cachedExchangeRateDate';
    var storageRequestCountKey = 'apiRequestCount';
    var maxRequestsPerDay = 90;
    var refreshIntervalMinutes = 10;

    var currentMode = 'RUB';
    var currencyType = 'RUB';

    // Элемент для отображения курса евро
    var exchangeRateDisplay = document.createElement('p');
    exchangeRateDisplay.textContent = 'Загрузка курса евро...';
    exchangeRateDisplay.style.fontWeight = 'bold';
    exchangeRateDisplay.style.margin = '5px 0';
    exchangeRateDisplay.style.fontSize = '0.95em';
    calculatorContainer.appendChild(exchangeRateDisplay);

    // Поле для ввода курса валюты
    var exchangeRateField = document.createElement('input');
    exchangeRateField.type = 'number';
    exchangeRateField.placeholder = 'Введите курс вручную';
    exchangeRateField.step = '0.0001';
    exchangeRateField.style.width = '280px';
    exchangeRateField.style.margin = '5px 0';
    exchangeRateField.style.padding = '3px';
    exchangeRateField.style.fontSize = '0.9em';
    calculatorContainer.appendChild(exchangeRateField);

    // Поле для ввода суммы
    var amountField = document.createElement('input');
    amountField.type = 'number';
    amountField.placeholder = 'Введите сумму';
    amountField.style.width = '280px';
    amountField.style.margin = '5px 0';
    amountField.style.padding = '3px';
    amountField.style.fontSize = '0.9em';
    calculatorContainer.appendChild(amountField);

    // Кнопки выбора режима
    var modeContainer = document.createElement('div');
    modeContainer.style.margin = '5px 0';
    modeContainer.style.display = 'flex';
    modeContainer.style.justifyContent = 'space-between';
    modeContainer.style.gap = '5px';

    var rubModeButton = document.createElement('button');
    rubModeButton.textContent = 'Хотят рубли';
    rubModeButton.style.backgroundColor = 'lightgreen';
    rubModeButton.style.flex = '1';
    rubModeButton.style.padding = '4px';
    rubModeButton.style.fontSize = '0.9em';

    var eurModeButton = document.createElement('button');
    eurModeButton.textContent = 'Хотят евро';
    eurModeButton.style.flex = '1';
    eurModeButton.style.padding = '4px';
    eurModeButton.style.fontSize = '0.9em';

    rubModeButton.addEventListener('click', function() {
        currentMode = 'RUB';
        rubModeButton.style.backgroundColor = 'green';
        eurModeButton.style.backgroundColor = '';
     + ');

    eurModeButton.addEventListener('click', function() {
        currentMode = 'EUR';
        eurModeButton.style.backgroundColor = 'green';
        rubModeButton.style.backgroundColor = '';
     + ');

    modeContainer.appendChild(rubModeButton);
    modeContainer.appendChild(eurModeButton);
    calculatorContainer.appendChild(modeContainer);

    // Кнопки выбора типа валюты
    var currencyTypeContainer = document.createElement('div');
    currencyTypeContainer.style.margin = '5px 0';
    currencyTypeContainer.style.display = 'flex';
    currencyTypeContainer.style.justifyContent = 'space-between';
    currencyTypeContainer.style.gap = '5px';

    var rubleButton = document.createElement('button');
    rubleButton.textContent = 'Рубли';
    rubleButton.style.backgroundColor = 'lightgray';
    rubleButton.style.flex = '1';
    rubleButton.style.padding = '4px';
    rubleButton.style.fontSize = '0.9em';

    var euroButton = document.createElement('button');
    euroButton.textContent = 'Евро';
    euroButton.style.flex = '1';
    euroButton.style.padding = '4px';
    euroButton.style.fontSize = '0.9em';

    rubleButton.addEventListener('click', function() {
        currencyType = 'RUB';
        rubleButton.style.backgroundColor = 'green';
        euroButton.style.backgroundColor = '';
     + ');

    euroButton.addEventListener('click', function() {
        currencyType = 'EUR';
        euroButton.style.backgroundColor = 'green';
        rubleButton.style.backgroundColor = '';
     + ');

    currencyTypeContainer.appendChild(rubleButton);
    currencyTypeContainer.appendChild(euroButton);
    calculatorContainer.appendChild(currencyTypeContainer);

    // Контейнер для кнопок с процентами
    var percentageContainer = document.createElement('div');
    percentageContainer.style.margin = '5px 0';
    percentageContainer.style.display = 'flex';
    percentageContainer.style.justifyContent = 'space-between';
    percentageContainer.style.gap = '2px';

    // Массив процентов
    var percentages = [3, 4, 5, 7, 10];

    // Создаем кнопки для каждого процента
    percentages.forEach(function(percentage) {
        var button = document.createElement('button');
        button.textContent = percentage + '%';
        button.style.flex = '1';
        button.style.padding = '4px';
        button.style.fontSize = '0.9em';
        button.style.minWidth = '45px';

        button.addEventListener('click', function() {
            // Сбрасываем цвет всех кнопок
            Array.from(percentageContainer.children).forEach(function(btn) {
                btn.style.backgroundColor = '';
             + ');
            // Устанавливаем зеленый цвет для нажатой кнопки
            this.style.backgroundColor = 'green';

            var value = parseFloat(amountField.value);
            var exchangeRate = parseFloat(exchangeRateField.value) || parseFloat(exchangeRateDisplay.dataset.rate);

            if (!isNaN(value) && !isNaN(exchangeRate)) {
                var finalAmount, percentValue;
                var profitText;
                var resultText;

                if (currentMode === 'RUB') {
                    if (currencyType === 'EUR') {
                        percentValue = value * (percentage / 100);
                        finalAmount = (value - percentValue) * exchangeRate;
                        profitText = 'Ваш доход: ' + percentValue.toFixed(2) + ' евро';
                     + ' else {
                        percentValue = value * (percentage / 100);
                        finalAmount = value + percentValue / exchangeRate;
                        profitText = 'Ваш доход: ' + percentValue.toFixed(2) + ' руб.';
                     + ' + 
                 + ' else if (currentMode === 'EUR') {
                    if (currencyType === 'EUR') {
                        percentValue = value * (percentage / 100);
                        finalAmount = value + percentValue / exchangeRate;
                        profitText = 'Ваш доход: ' + percentValue.toFixed(2) + ' евро';
                     + ' else {
                        percentValue = value * (percentage / 100);
                        finalAmount = (value - percentValue) / exchangeRate;
                        profitText = 'Ваш доход: ' + percentValue.toFixed(2) + ' руб.';
                     + ' + 
                 + ' + 

                if (currentMode === 'EUR' && currencyType === 'RUB') {
                    resultText = 'Итог: ' + Math.round(finalAmount) + ' euro';
                 + ' else {
                    resultText = 'Итог: ' + Math.round(finalAmount) + ' руб.';
                 + ' + 

                displayResult(resultText, profitText);
             + ' + 
         + ');

        percentageContainer.appendChild(button);
     + ');

    calculatorContainer.appendChild(percentageContainer);

    // Контейнер для результатов
    var resultContainer = document.createElement('div');
    resultContainer.style.margin = '5px 0';
    resultContainer.style.borderTop = '1px solid #ccc';
    resultContainer.style.paddingTop = '5px';
    calculatorContainer.appendChild(resultContainer);

    // Функция отображения результата
    function displayResult(resultText, profitText) {
        resultContainer.innerHTML = '';

        var resultElement = document.createElement('p');
        resultElement.textContent = resultText;
        resultElement.style.margin = '5px 0';
        resultElement.style.fontSize = '0.95em';
        resultContainer.appendChild(resultElement);

        var profitElement = document.createElement('p');
        profitElement.textContent = profitText;
        profitElement.style.color = 'green';
        profitElement.style.fontWeight = 'bold';
        profitElement.style.margin = '5px 0';
        profitElement.style.fontSize = '0.95em';
        resultContainer.appendChild(profitElement);

        var buttonContainer = document.createElement('div');
        buttonContainer.style.display = 'flex';
        buttonContainer.style.justifyContent = 'space-between';
        buttonContainer.style.gap = '5px';
        buttonContainer.style.margin = '5px 0';

        var copyButton = document.createElement('button');
        copyButton.textContent = 'Скопировать Итог';
        copyButton.style.flex = '1';
        copyButton.style.padding = '4px';
        copyButton.style.fontSize = '0.9em';
        copyButton.addEventListener('click', function() {
            var match = resultText.match(/(\d+)\s*(руб\.|euro)/);
            if (match) {
                var textToCopy = match[1] + ' ' + match[2];
                copyToClipboard(textToCopy, copyButton);
             + ' + 
         + ');

        var copyProfitButton = document.createElement('button');
        copyProfitButton.textContent = 'Скопировать Доход';
        copyProfitButton.style.flex = '1';
        copyProfitButton.style.padding = '4px';
        copyProfitButton.style.fontSize = '0.9em';
        copyProfitButton.addEventListener('click', function() {
            var match = profitText.match(/(\d+\.?\d*)/);
            if (match) {
                var textToCopy = match[1].replace('.', ',');
                copyToClipboard(textToCopy, copyProfitButton);
             + ' + 
         + ');

        buttonContainer.appendChild(copyButton);
        buttonContainer.appendChild(copyProfitButton);
        resultContainer.appendChild(buttonContainer);
     + ' + 

    // Функция для отображения ошибок
    function displayError(errorText) {
        resultContainer.innerHTML = '';
        var errorElement = document.createElement('p');
        errorElement.style.color = 'red';
        errorElement.style.margin = '5px 0';
        errorElement.style.fontSize = '0.9em';
        errorElement.textContent = errorText;
        resultContainer.appendChild(errorElement);
     + ' + 

    // Функция копирования в буфер обмена
    function copyToClipboard(text, button) {
        var tempInput = document.createElement('input');
        tempInput.value = text;
        document.body.appendChild(tempInput);
        tempInput.select();
        document.execCommand('copy');
        document.body.removeChild(tempInput);

        button.style.backgroundColor = 'green';
        setTimeout(() => {
            button.style.backgroundColor = '';
         + ', 1500);
     + ' + 

    // Функция получения курса валют
    async function fetchAndCacheExchangeRate() {
        var cachedRate = localStorage.getItem(storageKey);
        var cachedDate = localStorage.getItem(storageDateKey);
        var requestCount = parseInt(localStorage.getItem(storageRequestCountKey) || '0', 10);
        var now = new Date();
        var today = now.toISOString().split('T')[0];

        if (cachedDate !== today) {
            localStorage.setItem(storageRequestCountKey, '0');
         + ' + 

        if (cachedRate && cachedDate === today) {
            exchangeRateDisplay.textContent = 'Курс евро: ' + cachedRate + ' руб.';
            exchangeRateDisplay.dataset.rate = cachedRate;
         + ' else if (requestCount < maxRequestsPerDay) {
            try {
                var response = await fetch(apiUrl);
                var data = await response.json();

                if (data && data.rates && data.rates.RUB && data.rates.EUR) {
                    var euroToRubRate = (data.rates.RUB / data.rates.EUR).toFixed(4);
                    exchangeRateDisplay.textContent = 'Курс евро: ' + euroToRubRate + ' руб.';
                    exchangeRateDisplay.dataset.rate = euroToRubRate;
                    localStorage.setItem(storageKey, euroToRubRate);
                    localStorage.setItem(storageDateKey, today);
                    localStorage.setItem(storageRequestCountKey, (requestCount + 1).toString());
                 + ' else {
                    displayError('Ошибка при получении курса евро.');
                 + ' + 
             + ' catch (error) {
                console.error('Ошибка при запросе к Open Exchange Rates:', error);
                displayError('Ошибка при загрузке курса.');
             + ' + 
         + ' else {
            displayError('Лимит запросов на сегодня исчерпан.');
         + ' + 
     + ' + 

    // Запускаем первичную загрузку курса
    fetchAndCacheExchangeRate();

    // Обновляем курс каждые 10 минут
    setInterval(fetchAndCacheExchangeRate, refreshIntervalMinutes * 30 * 1000);

    // Добавляем калькулятор на страницу
    document.body.appendChild(calculatorContainer);

 + ')();
// Обход CORS
function loadScriptWithCORS(url, onSuccess, onError) {
    GM.xmlHttpRequest({
        method: 'GET',
        url: url,
        onload: function(response) {
            if (response.status === 200) {
                console.log('Скрипт загружен:', url);
                onSuccess(response.responseText);
            } else {
                console.error('Ошибка загрузки скрипта:', url, response.status);
                if (onError) onError(response.status);
            }
        },
        onerror: function(error) {
            console.error('Ошибка сети:', error);
            if (onError) onError(error);
        }
    });
}