您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Extremely simple $RBLX & Robux price conversion widget with multiple currencies.
// ==UserScript== // @name Tix Pro (for Roblox) // @namespace rblx.cash // @version 1337 // @description Extremely simple $RBLX & Robux price conversion widget with multiple currencies. // @author mobs2r // @match https://www.roblox.com/* // @grant GM_xmlhttpRequest // @grant GM_getValue // @grant GM_setValue // @grant GM_addStyle // @connect query1.finance.yahoo.com // @connect api.exchangerate-api.com // @connect api.frankfurter.app // ==/UserScript== (function() { 'use strict'; const ROBUX_RATE_USD = 0.0035; const CURRENCIES = { USD: { symbol: '$', name: 'US Dollar', flag: '🇺🇸' }, EUR: { symbol: '€', name: 'Euro', flag: '🇪🇺' }, GBP: { symbol: '£', name: 'British Pound', flag: '🇬🇧' }, CAD: { symbol: '$', name: 'Canadian Dollar', flag: '🇨🇦' }, AUD: { symbol: '$', name: 'Australian Dollar', flag: '🇦🇺' } }; function createTicker() { const tickerContainer = document.createElement('div'); tickerContainer.id = 'rblx-ticker-container'; tickerContainer.style.cssText = `position:fixed;top:15px;right:15px;background:rgba(30,30,30,0.95);border-radius:8px;z-index:9999;box-shadow:0 4px 20px rgba(0,0,0,0.5);border:1px solid #3a3a3a;width:40px;height:40px;font-family:'Gotham SSm A','Gotham SSm B',Arial,sans-serif;cursor:move;transition:all 0.2s ease;overflow:hidden;display:flex;flex-direction:column;align-items:center;`; const toggleBtn = document.createElement('div'); toggleBtn.id = 'rblx-toggle'; toggleBtn.style.cssText = `width:100%;height:40px;display:flex;align-items:center;justify-content:center;position:relative;`; const logoContainer = document.createElement('div'); logoContainer.id = 'rblx-logo-container'; logoContainer.style.cssText = `display:flex;align-items:center;justify-content:center;width:24px;height:24px;cursor:pointer;`; const logoSvg = document.createElementNS('http://www.w3.org/2000/svg','svg'); logoSvg.setAttribute('width','24'); logoSvg.setAttribute('height','24'); logoSvg.setAttribute('viewBox','0 0 24 24'); logoSvg.id = 'rblx-logo'; logoSvg.style.cssText = 'transition:transform 0.2s;'; const rect = document.createElementNS('http://www.w3.org/2000/svg','rect'); rect.setAttribute('x','2'); rect.setAttribute('y','2'); rect.setAttribute('width','20'); rect.setAttribute('height','20'); rect.setAttribute('rx','4'); rect.setAttribute('fill','#00ff88'); const text = document.createElementNS('http://www.w3.org/2000/svg','text'); text.setAttribute('x','12'); text.setAttribute('y','12'); text.setAttribute('text-anchor','middle'); text.setAttribute('dominant-baseline','middle'); text.setAttribute('font-size','14'); text.setAttribute('font-weight','bold'); text.setAttribute('fill','#1e1e1e'); text.textContent = 'R'; logoSvg.appendChild(rect); logoSvg.appendChild(text); logoContainer.appendChild(logoSvg); const title = document.createElement('span'); title.id = 'rblx-title'; title.textContent = 'Tix Pro'; title.style.cssText = `display:none;font-size:14px;font-weight:bold;margin-left:5px;color:#00ff88;text-transform:uppercase;letter-spacing:0.5px;`; toggleBtn.appendChild(logoContainer); toggleBtn.appendChild(title); const content = document.createElement('div'); content.id = 'rblx-content'; content.style.cssText = `display:none;width:100%;padding:15px;`; const stockRow = document.createElement('div'); stockRow.style.cssText = `display:flex;justify-content:space-between;margin-bottom:12px;`; const stockLabel = document.createElement('span'); stockLabel.textContent = '$RBLX:'; stockLabel.style.cssText = `font-size:14px;color:#b8b8b8;`; const stockValue = document.createElement('span'); stockValue.id = 'rblx-stock-value'; stockValue.textContent = 'Loading...'; stockValue.style.cssText = `font-size:14px;font-weight:700;color:#fff;`; stockRow.appendChild(stockLabel); stockRow.appendChild(stockValue); const robuxRow = document.createElement('div'); robuxRow.style.cssText = `display:flex;justify-content:space-between;margin-bottom:15px;`; const robuxLabel = document.createElement('span'); robuxLabel.textContent = 'Robux:'; robuxLabel.style.cssText = `font-size:14px;color:#b8b8b8;`; const robuxValue = document.createElement('span'); robuxValue.id = 'rblx-robux-value'; robuxValue.textContent = 'Loading...'; robuxValue.style.cssText = `font-size:14px;font-weight:700;color:#fff;display:flex;align-items:center;`; const robuxCurrencyIcon = document.createElement('span'); robuxCurrencyIcon.id = 'rblx-robux-currency'; robuxCurrencyIcon.style.cssText = `margin-right:4px;font-size:12px;`; robuxValue.prepend(robuxCurrencyIcon); robuxRow.appendChild(robuxLabel); robuxRow.appendChild(robuxValue); const currencyRow = document.createElement('div'); currencyRow.style.cssText = `display:flex;justify-content:space-between;align-items:center;margin-top:15px;padding-top:15px;border-top:1px solid #3a3a3a;`; const currencyLabel = document.createElement('span'); currencyLabel.textContent = 'Currency:'; currencyLabel.style.cssText = `font-size:13px;color:#b8b8b8;`; const currencySwitcher = document.createElement('div'); currencySwitcher.id = 'rblx-currency-switcher'; currencySwitcher.style.cssText = `display:flex;gap:8px;`; Object.keys(CURRENCIES).forEach(currency => { const flag = document.createElement('span'); flag.textContent = CURRENCIES[currency].flag; flag.dataset.currency = currency; flag.style.cssText = `cursor:pointer;font-size:18px;transition:transform 0.2s;`; flag.addEventListener('click',function(e){e.stopPropagation();switchCurrency(currency);setTimeout(()=>location.reload(),300);}); currencySwitcher.appendChild(flag); }); currencyRow.appendChild(currencyLabel); currencyRow.appendChild(currencySwitcher); content.appendChild(stockRow); content.appendChild(robuxRow); content.appendChild(currencyRow); tickerContainer.appendChild(toggleBtn); tickerContainer.appendChild(content); document.body.appendChild(tickerContainer); makeDraggable(tickerContainer); logoContainer.addEventListener('click',function(e){e.stopPropagation();toggleDockedState(tickerContainer);}); return{container:tickerContainer,stockValue,robuxValue,robuxCurrencyIcon,currencySwitcher}; } function makeDraggable(element){ let isDragging=false;let offsetX,offsetY; element.addEventListener('mousedown',function(e){ if(e.target.id==='rblx-logo-container'||e.target.id==='rblx-logo'||e.target.tagName==='text')return; isDragging=true; offsetX=e.clientX-element.getBoundingClientRect().left; offsetY=e.clientY-element.getBoundingClientRect().top; element.style.cursor='grabbing'; element.style.zIndex='10000'; element.style.boxShadow='0 10px 30px rgba(0,0,0,0.5)'; element.style.transition='none'; e.preventDefault(); }); document.addEventListener('mousemove',function(e){ if(!isDragging)return; const x=e.clientX-offsetX; const y=e.clientY-offsetY; const maxX=window.innerWidth-element.offsetWidth; const maxY=window.innerHeight-element.offsetHeight; element.style.left=Math.max(0,Math.min(maxX,x))+'px'; element.style.top=Math.max(0,Math.min(maxY,y))+'px'; element.style.right='auto'; element.style.bottom='auto'; }); document.addEventListener('mouseup',function(){ if(!isDragging)return; isDragging=false; element.style.cursor='move'; element.style.zIndex='9999'; element.style.boxShadow='0 4px 20px rgba(0,0,0,0.5)'; element.style.transition='all 0.2s ease'; GM_setValue('widgetPosition',{left:element.style.left,top:element.style.top,open:element.classList.contains('open')}); }); } function toggleDockedState(element){ if(element.style.transition==='none')return; const savedPosition=GM_getValue('widgetPosition'); let left=parseInt(savedPosition?.left||'15px',10); let top=parseInt(savedPosition?.top||'15px',10); if(element.classList.contains('open')){ element.classList.remove('open'); element.style.width='40px'; element.style.height='40px'; document.getElementById('rblx-content').style.display='none'; const maxX=window.innerWidth-40; const maxY=window.innerHeight-40; left=Math.max(0,Math.min(maxX,left)); top=Math.max(0,Math.min(maxY,top)); }else{ element.classList.add('open'); element.style.width='250px'; element.style.height='auto'; document.getElementById('rblx-content').style.display='block'; const maxX=window.innerWidth-250; const maxY=window.innerHeight-200; left=Math.max(0,Math.min(maxX,left)); top=Math.max(0,Math.min(maxY,top)); } element.style.left=left+'px'; element.style.top=top+'px'; element.style.right='auto'; element.style.bottom='auto'; element.style.cursor='move'; GM_setValue('widgetPosition',{left:element.style.left,top:element.style.top,open:element.classList.contains('open')}); } function fetchStockData(){ return new Promise((resolve,reject)=>{ GM_xmlhttpRequest({method:"GET",url:"https://query1.finance.yahoo.com/v8/finance/chart/RBLX",onload:function(response){ try{const data=JSON.parse(response.responseText);if(data.chart?.result?.[0]?.meta){const price=data.chart.result[0].meta.regularMarketPrice;const change=data.chart.result[0].meta.regularMarketChange;resolve({price,change});}else{reject('Invalid stock data response');}}catch(e){reject('Error parsing stock data: '+e.message);}},onerror:function(error){reject('Stock fetch error: '+error.statusText);},ontimeout:function(){reject('Stock fetch timeout');}}); }); } function fetchExchangeRates(){ return new Promise((resolve,reject)=>{ GM_xmlhttpRequest({method:"GET",url:"https://api.exchangerate-api.com/v4/latest/USD",onload:function(response){ try{const data=JSON.parse(response.responseText);if(data.rates){resolve(data.rates);}else{fetchExchangeRatesFallback().then(resolve).catch(reject);}}catch(e){fetchExchangeRatesFallback().then(resolve).catch(reject);}},onerror:function(error){fetchExchangeRatesFallback().then(resolve).catch(reject);},ontimeout:function(){fetchExchangeRatesFallback().then(resolve).catch(reject);}}); }); } function fetchExchangeRatesFallback(){ return new Promise((resolve,reject)=>{ GM_xmlhttpRequest({method:"GET",url:"https://api.frankfurter.app/latest?from=USD",onload:function(response){ try{const data=JSON.parse(response.responseText);if(data.rates){resolve(data.rates);}else{reject('No rates data in fallback API response');}}catch(e){reject('Error parsing fallback exchange rates: '+e.message);}},onerror:function(error){reject('Fallback exchange rate fetch error: '+error.statusText);},ontimeout:function(){reject('Fallback exchange rate fetch timeout');}}); }); } function switchCurrency(currency){ GM_setValue('selectedCurrency',currency); } async function updateTicker(currency){ const currencyData=CURRENCIES[currency]||CURRENCIES.USD; try{ const[stockData,exchangeRates]=await Promise.all([fetchStockData(),fetchExchangeRates()]); const rate=exchangeRates[currency]||1; const stockValue=document.getElementById('rblx-stock-value'); stockValue.textContent=`${(stockData.price*rate).toFixed(2)}`; stockValue.className=stockData.change>=0?'positive':'negative'; const robuxValue=document.getElementById('rblx-robux-value'); const robuxCurrencyIcon=document.getElementById('rblx-robux-currency'); robuxValue.textContent=`${(ROBUX_RATE_USD*rate).toFixed(4)}`; robuxCurrencyIcon.textContent=currencyData.symbol; document.querySelectorAll('#rblx-currency-switcher span').forEach(flag=>{ flag.style.opacity=flag.dataset.currency===currency?'1':'0.6'; flag.style.transform=flag.dataset.currency===currency?'scale(1.2)':'scale(1)'; }); }catch(error){ console.error('Tix Pro Error:',error); document.getElementById('rblx-stock-value').textContent='Error'; document.getElementById('rblx-robux-value').textContent='Error'; document.getElementById('rblx-stock-value').className='negative'; } } function addStyles(){ GM_addStyle(` .positive{color:#00ff88!important;} .negative{color:#ff4d4d!important;} #rblx-currency-switcher span:hover{transform:scale(1.2);opacity:0.9!important;} #rblx-logo-container:hover{transform:scale(1.1);} #rblx-ticker-container.open{width:250px;height:auto;padding-bottom:10px;} #rblx-ticker-container{transition:all 0.2s ease,box-shadow 0.2s ease;} #rblx-ticker-container:hover{transform:translateY(-3px);box-shadow:0 8px 25px rgba(0,0,0,0.6);} #rblx-ticker-container.open #rblx-title{display:inline-block;} .status-indicator{display:inline-block;width:10px;height:10px;border-radius:50%;margin-left:8px;vertical-align:middle;} .status-active{background-color:#00ff88;box-shadow:0 0 5px #00ff88;} .status-inactive{background-color:#ff4d4d;box-shadow:0 0 5px #ff4d4d;} #rblx-currency-switcher span{opacity:0.6;transition:all 0.2s ease;} #rblx-currency-switcher span:hover{opacity:0.9;} `); } async function init(){ addStyles(); const{container}=createTicker(); const savedCurrency=GM_getValue('selectedCurrency','USD'); const savedPosition=GM_getValue('widgetPosition'); if(savedPosition){ const left=parseInt(savedPosition.left,10); const top=parseInt(savedPosition.top,10); const maxX=window.innerWidth-(savedPosition.open?250:40); const maxY=window.innerHeight-(savedPosition.open?200:40); container.style.left=`${Math.max(0,Math.min(maxX,left))}px`; container.style.top=`${Math.max(0,Math.min(maxY,top))}px`; if(savedPosition.open){ container.classList.add('open'); container.style.width='250px'; container.style.height='auto'; document.getElementById('rblx-content').style.display='block'; } }else{ container.classList.remove('open'); container.style.width='40px'; container.style.height='40px'; document.getElementById('rblx-content').style.display='none'; } await updateTicker(savedCurrency); } init(); })();