您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
BEST EXPLOIT IN GAME1! REMINDER: it is still in a very early stage. report any bugs or issues to the author and please dont judge too harshly at this stage. Join TriX Discord server to receive more updates: https://discord.gg/Zp7cqqZzXX have fun cheating :D
当前为
// ==UserScript== // @name TriX Executor (BETA) for Territorial.io // @namespace https://gf.qytechs.cn/en/users/COURTESYCOIL // @version Beta-Orion-2024.08.01 // @description BEST EXPLOIT IN GAME1! REMINDER: it is still in a very early stage. report any bugs or issues to the author and please dont judge too harshly at this stage. Join TriX Discord server to receive more updates: https://discord.gg/Zp7cqqZzXX have fun cheating :D // @author Painsel // @match *://territorial.io/* // @require https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-javascript.min.js // @grant GM_getValue // @grant GM_setValue // @grant unsafeWindow // @run-at document-start // @license MIT // @icon data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iIzAwYWFmZiI+PHBhdGggZD0iTTE4LjM2IDIyLjA1bC00LjQyLTQuNDJDMTMuNDcgMTcuODcgMTMgMTguMTcgMTMgMTguNUMxMyAyMC45OSAxNS4wMSAyMyAxNy41IDIzaDMuNTRjLTIuNDUtMS40OC00LjQyLTMuNDUtNS5LTUuOTV6TTggMTNjMS42NiAwIDMuMTgtLjU5IDQuMzgtMS42MkwxMCAxMy41VjIybDIuNS0yLjVMMTggMTMuOTZjLjM1LS40OC42NS0xIC44Ny0xLjU1QzE4LjYxIDEzLjQxIDE4IDEyLjM0IDE4IDEyVjBoLTJ2MTJjMCAuMzQtLjAyLjY3LS4wNiAxLS4zMy4xOC0uNjguMy0xLjA0LjM3LTEuNzMgMC0zLjI3LS45My00LjE2LTIuMzZMNiAxMy42VjVINXY4eiIvPjwvc3ZnPg== // ==/UserScript== /* global Prism, unsafeWindow */ (function() { 'use strict'; // --- Global State for Advanced Features --- let interceptionEnabled = false; let queuedMessages = new Map(); let renderInterceptorQueue = () => {}; let customWs = null; let updateConnectionStatus = () => {}; let updatePingDisplay = () => {}; let logPacketCallback = () => {}; // --- WebSocket Proxy Setup --- const OriginalWebSocket = unsafeWindow.WebSocket; unsafeWindow.WebSocket = function(url, protocols) { if (!url.includes('/s52/')) { return new OriginalWebSocket(url, protocols); } console.log(`[TriX] Intercepting WebSocket connection to: ${url}`); const ws = new OriginalWebSocket(url, protocols); let originalOnMessageHandler = null; const originalSend = ws.send.bind(ws); ws.send = function(data) { if (interceptionEnabled) { const messageId = `send-${Date.now()}-${Math.random()}`; const promise = new Promise(resolve => queuedMessages.set(messageId, { direction: 'send', data, resolve })); renderInterceptorQueue(); promise.then(decision => { queuedMessages.delete(messageId); renderInterceptorQueue(); if (decision.action === 'forward') { originalSend(decision.data); logPacketCallback('send', `[Forwarded] ${decision.data}`); } else { logPacketCallback('send', `[Blocked] ${data}`); } }); } else { logPacketCallback('send', data); // THIS LINE KEEPS PACKET LOGGER WORKING return originalSend(data); } }; Object.defineProperty(ws, 'onmessage', { get: () => originalOnMessageHandler, set: (handler) => { originalOnMessageHandler = handler; ws.addEventListener('message', event => { if (interceptionEnabled) { const messageId = `recv-${Date.now()}-${Math.random()}`; const promise = new Promise(resolve => queuedMessages.set(messageId, { direction: 'receive', data: event.data, resolve })); renderInterceptorQueue(); promise.then(decision => { queuedMessages.delete(messageId); renderInterceptorQueue(); if (decision.action === 'forward') { logPacketCallback('receive', `[Forwarded] ${decision.data}`); if (originalOnMessageHandler) originalOnMessageHandler({ data: decision.data }); } else { logPacketCallback('receive', `[Blocked] ${event.data}`); } }); } else { logPacketCallback('receive', event.data); // THIS LINE KEEPS PACKET LOGGER WORKING if (originalOnMessageHandler) originalOnMessageHandler(event); } }); }, configurable: true }); ws.addEventListener('open', () => updateConnectionStatus('connected', 'Connection established.')); ws.addEventListener('close', (event) => { if (unsafeWindow.webSocketManager && unsafeWindow.webSocketManager.activeSocket === ws) unsafeWindow.webSocketManager.activeSocket = null; updateConnectionStatus('disconnected', `Disconnected. Code: ${event.code}`); updatePingDisplay('---'); }); ws.addEventListener('error', () => updateConnectionStatus('error', 'A connection error occurred.')); unsafeWindow.webSocketManager = { activeSocket: ws }; return ws; }; // --- Main script logic --- function initialize() { const shadowHost = document.createElement('div'); shadowHost.id = 'trix-host'; document.body.appendChild(shadowHost); const shadowRoot = shadowHost.attachShadow({ mode: 'open' }); const styleElement = document.createElement('style'); styleElement.textContent = ` /* PrismJS Theme */ code[class*="language-"],pre[class*="language-"]{color:#ccc;background:0 0;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;tab-size:4;}pre[class*="language-"]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*="language-"],pre[class*="language-"]{background:#2d2d2d}:not(pre)>code[class*="language-"]{padding:.1em;border-radius:.3em;white-space:normal}.token.comment{color:#999}.token.punctuation{color:#ccc}.token.tag{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.number,.token.function{color:#f08d49}.token.property,.token.class-name,.token.constant,.token.symbol{color:#f8c555}.token.selector,.token.important,.token.atrule,.token.keyword,.token.builtin{color:#cc99cd}.token.string,.token.char,.attr-value,.token.regex,.token.variable{color:#7ec699}.token.operator,.token.entity,.token.url{color:#67cdcc} /* --- General UI --- */ @keyframes trix-fade-in { from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); } } @keyframes trix-slide-in { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } #trix-container { position: fixed; top: 80px; right: 15px; width: 500px; min-height: 450px; z-index: 99998; color: #fff; border-radius: 10px; overflow: hidden; box-shadow: 0 5px 25px rgba(0,0,0,0.5); display: flex; flex-direction: column; backdrop-filter: blur(10px); animation: trix-slide-in 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; resize: both; } #trix-toggle-btn { position: fixed; top: 15px; right: 15px; z-index: 99999; width: 50px; height: 50px; background-color: rgba(30, 30, 34, 0.8); color: #00aaff; border: 2px solid #00aaff; border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 24px; transition: all 0.3s ease; backdrop-filter: blur(5px); font-family: monospace; box-shadow: 0 0 10px rgba(0, 170, 255, 0.5); } #trix-container.hidden { display: none; } #trix-header, #trix-footer { cursor: move; user-select: none; } #trix-header { padding: 10px 15px; font-weight: bold; font-size: 16px; display: flex; justify-content: space-between; align-items: center; background-color: rgba(0,0,0,0.3); } #trix-header-controls { display: flex; align-items: center; gap: 15px; } #trix-fps-display, #trix-ping-display { font-size: 12px; } #trix-conn-status { width: 12px; height: 12px; border-radius: 50%; } #trix-conn-status.connected { background-color: #28a745; } #trix-conn-status.disconnected { background-color: #dc3545; } #trix-conn-status.connecting { background-color: #ffc107; } .ping-good { color: #28a745; } .ping-ok { color: #ffc107; } .ping-bad { color: #dc3545; } #trix-content { padding: 0 15px 15px 15px; flex-grow: 1; display: flex; flex-direction: column; overflow: hidden; } .trix-tabs { display: flex; flex-wrap: wrap; border-bottom: 1px solid #555; } .trix-tab { background: #2a2a30; padding: 8px 12px; cursor: pointer; border-radius: 5px 5px 0 0; margin-right: 4px; position: relative; } .trix-tab.active { background: #1e1e22; font-weight: bold; color: #00aaff; } .trix-view { display: flex; flex-direction: column; flex-grow: 1; margin-top: 10px; overflow: hidden; } .trix-action-bar { display: flex; gap: 10px; margin-top: 10px; } .trix-button { background-color: #007bff; color: white; border: none; padding: 8px 12px; cursor: pointer; border-radius: 5px; flex-grow: 1; transition: background-color 0.2s; } .trix-input { background: #2d2d2d; border: 1px solid #555; color: #ccc; padding: 8px; border-radius: 5px; font-family: monospace; box-sizing: border-box; } .trix-status-bar { margin-top: 10px; padding: 5px; background: rgba(0,0,0,0.2); font-size: 12px; border-radius: 3px; min-height: 1em; } /* --- WebSocket Client Styles --- */ #trix-ws-client-view { display: grid; grid-template-rows: auto auto 1fr auto; gap: 10px; height: 100%; } .trix-ws-client-grid { display: grid; grid-template-columns: 1fr auto; gap: 10px; align-items: start; } #trix-ws-url { width: 100%; } #trix-ws-connect-btn.connected { background-color: #dc3545; } #trix-ws-console { flex-grow: 1; background: #1e1e22; border: 1px solid #555; border-radius: 5px; padding: 10px; overflow-y: auto; font-family: monospace; font-size: 12px; white-space: pre-wrap; word-break: break-all; } .ws-console-send { color: #7ec699; } .ws-console-receive { color: #cc99cd; } .ws-console-system { color: #f08d49; } #trix-ws-message { width: 100%; min-height: 80px; resize: vertical; } .trix-ws-message-controls { display: flex; flex-direction: column; gap: 5px; } /* --- Interceptor Styles --- */ #trix-interceptor-view { flex-grow: 1; display: flex; flex-direction: column; } #trix-interceptor-controls { display: flex; gap: 10px; margin-bottom: 10px; } #trix-toggle-interception-btn.active { background-color: #dc3545; box-shadow: 0 0 8px #dc3545; } #trix-interceptor-queue { flex-grow: 1; background: #1e1e22; border: 1px solid #555; border-radius: 5px; padding: 10px; overflow-y: auto; font-family: monospace; font-size: 12px; } .interceptor-item { border: 1px solid #444; border-radius: 4px; padding: 8px; margin-bottom: 8px; background: #2a2a30; } .interceptor-item-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 5px; font-weight: bold; } .interceptor-item-header .send { color: #7ec699; } .interceptor-item-header .receive { color: #cc99cd; } .interceptor-item textarea { width: 100%; box-sizing: border-box; background: #2d2d2d; color: #ccc; border: 1px solid #555; min-height: 40px; font-family: inherit; } .interceptor-item-actions { display: flex; gap: 5px; margin-top: 5px; } .interceptor-btn { flex-grow: 1; padding: 4px; font-size: 11px; } .interceptor-forward-btn { background-color: #28a745; } .interceptor-block-btn { background-color: #dc3545; } /* --- Other Styles (Logger, Storage, etc.) --- */ #trix-packet-log-view, #trix-storage-view-container, #trix-injector-container { flex-grow: 1; display: flex; flex-direction: column; } #trix-packet-log { flex-grow: 1; background: #1e1e22; border: 1px solid #555; border-radius: 5px; padding: 10px; overflow-y: auto; font-family: monospace; font-size: 12px; } #trix-storage-view { flex-grow: 1; overflow-y: auto; font-family: monospace; font-size: 13px; } .storage-table { margin-top: 10px; } .storage-header { font-weight: bold; color: #00aaff; border-bottom: 1px solid #555; padding-bottom: 5px; margin-bottom: 5px; } .storage-row { display: flex; align-items: center; border-bottom: 1px solid #333; padding: 4px 2px; } .storage-key { color: #cc99cd; min-width: 100px; max-width: 100px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .storage-value { color: #7ec699; flex-grow: 1; cursor: pointer; word-break: break-all; } .storage-value-input { background: #3a3a42; color: #eee; border: 1px solid #00aaff; width: 100%; font-family: inherit; font-size: inherit; } .storage-delete { cursor: pointer; color: #ff5555; opacity: 0.5; margin-left: 10px; font-weight: bold; } .storage-delete:hover { opacity: 1; } `; shadowRoot.appendChild(styleElement); let state = { tabs: [], activeTabId: null, settings: { theme: 'dark-knight', position: { top: '80px', right: '15px' }, size: { width: '500px', height: '450px' } }, wsClient: { url: 'wss://echo.websocket.events', protocols: '', savedMessages: { 'Example JSON': '{"action":"ping","id":123}'} } }; const $ = (selector, parent = shadowRoot) => parent.querySelector(selector); const debounce = (func, delay) => { let t; return (...a) => { clearTimeout(t); t = setTimeout(() => func.apply(this, a), delay); }; }; function loadState() { const savedState = GM_getValue('trixExecutorState'); if (savedState) { state.tabs = savedState.tabs || []; state.activeTabId = savedState.activeTabId; state.settings = { ...state.settings, ...savedState.settings }; state.wsClient = { ...state.wsClient, ...savedState.wsClient }; } const defaultTabs = [ { id: 'logger', name: 'Packet Logger', isPermanent: true }, { id: 'interceptor', name: 'Interceptor', isPermanent: true }, { id: 'ws_client', name: 'WS Client', isPermanent: true }, { id: 'storage', name: 'Storage', isPermanent: true }, { id: Date.now(), name: "Welcome", code: "// Welcome to TriX Executor!" } ]; ['logger', 'interceptor', 'ws_client', 'storage'].forEach((id, index) => { const defaultTab = defaultTabs.find(d => d.id === id); if (!state.tabs.find(t => t.id === id)) { state.tabs.splice(index, 0, defaultTab); } }); if (!state.tabs.find(t => t.code)) { state.tabs.push(defaultTabs.find(t => t.code)); } if (!state.activeTabId || !state.tabs.find(t => t.id === state.activeTabId)) { state.activeTabId = state.tabs[0].id; } } const saveState = debounce(() => { const container = $('#trix-container'); if (container) { state.settings.position = { top: container.style.top, left: container.style.left, right: container.style.right, bottom: 'auto' }; state.settings.size = { width: container.style.width, height: container.style.height }; } GM_setValue('trixExecutorState', state); }, 500); let ui, editor; function createUI() { const toggleBtn = document.createElement('div'); toggleBtn.id = 'trix-toggle-btn'; toggleBtn.title = 'Toggle TriX Executor (BETA)'; toggleBtn.innerHTML = 'X'; const container = document.createElement('div'); container.id = 'trix-container'; container.classList.add('hidden'); container.innerHTML = ` <div id="trix-header"> <div><span class="trix-title-3d">TriX Executor</span><span class="trix-version-tag">(v${GM_info.script.version})</span></div> <div id="trix-header-controls"> <span id="trix-conn-status" class="disconnected" title="No connection"></span> <span id="trix-ping-display">Ping: ---</span> <span id="trix-fps-display"></span> <span id="trix-close-btn" title="Close">✖</span> </div> </div> <div id="trix-content"></div> <div id="trix-footer" style="padding:10px; text-align:center; background:rgba(0,0,0,0.2);"> <button id="trix-settings-btn" class="trix-button" style="flex-grow:0; padding: 5px 15px;">Settings</button> </div>`; shadowRoot.append(toggleBtn, container); return { toggleBtn, container }; } updateConnectionStatus = function(status, title) { const statusIndicator = $('#trix-conn-status'); if (statusIndicator) { statusIndicator.className = ''; statusIndicator.classList.add(status); statusIndicator.title = title; } }; updatePingDisplay = function(ping) { const pingDisplay = $('#trix-ping-display'); if (!pingDisplay) return; pingDisplay.className = ''; if (typeof ping === 'number') { pingDisplay.textContent = `Ping: ${ping}`; if (ping < 100) pingDisplay.classList.add('ping-good'); else if (ping < 200) pingDisplay.classList.add('ping-ok'); else pingDisplay.classList.add('ping-bad'); } else { pingDisplay.textContent = `Ping: ${ping}`; } }; async function measurePing() { const startTime = performance.now(); const url = `${window.location.protocol}//${window.location.host}/favicon.ico?_ts=${Date.now()}`; try { const response = await fetch(url, { method: 'HEAD', cache: 'no-store' }); if (response.ok) { const endTime = performance.now(); updatePingDisplay(Math.round(endTime - startTime)); } else { updatePingDisplay('Error'); } } catch (error) { updatePingDisplay('Error'); } } function renderTabs() { const tabsContainer = $('.trix-tabs'); if (!tabsContainer) return; tabsContainer.innerHTML = ''; state.tabs.forEach(tab => { const tabEl = document.createElement('div'); tabEl.className = 'trix-tab'; tabEl.dataset.tabId = tab.id; if (tab.id === state.activeTabId) tabEl.classList.add('active'); tabEl.innerHTML = `<span class="trix-tab-name">${tab.name}</span>` + (!tab.isPermanent ? `<span class="trix-tab-close">x</span>` : ''); tabsContainer.appendChild(tabEl); }); const newTabBtn = document.createElement('button'); newTabBtn.id = 'trix-new-tab-btn'; newTabBtn.textContent = '+'; tabsContainer.appendChild(newTabBtn); renderEditor(); } function renderActiveView() { const content = $('#trix-content'); const activeTab = state.tabs.find(t => t.id === state.activeTabId); content.innerHTML = ''; if (activeTab.id === 'ws_client') { content.innerHTML = ` <div id="trix-ws-client-view" class="trix-view"> <div class="trix-tabs"></div> <div class="trix-ws-client-grid"> <input type="text" id="trix-ws-url" class="trix-input" placeholder="WebSocket URL (e.g., wss://...)" value="${state.wsClient.url}"> <button id="trix-ws-connect-btn" class="trix-button" style="flex-grow:0;">Connect</button> </div> <div id="trix-ws-console"><div class="ws-console-system">Status: Disconnected</div></div> <div class="trix-ws-client-grid"> <textarea id="trix-ws-message" class="trix-input" placeholder="Enter message to send..."></textarea> <div class="trix-ws-message-controls"> <button id="trix-ws-send-btn" class="trix-button">Send</button> <select id="trix-ws-saved-messages" class="trix-input"><option value="">Load</option></select> <button id="trix-ws-save-msg-btn" class="trix-button" style="background-color:#0d6efd;">Save</button> <button id="trix-ws-del-msg-btn" class="trix-button" style="background-color:#6c757d;">Del</button> </div> </div> </div>`; renderTabs(); renderWsSavedMessages(); } else if (activeTab.id === 'interceptor') { content.innerHTML = ` <div id="trix-interceptor-view" class="trix-view"> <div class="trix-tabs"></div> <div id="trix-interceptor-controls"> <button id="trix-toggle-interception-btn" class="trix-button">Enable Interception</button> <button id="trix-forward-all-btn" class="trix-button" style="background-color:#28a745;">Forward All</button> <button id="trix-block-all-btn" class="trix-button" style="background-color:#dc3545;">Block All</button> </div> <div id="trix-interceptor-queue"></div> <div class="trix-status-bar">Warning: Enabling interception may cause disconnects if messages are not processed quickly.</div> </div>`; renderTabs(); renderInterceptorQueue(); const toggleBtn = $('#trix-toggle-interception-btn'); if (interceptionEnabled) { toggleBtn.classList.add('active'); toggleBtn.textContent = 'Interception Enabled'; } } else if (activeTab.id === 'logger') { content.innerHTML = ` <div id="trix-packet-log-view" class="trix-view"> <div class="trix-tabs"></div> <div id="trix-packet-log"></div> <div class="trix-action-bar"> <button id="trix-clear-log-btn" class="trix-button">Clear Log</button> </div> </div>`; $('#trix-clear-log-btn').addEventListener('click', () => $('#trix-packet-log').innerHTML = ''); renderTabs(); } else if (activeTab.id === 'storage') { content.innerHTML = ` <div id="trix-storage-view-container" class="trix-view"> <div class="trix-tabs"></div> <div id="trix-storage-view"></div> <div class="trix-action-bar"> <button id="trix-refresh-storage-btn" class="trix-button">Refresh</button> <button id="trix-add-storage-btn" class="trix-button">Add Local Storage Entry</button> </div> </div>`; renderTabs(); renderStorageView(); } else { content.innerHTML = ` <div id="trix-injector-container" class="trix-view"> <div class="trix-tabs"></div> <div class="trix-editor-area"></div> <div class="trix-action-bar"> <button id="trix-execute-btn" class="trix-button">Execute</button> <button id="trix-clear-btn" class="trix-button">Clear</button> </div> <div class="trix-status-bar">Ready.</div> </div>`; renderTabs(); } } renderInterceptorQueue = function() { const queueContainer = $('#trix-interceptor-queue'); if (!queueContainer) return; queueContainer.innerHTML = ''; if (queuedMessages.size === 0) { queueContainer.textContent = 'No messages pending.'; return; } for (const [id, msg] of queuedMessages.entries()) { const item = document.createElement('div'); item.className = 'interceptor-item'; item.dataset.messageId = id; item.innerHTML = ` <div class="interceptor-item-header"><span class="${msg.direction}">${msg.direction.toUpperCase()}</span></div> <textarea>${msg.data}</textarea> <div class="interceptor-item-actions"> <button class="trix-button interceptor-btn interceptor-forward-btn">Forward</button> <button class="trix-button interceptor-btn interceptor-block-btn">Block</button> </div>`; queueContainer.appendChild(item); } }; function renderStorageView() { const view = $('#trix-storage-view'); if (!view) return; view.innerHTML = ''; const storages = { 'Local Storage': unsafeWindow.localStorage, 'Session Storage': unsafeWindow.sessionStorage }; for (const [name, storage] of Object.entries(storages)) { const container = document.createElement('div'); container.className = 'storage-table'; container.innerHTML = `<div class="storage-header">${name} (${storage.length} items)</div>`; for (let i = 0; i < storage.length; i++) { const key = storage.key(i); const value = storage.getItem(key); const row = document.createElement('div'); row.className = 'storage-row'; row.dataset.key = key; row.dataset.storage = name === 'Local Storage' ? 'local' : 'session'; row.innerHTML = `<span class="storage-key" title="${key}">${key}</span><span class="storage-value">${value}</span><span class="storage-delete" title="Delete key">✖</span>`; container.appendChild(row); } view.appendChild(container); } } function renderWsSavedMessages() { const select = $('#trix-ws-saved-messages'); if (!select) return; select.innerHTML = '<option value="">Load Message</option>'; for (const name in state.wsClient.savedMessages) { const option = document.createElement('option'); option.value = name; option.textContent = name; select.appendChild(option); } } function logToWsClientConsole(message, type = 'system') { const consoleEl = $('#trix-ws-console'); if (!consoleEl) return; const line = document.createElement('div'); line.className = `ws-console-${type}`; line.textContent = `[${type.toUpperCase()}] ${message}`; consoleEl.appendChild(line); consoleEl.scrollTop = consoleEl.scrollHeight; } function connectCustomWs() { const urlInput = $('#trix-ws-url'); const connectBtn = $('#trix-ws-connect-btn'); if (customWs && customWs.readyState < 2) { customWs.close(); return; } state.wsClient.url = urlInput.value.trim(); saveState(); if (!state.wsClient.url) { logToWsClientConsole('Error: URL cannot be empty.', 'system'); return; } connectBtn.textContent = 'Connecting...'; connectBtn.disabled = true; logToWsClientConsole(`Connecting to ${state.wsClient.url}...`, 'system'); customWs = new OriginalWebSocket(state.wsClient.url); customWs.onopen = () => { logToWsClientConsole('Connection established.', 'system'); connectBtn.textContent = 'Disconnect'; connectBtn.classList.add('connected'); connectBtn.disabled = false; }; customWs.onmessage = (event) => { logToWsClientConsole(event.data, 'receive'); }; customWs.onclose = () => { logToWsClientConsole('Connection closed.', 'system'); connectBtn.textContent = 'Connect'; connectBtn.classList.remove('connected'); connectBtn.disabled = false; customWs = null; }; customWs.onerror = () => { logToWsClientConsole('Connection error.', 'system'); connectBtn.textContent = 'Connect'; connectBtn.classList.remove('connected'); connectBtn.disabled = false; customWs = null; }; } function renderEditor() { const editorArea = $('.trix-editor-area'); if (!editorArea) { editor = null; return; } const activeTab = state.tabs.find(t => t.id === state.activeTabId); if (!activeTab || typeof activeTab.code !== 'string') { editorArea.innerHTML = ''; editor = null; return; } editorArea.innerHTML = `<textarea spellcheck="false" autocapitalize="off" autocomplete="off" autocorrect="off"></textarea><pre class="language-js"><code></code></pre>`; editor = { textarea: $('textarea', editorArea), pre: $('pre', editorArea), code: $('code', editorArea) }; editor.textarea.value = activeTab.code; highlightCode(activeTab.code); addEditorEventListeners(); } function highlightCode(code) { if (editor) editor.code.innerHTML = Prism.highlight(code + '\n', Prism.languages.javascript, 'javascript'); } function addEditorEventListeners() { if (!editor) return; editor.textarea.addEventListener('input', () => { const activeTab = state.tabs.find(t => t.id === state.activeTabId); if (activeTab) { activeTab.code = editor.textarea.value; highlightCode(activeTab.code); saveState(); } }); editor.textarea.addEventListener('scroll', () => { if (editor) { editor.pre.scrollTop = editor.textarea.scrollTop; editor.pre.scrollLeft = editor.textarea.scrollLeft; }}); editor.textarea.addEventListener('keydown', e => { if (e.key === 'Tab') { e.preventDefault(); const s = e.target.selectionStart, end = e.target.selectionEnd; e.target.value = e.target.value.substring(0, s) + ' ' + e.target.value.substring(end); e.target.selectionStart = e.target.selectionEnd = s + 2; editor.textarea.dispatchEvent(new Event('input')); } }); } function initEventListeners() { ui.toggleBtn.addEventListener('click', () => ui.container.classList.toggle('hidden')); $('#trix-close-btn').addEventListener('click', () => ui.container.classList.add('hidden')); initDraggable(ui.container, [$('#trix-header'), $('#trix-footer')]); ui.container.addEventListener('click', handleContainerClick); ui.container.addEventListener('dblclick', handleContainerDblClick); $('#trix-settings-btn').addEventListener('click', showSettingsModal); const resizeObserver = new ResizeObserver(debounce(saveState, 500)); resizeObserver.observe(ui.container); } function handleContainerClick(e) { const target = e.target; const tabEl = target.closest('.trix-tab'); if (tabEl && !target.classList.contains('trix-tab-close')) { const tabId = isNaN(parseInt(tabEl.dataset.tabId)) ? tabEl.dataset.tabId : parseInt(tabEl.dataset.tabId); if (tabId !== state.activeTabId) { state.activeTabId = tabId; renderActiveView(); saveState(); } } if (target.classList.contains('trix-tab-close')) { const tabId = parseInt(tabEl.dataset.tabId, 10); state.tabs = state.tabs.filter(t => t.id !== tabId); if (state.activeTabId === tabId) state.activeTabId = state.tabs[0].id; renderActiveView(); saveState(); } if (target.id === 'trix-new-tab-btn') { const newId = Date.now(), newName = `Script ${state.tabs.length}`; state.tabs.push({ id: newId, name: newName, code: `// ${newName}` }); state.activeTabId = newId; renderActiveView(); saveState(); } if (target.id === 'trix-execute-btn') executeScript(); if (target.id === 'trix-clear-btn') { const activeTab = state.tabs.find(t => t.id === state.activeTabId); if (activeTab) { activeTab.code = ''; renderEditor(); saveState(); } } if (target.classList.contains('storage-delete')) { const row = target.closest('.storage-row'); const { key, storage } = row.dataset; if (confirm(`Delete key "${key}"?`)) { (storage === 'local' ? unsafeWindow.localStorage : unsafeWindow.sessionStorage).removeItem(key); renderStorageView(); } } else if (target.classList.contains('storage-value')) { const row = target.closest('.storage-row'); const { key, storage } = row.dataset; const input = document.createElement('input'); input.type = 'text'; input.className = 'storage-value-input'; input.value = target.textContent; target.replaceWith(input); input.focus(); const finishEdit = () => { (storage === 'local' ? unsafeWindow.localStorage : unsafeWindow.sessionStorage).setItem(key, input.value); renderStorageView(); }; input.addEventListener('blur', finishEdit, { once: true }); input.addEventListener('keydown', e => { if (e.key === 'Enter') finishEdit(); if(e.key === 'Escape') renderStorageView(); }); } if (target.id === 'trix-toggle-interception-btn') { interceptionEnabled = !interceptionEnabled; target.classList.toggle('active', interceptionEnabled); target.textContent = interceptionEnabled ? 'Interception Enabled' : 'Enable Interception'; } if (target.id === 'trix-forward-all-btn') { for (const msg of queuedMessages.values()) { msg.resolve({ action: 'forward', data: msg.data }); } } if (target.id === 'trix-block-all-btn') { for (const msg of queuedMessages.values()) { msg.resolve({ action: 'block' }); } } const messageItem = target.closest('.interceptor-item'); if (messageItem) { const messageId = messageItem.dataset.messageId; const message = queuedMessages.get(messageId); if (!message) return; if (target.classList.contains('interceptor-forward-btn')) { const editedData = messageItem.querySelector('textarea').value; message.resolve({ action: 'forward', data: editedData }); } else if (target.classList.contains('interceptor-block-btn')) { message.resolve({ action: 'block' }); } } if (target.id === 'trix-ws-connect-btn') connectCustomWs(); if (target.id === 'trix-ws-send-btn') { if (customWs && customWs.readyState === 1) { const message = $('#trix-ws-message').value; customWs.send(message); logToWsClientConsole(message, 'send'); } else { logToWsClientConsole('Error: Not connected.', 'system'); } } if (target.id === 'trix-ws-save-msg-btn') { const message = $('#trix-ws-message').value; if (!message) return; const name = prompt('Enter a name for this message:', 'My Message'); if (name) { state.wsClient.savedMessages[name] = message; saveState(); renderWsSavedMessages(); } } if (target.id === 'trix-ws-del-msg-btn') { const select = $('#trix-ws-saved-messages'); const name = select.value; if (name && confirm(`Delete saved message "${name}"?`)) { delete state.wsClient.savedMessages[name]; saveState(); renderWsSavedMessages(); } } if(target.id === 'trix-ws-saved-messages') { const name = target.value; if (name) { $('#trix-ws-message').value = state.wsClient.savedMessages[name]; } } } function handleContainerDblClick(e) { const nameEl = e.target.closest('.trix-tab-name'); if (!nameEl) return; const tabEl = nameEl.closest('.trix-tab'); const tabId = parseInt(tabEl.dataset.tabId, 10); const tab = state.tabs.find(t => t.id === tabId); if (!tab || tab.isPermanent) return; const input = document.createElement('input'); input.type = 'text'; input.className = 'trix-tab-rename-input'; input.value = tab.name; nameEl.replaceWith(input); input.focus(); input.select(); const finishEditing = () => { const newName = input.value.trim(); if (newName) tab.name = newName; saveState(); renderTabs(); }; input.addEventListener('blur', finishEditing, { once: true }); input.addEventListener('keydown', e => { if (e.key === 'Enter') finishEditing(); else if (e.key === 'Escape') renderTabs(); }); } function initDraggable(container, handles) { let isDragging = false, offsetX, offsetY; handles.forEach(handle => { handle.addEventListener('mousedown', e => { if (e.target.closest('#trix-header-controls') || e.target.closest('.trix-title-3d') || e.target.closest('#trix-settings-btn')) return; isDragging = true; offsetX = e.clientX - container.offsetLeft; offsetY = e.clientY - container.offsetTop; container.style.right = 'auto'; container.style.bottom = 'auto'; document.body.style.userSelect = 'none'; }); }); document.addEventListener('mousemove', e => { if (isDragging) { container.style.left = `${e.clientX - offsetX}px`; container.style.top = `${Math.max(0, e.clientY - offsetY)}px`; } }); document.addEventListener('mouseup', () => { if (isDragging) { isDragging = false; document.body.style.userSelect = ''; saveState(); } }); } function executeScript() { const statusBar = $('.trix-status-bar'); if (!statusBar) return; const activeTab = state.tabs.find(t => t.id === state.activeTabId); if (!activeTab || !activeTab.code) { statusBar.textContent = 'Nothing to execute.'; return; } try { new Function(activeTab.code)(); statusBar.textContent = `Success: Executed '${activeTab.name}' at ${new Date().toLocaleTimeString()}`; statusBar.className = 'trix-status-bar trix-status-success'; } catch (error) { console.error('TriX Executor Error:', error); statusBar.textContent = `Error: ${error.message}`; statusBar.className = 'trix-status-bar trix-status-error'; } } function showSettingsModal() { const modal = document.createElement('div'); modal.id = 'trix-settings-modal'; modal.innerHTML = ` <div class="trix-settings-content"> <h3>Settings</h3> <label for="trix-theme-select">Theme:</label> <select id="trix-theme-select"> <option value="dark-knight">Dark Knight</option> <option value="arctic-light">Arctic Light</option> <option value="crimson">Crimson</option> </select> <button id="trix-settings-close">Close</button> </div>`; shadowRoot.appendChild(modal); $('#trix-theme-select').value = state.settings.theme; $('#trix-theme-select').addEventListener('change', e => { state.settings.theme = e.target.value; applySettings(); saveState(); }); $('#trix-settings-close').addEventListener('click', () => modal.remove()); modal.addEventListener('click', e => { if (e.target.id === 'trix-settings-modal') modal.remove(); }); } function applySettings() { const container = $('#trix-container'); container.dataset.theme = state.settings.theme; Object.assign(container.style, state.settings.position, state.settings.size); } logPacketCallback = function(type, data) { const logContainer = $('#trix-packet-log'); if (!logContainer) return; const item = document.createElement('div'); item.className = `packet-item packet-${type}`; let formattedData = data; if (data instanceof ArrayBuffer) formattedData = `[ArrayBuffer, ${data.byteLength} bytes]`; else if (data instanceof Blob) formattedData = `[Blob, ${data.size} bytes, type: ${data.type}]`; item.innerHTML = `<span class="packet-meta">[${type.toUpperCase()}]</span> ${formattedData}`; logContainer.appendChild(item); logContainer.scrollTop = logContainer.scrollHeight; }; loadState(); ui = createUI(); applySettings(); renderActiveView(); initEventListeners(); setInterval(measurePing, 2000); let lastFrameTime = performance.now(), frameCount = 0; const fpsDisplay = $('#trix-fps-display'); function updateFPS(now) { frameCount++; if (now >= lastFrameTime + 1000) { if (fpsDisplay) fpsDisplay.textContent = `FPS: ${frameCount}`; lastFrameTime = now; frameCount = 0; } requestAnimationFrame(updateFPS); } requestAnimationFrame(updateFPS); } function waitForElement(selector, callback) { const maxRetries = 40; const retryInterval = 250; let retryCount = 0; const checkInterval = setInterval(() => { const element = document.querySelector(selector); if (element) { console.log(`[TriX] Target element '${selector}' found. Initializing script.`); clearInterval(checkInterval); callback(); } else { retryCount++; console.log(`[TriX] Target element not found. Retry ${retryCount}/${maxRetries}...`); if (retryCount >= maxRetries) { clearInterval(checkInterval); console.error(`[TriX] Failed to find target element '${selector}' after ${maxRetries} retries. Aborting initialization.`); } } }, retryInterval); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => waitForElement('#canvasA', initialize)); } else { waitForElement('#canvasA', initialize); } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址