YouTube SuperChat貨幣轉換器

將各種國際貨幣金額轉為你所在地區的幣種(如CNY,HKD,NTD,USD,JPY等)

目前為 2024-07-27 提交的版本,檢視 最新版本

// ==UserScript==
// @name         YouTube SuperChat货币转换器
// @name:zh-CN   YouTube SuperChat货币转换器
// @name:zh-TW   YouTube SuperChat貨幣轉換器
// @name:zh-HK   YouTube SuperChat貨幣轉換器

// @description         将各种国际货币金额转为你所在地区的币种(如CNY,HKD,NTD,USD,JPY等)
// @description:zh-CN   将各种国际货币金额转为你所在地区的币种(如CNY,HKD,NTD,USD,JPY等)
// @description:zh-TW   將各種國際貨幣金額轉為你所在地區的幣種(如CNY,HKD,NTD,USD,JPY等)
// @description:zh-HK   將各種國際貨幣金額轉為你所在地區的幣種(如CNY,HKD,NTD,USD,JPY等)


// @namespace    http://tampermonkey.net/
// @version      1.6
// @author       kksskkoopp
// @match        https://www.youtube.com/live_chat_replay*
// @match        https://www.youtube.com/live_chat*
// @grant        GM_xmlhttpRequest
// @require      https://code.jquery.com/jquery-3.6.0.min.js
// @connect      v6.exchangerate-api.com
// @license      MIT
// @run-at       document-start
// ==/UserScript==
/* globals  $ */
(function() {
    'use strict';

    // 汇率API信息
    let chinese_name_font_size = 7; // 中文货币名 字体大小
    let converted_currency_font_size = 10; // 转换后的货币金额 字体大小

    let targetCurrencyCode = 'CNY'; // 目标币种
    let targetCurrencyPrecision = 2 // 目标币种保留的小数位数;
    // API密钥请在 https://www.exchangerate-api.com/ 免费获取:邮箱注册->一键激活->复制API密钥->添加到下面的的API_KEY
    let API_KEY = [
    '0c719520222e5b86cf26f479',
    'd6aa54b3dd223922e629fcc8',
    '064bacfc0b0c5d7fe525b541',
    '35d768f3c2f9e84a00116085',
    '975287c29230eca269117358'
    
    ]; // *** API密钥 *** (将API_KEY添加到方括号内,不要替换下面API_URL中的API_KEY)
    
    let rand = Math.floor(Math.random() * API_KEY.length);
    let API_URL = `https://v6.exchangerate-api.com/v6/${API_KEY[rand]}/latest/${targetCurrencyCode}`; // API地址
    let API_QUOTA_URL = `https://v6.exchangerate-api.com/v6/${API_KEY[rand]}/quota` // 剩余额度查询地址

    // YoutubeSuperChat货币符号 : ISO-4217货币码
    let currencyMap = {
    '$': { ISO_code: 'USD', chinese_name: '美元' },
    '¥': { ISO_code: 'JPY', chinese_name: '日元' },
    '₱': { ISO_code: 'PHP', chinese_name: '菲律宾比索' },
    '£': { ISO_code: 'GBP', chinese_name: '英镑' },
    '€': { ISO_code: 'EUR', chinese_name: '欧元' },
    '₩': { ISO_code: 'KRW', chinese_name: '韩元' },
    '₽': { ISO_code: 'RUB', chinese_name: '俄罗斯卢布' },
    '₹': { ISO_code: 'INR', chinese_name: '印度卢比' },
    '฿': { ISO_code: 'THB', chinese_name: '泰铢' },
    '₫': { ISO_code: 'VND', chinese_name: '越南盾' },

    'A$': { ISO_code: 'AUD', chinese_name: '澳大利亚元' },
    'AU$': { ISO_code: 'AUD', chinese_name: '澳大利亚元' },
    'NZ$': { ISO_code: 'NZD', chinese_name: '新西兰元' },
    'R$': { ISO_code: 'BRL', chinese_name: '巴西雷亚尔' },
    'HK$': { ISO_code: 'HKD', chinese_name: '港币' },
    'NT$': { ISO_code: 'TWD', chinese_name: '新台币' },
    'CA$': { ISO_code: 'CAD', chinese_name: '加拿大元' },
    'US$': { ISO_code: 'USD', chinese_name: '美元' },
    'JP¥': { ISO_code: 'JPY', chinese_name: '日元' },

    'AED': { ISO_code: 'AED', chinese_name: '阿联酋迪拉姆' },
    'AFN': { ISO_code: 'AFN', chinese_name: '阿富汗尼' },
    'ALL': { ISO_code: 'ALL', chinese_name: '阿尔巴尼亚列克' },
    'AMD': { ISO_code: 'AMD', chinese_name: '亚美尼亚德拉姆' },
    'ANG': { ISO_code: 'ANG', chinese_name: '荷属安的列斯盾' },
    'AOA': { ISO_code: 'AOA', chinese_name: '安哥拉宽扎' },
    'ARS': { ISO_code: 'ARS', chinese_name: '阿根廷比索' },
    'AUD': { ISO_code: 'AUD', chinese_name: '澳大利亚元' },
    'AWG': { ISO_code: 'AWG', chinese_name: '阿鲁巴弗罗林' },
    'AZN': { ISO_code: 'AZN', chinese_name: '阿塞拜疆马纳特' },
    'BAM': { ISO_code: 'BAM', chinese_name: '波斯尼亚和黑塞哥维那马克' },
    'BBD': { ISO_code: 'BBD', chinese_name: '巴巴多斯元' },
    'BDT': { ISO_code: 'BDT', chinese_name: '孟加拉塔卡' },
    'BGN': { ISO_code: 'BGN', chinese_name: '保加利亚列弗' },
    'BHD': { ISO_code: 'BHD', chinese_name: '巴林第纳尔' },
    'BIF': { ISO_code: 'BIF', chinese_name: '布隆迪法郎' },
    'BMD': { ISO_code: 'BMD', chinese_name: '百慕大元' },
    'BND': { ISO_code: 'BND', chinese_name: '文莱元' },
    'BOB': { ISO_code: 'BOB', chinese_name: '玻利维亚诺' },
    'BRL': { ISO_code: 'BRL', chinese_name: '巴西雷亚尔' },
    'BSD': { ISO_code: 'BSD', chinese_name: '巴哈马元' },
    'BTN': { ISO_code: 'BTN', chinese_name: '不丹努扎姆' },
    'BWP': { ISO_code: 'BWP', chinese_name: '博茨瓦纳普拉' },
    'BYN': { ISO_code: 'BYN', chinese_name: '白俄罗斯卢布' },
    'BZD': { ISO_code: 'BZD', chinese_name: '伯利兹元' },
    'CAD': { ISO_code: 'CAD', chinese_name: '加拿大元' },
    'CDF': { ISO_code: 'CDF', chinese_name: '刚果法郎' },
    'CHF': { ISO_code: 'CHF', chinese_name: '瑞士法郎' },
    'CLP': { ISO_code: 'CLP', chinese_name: '智利比索' },
    'CNY': { ISO_code: 'CNY', chinese_name: '人民币' },
    'COP': { ISO_code: 'COP', chinese_name: '哥伦比亚比索' },
    'CRC': { ISO_code: 'CRC', chinese_name: '哥斯达黎加科朗' },
    'CUP': { ISO_code: 'CUP', chinese_name: '古巴比索' },
    'CVE': { ISO_code: 'CVE', chinese_name: '佛得角埃斯库多' },
    'CZK': { ISO_code: 'CZK', chinese_name: '捷克克朗' },
    'DJF': { ISO_code: 'DJF', chinese_name: '吉布提法郎' },
    'DKK': { ISO_code: 'DKK', chinese_name: '丹麦克朗' },
    'DOP': { ISO_code: 'DOP', chinese_name: '多米尼加比索' },
    'DZD': { ISO_code: 'DZD', chinese_name: '阿尔及利亚第纳尔' },
    'EGP': { ISO_code: 'EGP', chinese_name: '埃及镑' },
    'ERN': { ISO_code: 'ERN', chinese_name: '厄立特里亚纳克法' },
    'ETB': { ISO_code: 'ETB', chinese_name: '埃塞俄比亚比尔' },
    'EUR': { ISO_code: 'EUR', chinese_name: '欧元' },
    'FJD': { ISO_code: 'FJD', chinese_name: '斐济元' },
    'FKP': { ISO_code: 'FKP', chinese_name: '福克兰群岛镑' },
    'FOK': { ISO_code: 'FOK', chinese_name: '法罗克朗' },
    'GBP': { ISO_code: 'GBP', chinese_name: '英镑' },
    'GEL': { ISO_code: 'GEL', chinese_name: '格鲁吉亚拉里' },
    'GGP': { ISO_code: 'GGP', chinese_name: '根西镑' },
    'GHS': { ISO_code: 'GHS', chinese_name: '加纳塞地' },
    'GIP': { ISO_code: 'GIP', chinese_name: '直布罗陀镑' },
    'GMD': { ISO_code: 'GMD', chinese_name: '冈比亚达拉西' },
    'GNF': { ISO_code: 'GNF', chinese_name: '几内亚法郎' },
    'GTQ': { ISO_code: 'GTQ', chinese_name: '危地马拉格查尔' },
    'GYD': { ISO_code: 'GYD', chinese_name: '圭亚那元' },
    'HKD': { ISO_code: 'HKD', chinese_name: '港币' },
    'HNL': { ISO_code: 'HNL', chinese_name: '洪都拉斯伦皮拉' },
    'HRK': { ISO_code: 'HRK', chinese_name: '克罗地亚库纳' },
    'HTG': { ISO_code: 'HTG', chinese_name: '海地古德' },
    'HUF': { ISO_code: 'HUF', chinese_name: '匈牙利福林' },
    'IDR': { ISO_code: 'IDR', chinese_name: '印尼卢比' },
    'ILS': { ISO_code: 'ILS', chinese_name: '以色列新谢克尔' },
    'IMP': { ISO_code: 'IMP', chinese_name: '马恩岛镑' },
    'INR': { ISO_code: 'INR', chinese_name: '印度卢比' },
    'IQD': { ISO_code: 'IQD', chinese_name: '伊拉克第纳尔' },
    'IRR': { ISO_code: 'IRR', chinese_name: '伊朗里亚尔' },
    'ISK': { ISO_code: 'ISK', chinese_name: '冰岛克朗' },
    'JEP': { ISO_code: 'JEP', chinese_name: '泽西镑' },
    'JMD': { ISO_code: 'JMD', chinese_name: '牙买加元' },
    'JOD': { ISO_code: 'JOD', chinese_name: '约旦第纳尔' },
    'JPY': { ISO_code: 'JPY', chinese_name: '日元' },
    'KES': { ISO_code: 'KES', chinese_name: '肯尼亚先令' },
    'KGS': { ISO_code: 'KGS', chinese_name: '吉尔吉斯斯坦索姆' },
    'KHR': { ISO_code: 'KHR', chinese_name: '柬埔寨瑞尔' },
    'KID': { ISO_code: 'KID', chinese_name: '基里巴斯元' },
    'KMF': { ISO_code: 'KMF', chinese_name: '科摩罗法郎' },
    'KRW': { ISO_code: 'KRW', chinese_name: '韩元' },
    'KWD': { ISO_code: 'KWD', chinese_name: '科威特第纳尔' },
    'KYD': { ISO_code: 'KYD', chinese_name: '开曼群岛元' },
    'KZT': { ISO_code: 'KZT', chinese_name: '哈萨克斯坦坚戈' },
    'LAK': { ISO_code: 'LAK', chinese_name: '老挝基普' },
    'LBP': { ISO_code: 'LBP', chinese_name: '黎巴嫩镑' },
    'LKR': { ISO_code: 'LKR', chinese_name: '斯里兰卡卢比' },
    'LRD': { ISO_code: 'LRD', chinese_name: '利比里亚元' },
    'LSL': { ISO_code: 'LSL', chinese_name: '莱索托洛蒂' },
    'LYD': { ISO_code: 'LYD', chinese_name: '利比亚第纳尔' },
    'MAD': { ISO_code: 'MAD', chinese_name: '摩洛哥迪拉姆' },
    'MDL': { ISO_code: 'MDL', chinese_name: '摩尔多瓦列伊' },
    'MGA': { ISO_code: 'MGA', chinese_name: '马达加斯加阿里亚里' },
    'MKD': { ISO_code: 'MKD', chinese_name: '马其顿第纳尔' },
    'MMK': { ISO_code: 'MMK', chinese_name: '缅元' },
    'MNT': { ISO_code: 'MNT', chinese_name: '蒙古图格里克' },
    'MOP': { ISO_code: 'MOP', chinese_name: '澳门元' },
    'MRU': { ISO_code: 'MRU', chinese_name: '毛里塔尼亚乌吉亚' },
    'MUR': { ISO_code: 'MUR', chinese_name: '毛里求斯卢比' },
    'MVR': { ISO_code: 'MVR', chinese_name: '马尔代夫拉菲亚' },
    'MWK': { ISO_code: 'MWK', chinese_name: '马拉维克瓦查' },
    'MXN': { ISO_code: 'MXN', chinese_name: '墨西哥比索' },
    'MYR': { ISO_code: 'MYR', chinese_name: '马来西亚林吉特' },
    'MZN': { ISO_code: 'MZN', chinese_name: '莫桑比克梅蒂卡尔' },
    'NAD': { ISO_code: 'NAD', chinese_name: '纳米比亚元' },
    'NGN': { ISO_code: 'NGN', chinese_name: '尼日利亚奈拉' },
    'NIO': { ISO_code: 'NIO', chinese_name: '尼加拉瓜科多巴' },
    'NOK': { ISO_code: 'NOK', chinese_name: '挪威克朗' },
    'NPR': { ISO_code: 'NPR', chinese_name: '尼泊尔卢比' },
    'NZD': { ISO_code: 'NZD', chinese_name: '新西兰元' },
    'OMR': { ISO_code: 'OMR', chinese_name: '阿曼里亚尔' },
    'PAB': { ISO_code: 'PAB', chinese_name: '巴拿马巴波亚' },
    'PEN': { ISO_code: 'PEN', chinese_name: '秘鲁新索尔' },
    'PGK': { ISO_code: 'PGK', chinese_name: '巴布亚新几内亚基那' },
    'PHP': { ISO_code: 'PHP', chinese_name: '菲律宾比索' },
    'PKR': { ISO_code: 'PKR', chinese_name: '巴基斯坦卢比' },
    'PLN': { ISO_code: 'PLN', chinese_name: '波兰兹罗提' },
    'PYG': { ISO_code: 'PYG', chinese_name: '巴拉圭瓜拉尼' },
    'QAR': { ISO_code: 'QAR', chinese_name: '卡塔尔里亚尔' },
    'RON': { ISO_code: 'RON', chinese_name: '罗马尼亚列伊' },
    'RSD': { ISO_code: 'RSD', chinese_name: '塞尔维亚第纳尔' },
    'RUB': { ISO_code: 'RUB', chinese_name: '俄罗斯卢布' },
    'RWF': { ISO_code: 'RWF', chinese_name: '卢旺达法郎' },
    'SAR': { ISO_code: 'SAR', chinese_name: '沙特里亚尔' },
    'SBD': { ISO_code: 'SBD', chinese_name: '所罗门群岛元' },
    'SCR': { ISO_code: 'SCR', chinese_name: '塞舌尔卢比' },
    'SDG': { ISO_code: 'SDG', chinese_name: '苏丹镑' },
    'SEK': { ISO_code: 'SEK', chinese_name: '瑞典克朗' },
    'SGD': { ISO_code: 'SGD', chinese_name: '新加坡元' },
    'SHP': { ISO_code: 'SHP', chinese_name: '圣赫勒拿镑' },
    'SLE': { ISO_code: 'SLE', chinese_name: '塞拉利昂利昂' },
    'SOS': { ISO_code: 'SOS', chinese_name: '索马里先令' },
    'SRD': { ISO_code: 'SRD', chinese_name: '苏里南元' },
    'SSP': { ISO_code: 'SSP', chinese_name: '南苏丹镑' },
    'STN': { ISO_code: 'STN', chinese_name: '圣多美和普林西比多布拉' },
    'SYP': { ISO_code: 'SYP', chinese_name: '叙利亚镑' },
    'SZL': { ISO_code: 'SZL', chinese_name: '斯威士兰里兰吉尼' },
    'THB': { ISO_code: 'THB', chinese_name: '泰铢' },
    'TJS': { ISO_code: 'TJS', chinese_name: '塔吉克斯坦索莫尼' },
    'TMT': { ISO_code: 'TMT', chinese_name: '土库曼斯坦马纳特' },
    'TND': { ISO_code: 'TND', chinese_name: '突尼斯第纳尔' },
    'TOP': { ISO_code: 'TOP', chinese_name: '汤加潘加' },
    'TRY': { ISO_code: 'TRY', chinese_name: '土耳其里拉' },
    'TTD': { ISO_code: 'TTD', chinese_name: '特立尼达和多巴哥元' },
    'TVD': { ISO_code: 'TVD', chinese_name: '图瓦卢元' },
    'TWD': { ISO_code: 'TWD', chinese_name: '新台币' },
    'TZS': { ISO_code: 'TZS', chinese_name: '坦桑尼亚先令' },
    'UAH': { ISO_code: 'UAH', chinese_name: '乌克兰格里夫纳' },
    'UGX': { ISO_code: 'UGX', chinese_name: '乌干达先令' },
    'USD': { ISO_code: 'USD', chinese_name: '美元' },
    'UYU': { ISO_code: 'UYU', chinese_name: '乌拉圭比索' },
    'UZS': { ISO_code: 'UZS', chinese_name: '乌兹别克斯坦苏姆' },
    'VES': { ISO_code: 'VES', chinese_name: '委内瑞拉主权玻利瓦尔' },
    'VND': { ISO_code: 'VND', chinese_name: '越南盾' },
    'VUV': { ISO_code: 'VUV', chinese_name: '瓦努阿图瓦图' },
    'WST': { ISO_code: 'WST', chinese_name: '萨摩亚塔拉' },
    'XAF': { ISO_code: 'XAF', chinese_name: '中非法郎' },
    'XCD': { ISO_code: 'XCD', chinese_name: '东加勒比元' },
    'XDR': { ISO_code: 'XDR', chinese_name: '特别提款权' },
    'XOF': { ISO_code: 'XOF', chinese_name: '西非法郎' },
    'XPF': { ISO_code: 'XPF', chinese_name: '太平洋法郎' },
    'YER': { ISO_code: 'YER', chinese_name: '也门里亚尔' },
    'ZAR': { ISO_code: 'ZAR', chinese_name: '南非兰特' },
    'ZMW': { ISO_code: 'ZMW', chinese_name: '赞比亚克瓦查' },
    'ZWL': { ISO_code: 'ZWL', chinese_name: '津巴布韦元' }
    };

    let exchangeRates = {};
    //  计算公式: targetCurrencyAmount = fromCurrencyAmount * exchangeRates[fromCurrenyCode]

    // 获取汇率信息
    function fetchExchangeRates(callback) {

        // 查询剩余额度
        GM_xmlhttpRequest({
            method: 'GET',
            url: API_QUOTA_URL,
            onload: function(response) {
                let data = JSON.parse(response.responseText);
                if (data['result'] == 'success')
                {
                    console.log(`每月计划额度:${data['plan_quota']}, 剩余额度:${data['requests_remaining']}`);
                }
                else if (data['result'] == 'error')
                {
                    // 详情见 https://www.exchangerate-api.com/docs/request-quota-endpoint
                    if (data['error-type'] === 'invalid-key') {
                        console.log("错误:无效的API密钥。");
                    } else if (data['error-type'] === 'inactive-account') {
                        console.log("错误:账户未激活,请在您的电子邮件激活。");
                    } else if (data['error-type'] === 'quota-reached') {
                        console.log("错误:无剩余查询额度,您的全部查询额度已使用完毕。");
                    }
                }
            },
            onerror: function() {
                console.error('错误:脚本无法连接到汇率接口 https://www.exchangerate-api.com/');
            }
        });

        console.log(`开始请求汇率${API_URL}`);
        GM_xmlhttpRequest({
            method: 'GET',
            url: API_URL,
            onload: function(response) {
                let data = JSON.parse(response.responseText);
                if (data['result'] == 'success')
                {
                    exchangeRates = data['conversion_rates'];
                    let datetime = new Date();
                    console.log(`今日汇率 ${datetime.toLocaleDateString()} ${datetime.toLocaleTimeString()}`);
                    console.log(exchangeRates);
                    console.log('汇率请求完毕');
                    callback();
                }
                else if (data['result'] == 'error')
                {
                    // 详情见 https://www.exchangerate-api.com/docs/standard-requests
                    if (data['error-type'] === 'unsupported-code') {
                        console.log("错误:不支持的币种。");
                    } else if (data['error-type'] === 'malformed-request') {
                        console.log("错误:请求格式错误。");
                    } else if (data['error-type'] === 'invalid-key') {
                        console.log("错误:无效的API密钥。");
                    } else if (data['error-type'] === 'inactive-account') {
                        console.log("错误:账户未激活,请在您的电子邮件激活。");
                    } else if (data['error-type'] === 'quota-reached') {
                        console.log("错误:无剩余查询额度,您的全部查询额度已使用完毕。");
                    }
                }
            },
            onerror: function() {
                console.error('错误:脚本无法连接到汇率接口 https://www.exchangerate-api.com/');
            }
        });
    }


    // 解析金额和货币类型
    function parseAmountAndCurrency(text) {
        // 去除空白字符
        text = text.replace(/\s+/g, '');
        // 遍历currencyMap字典,找到匹配的货币符号
        for (const [symbol, info] of Object.entries(currencyMap)) {
            if (text.startsWith(symbol)) {
                const amount = parseFloat(text.replace(symbol, '').replace(/,/g, '')); // 提取金额并去除千位分隔符
                return { amount, fromCurrencyCode: info.ISO_code, chinese_name: info.chinese_name };
            }
        }
        console.error(`不支持的币种${text}.`);
        return null;
    }

    // 观察DOM变化并转换SuperChat金额
    function observeSuperChat() {

        let targetNode = document.body;
        let config = { childList: true, subtree: true };

        let callback = function(mutationsList, observer) {
            for(let mutation of mutationsList) {
                if (mutation.type === 'childList') {
                    // 遍历新增的节点,检查是否包含SuperChat消息
                    $(mutation.addedNodes).each(function() {
                        let element = $(this).find('yt-formatted-string.style-scope.yt-live-chat-paid-message-renderer, yt-formatted-string.style-scope.yt-live-chat-paid-sticker-renderer').not('#deleted-state, #dashboard-deleted-state');
                        if (element.length > 0) {
                            // 处理每个SuperChat消息
                            element.each(function() {
                                if (!$(this).attr('YoutubeSuperChatCurrencyConverter-converted')) {

                                    let message = $(this).text().trim();
                                    console.log(`捕获到超级留言[${message}]`);
                                    let parsed = parseAmountAndCurrency(message);
                                    if (parsed) {
                                        console.log(`解析成功${message}:${parsed.fromCurrencyCode} ${parsed.amount}`);
                                        // 获取汇率并计算转换后的金额
                                        let targetCurrencyAmount = parsed.amount / exchangeRates[parsed.fromCurrencyCode];
                                        let targetCurrencyAmount_str = targetCurrencyAmount.toFixed(targetCurrencyPrecision);

                                        const additionalInfo = `
                                            <div class="split-text" style="display: inline-block; white-space: no-wrap; text-align: left;">
                                                <div style="font-size: ${chinese_name_font_size}px; margin-bottom: 1px;">${parsed.chinese_name}</div>
                                                <div style="font-size: ${converted_currency_font_size}px;  margin-top: 1px;">=${targetCurrencyCode} ${targetCurrencyAmount_str}</div>
                                            </div>`;

                                        $(this).html(message + additionalInfo);
                                        $(this).attr('YoutubeSuperChatCurrencyConverter-converted', 'true'); // 标记为已处理

                                    } else {
                                        console.log(`解析失败[${message}]`);
                                    }
                                }
                            });
                        }
                    });
                }
            }
        };

        // 创建并启动MutationObserver
        let observer = new MutationObserver(callback);
        observer.observe(targetNode, config);
    }

    // 启动观察器
    $(document).ready(fetchExchangeRates(observeSuperChat));
})();