您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Gartic.io Enhanced Experience
// ==UserScript== // @name GarticPro v3.0 // @namespace http://tampermonkey.net/ // @version 3.0 // @description Gartic.io Enhanced Experience // @author ProDev // @match https://gartic.io/?Pro* // @icon https://www.google.com/s2/favicons?sz=64&domain=gartic.io // @grant GM_addStyle // @grant GM_xmlhttpRequest // @grant unsafeWindow // ==/UserScript== (function() { 'use strict'; const STYLE = ` :root { --primary: #6c5ce7; --primary-dark: #5849e0; --accent: #a55eea; --background: rgba(15,15,26,0.95); --surface: rgba(26,26,46,0.98); --surface-light: rgba(36,36,68,0.95); --text: #ffffff; --text-secondary: rgba(255,255,255,0.7); --border: rgba(108,92,231,0.2); --shadow: 0 4px 20px rgba(0,0,0,0.25); --shadow-lg: 0 8px 30px rgba(0,0,0,0.3); --gradient: linear-gradient(45deg, var(--primary), var(--accent)); } #garticpro { position: fixed; top: 20px; left: 20px; width: 95%; max-width: 400px; background: var(--surface); border-radius: 15px; box-shadow: var(--shadow-lg); z-index: 9999; backdrop-filter: blur(10px); border: 1px solid var(--border); overflow: hidden; transform: translateX(-420px); transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1); opacity: 0; animation: fadeIn 0.5s ease forwards; } #garticpro.active { transform: translateX(0); } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } .gp-header { background: var(--gradient); padding: 1rem; display: flex; align-items: center; justify-content: space-between; } .gp-title { display: flex; align-items: center; gap: 0.75rem; } .gp-logo { width: 32px; height: 32px; background: rgba(255,255,255,0.2); border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 1.25rem; } .gp-name { font-weight: 600; font-size: 1rem; color: var(--text); } .gp-version { font-size: 0.75rem; opacity: 0.8; } .gp-toggle { position: fixed; top: 20px; left: 20px; width: 48px; height: 48px; background: var(--surface); border-radius: 12px; display: flex; align-items: center; justify-content: center; cursor: pointer; box-shadow: var(--shadow); border: 1px solid var(--border); transition: all 0.3s ease; z-index: 10000; } .gp-toggle:hover { transform: scale(1.05); background: var(--surface-light); } .gp-content { padding: 1rem; } .gp-stats { display: flex; justify-content: space-between; margin-bottom: 1rem; } .gp-stat { background: var(--surface-light); padding: 0.75rem; border-radius: 12px; display: flex; align-items: center; gap: 0.5rem; transition: all 0.3s ease; } .gp-stat-icon { width: 32px; height: 32px; background: var(--primary); border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 1rem; } .gp-stat-info { flex: 1; } .gp-stat-value { font-size: 1rem; font-weight: 600; } .gp-stat-label { font-size: 0.75rem; color: var(--text-secondary); } .gp-tabs { display: flex; gap: 0.5rem; margin-bottom: 1rem; padding: 0.25rem; background: var(--surface-light); border-radius: 10px; } .gp-tab { flex: 1; padding: 0.5rem; border: none; background: none; color: var(--text); border-radius: 8px; cursor: pointer; transition: all 0.3s ease; font-weight: 500; } .gp-tab.active { background: var(--primary); } .gp-list { display: flex; flex-direction: column; gap: 0.5rem; max-height: 400px; overflow-y: auto; padding-right: 0.5rem; } .gp-room, .gp-player { background: var(--surface-light); padding: 0.75rem; border-radius: 12px; display: flex; align-items: center; gap: 0.5rem; transition: all 0.3s ease; } .gp-avatar { width: 40px; height: 40px; border-radius: 10px; overflow: hidden; } .gp-avatar img { width: 100%; height: 100%; object-fit: cover; } .gp-info { flex: 1; } .gp-name { font-weight: 600; font-size: 0.875rem; margin-bottom: 0.25rem; } .gp-stats { font-size: 0.75rem; color: var(--text-secondary); display: flex; gap: 0.5rem; } .gp-actions { display: flex; gap: 0.5rem; } .gp-action { width: 28px; height: 28px; border-radius: 8px; display: flex; align-items: center; justify-content: center; background: rgba(255,255,255,0.1); border: none; color: var(--text); cursor: pointer; transition: all 0.3s ease; } `; class GarticPro { constructor() { this.players = new Map(); this.rooms = new Map(); this.activeTab = 'players'; this.initialized = false; } init() { this.createUI(); this.fetchRoomData(); this.setupEventListeners(); this.startUpdateCycle(); this.initialized = true; } createUI() { GM_addStyle(STYLE); const container = document.createElement('div'); container.id = 'garticpro'; container.innerHTML = ` <div class="gp-header"> <div class="gp-title"> <div class="gp-logo"> <i class="fas fa-dragon"></i> </div> <div> <div class="gp-name">GarticPro</div> <div class="gp-version">v3.0</div> </div> </div> </div> <div class="gp-content"> <div class="gp-stats"> <div class="gp-stat"> <div class="gp-stat-icon"> <i class="fas fa-users"></i> </div> <div class="gp-stat-info"> <div class="gp-stat-value" id="gp-player-count">0</div> <div class="gp-stat-label">Players</div> </div> </div> <div class="gp-stat"> <div class="gp-stat-icon"> <i class="fas fa-door-open"></i> </div> <div class="gp-stat-info"> <div class="gp-stat-value" id="gp-room-count">0</div> <div class="gp-stat-label">Rooms</div> </div> </div> </div> <div class="gp-tabs"> <button class="gp-tab active" data-tab="players"> <i class="fas fa-users"></i> Players </button> <button class="gp-tab" data-tab="rooms"> <i class="fas fa-door-open"></i> Rooms </button> </div> <div class="gp-list gp-players" id="gp-player-list"></div> <div class="gp-list gp-rooms" id="gp-room-list" style="display: none;"></div> </div> `; const toggle = document.createElement('div'); toggle.className = 'gp-toggle'; toggle.innerHTML = '<i class="fas fa-dragon"></i>'; toggle.onclick = () => container.classList.toggle('active'); document.body.appendChild(container); document.body.appendChild(toggle); const loadFontAwesome = () => { const link = document.createElement('link'); link.rel = 'stylesheet'; link.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css'; document.head.appendChild(link); }; loadFontAwesome(); } fetchRoomData() { fetch("https://gartic.io/req/list?search=&language[]=8") .then(response => response.json()) .then(data => { data.forEach(room => { if (room.quant > 0) { this.getData(room.code); } }); }); setTimeout(() => this.fetchRoomData(), 5000); } getData(roomCode) { const players = []; ['01', '02', '03', '04', '05', '06'].forEach(server => { const ws = new WebSocket(`wss://server${server}.gartic.io/socket.io/?EIO=3&transport=websocket`); ws.onopen = () => { ws.send(`42[12,{"v":20000,"platform":0,"sala":"${roomCode.slice(-4)}"}]`); }; ws.onmessage = (msg) => { if (msg.data.startsWith('42')) { const data = JSON.parse(msg.data.slice(2)); if (data[0] == 5) { data[5].forEach(user => { players.push({ points: user.pontos, victory: user.vitorias, id: user.id, avatar: user.avatar, room: `https://gartic.io/${roomCode}`, nick: user.nick, foto: user.foto }); this.players.set(user.id, { points: user.pontos, victory: user.vitorias, id: user.id, avatar: user.avatar, room: `https://gartic.io/${roomCode}`, nick: user.nick, foto: user.foto }); }); this.rooms.set(roomCode, { code: roomCode, players: players.length, language: data[1], timestamp: Date.now(), playersData: players }); this.updatePlayerList(); this.updateRoomList(); this.updateStats(); ws.close(); } } }; }); } updatePlayerList() { const container = document.getElementById('gp-player-list'); if (!container || this.activeTab !== 'players') return; const players = Array.from(this.players.values()) .sort((a, b) => b.points - a.points); container.innerHTML = players.map(player => ` <div class="gp-player" data-id="${player.id}"> <div class="gp-avatar"> <img src="${player.foto || `https://gartic.io/static/images/avatar/svg/${player.avatar}.svg`}" alt="${player.nick}"> </div> <div class="gp-info"> <div class="gp-name">${player.nick}</div> <div class="gp-stats"> <div> <i class="fas fa-star"></i> ${player.points.toLocaleString()} </div> <div> <i class="fas fa-door-open"></i> ${player.room} </div> </div> </div> </div> `).join(''); } updateRoomList() { const container = document.getElementById('gp-room-list'); if (!container || this.activeTab !== 'rooms') return; const rooms = Array.from(this.rooms.values()) .sort((a, b) => b.players - a.players); container.innerHTML = rooms.map(room => ` <div class="gp-room" data-code="${room.code}"> <div class="gp-info"> <div class="gp-name">Room: ${room.code}</div> <div class="gp-stats"> <div> <i class="fas fa-users"></i> ${room.players} Players </div> <div> <i class="fas fa-globe"></i> ${room.language.toUpperCase()} </div> </div> </div> <div class="gp-actions"> <button class="gp-action" onclick="window.open('https://gartic.io/${room.code}/viewer', '_blank')"> <i class="fas fa-eye"></i> </button> </div> </div> `).join(''); } updateStats() { const playerCount = document.getElementById('gp-player-count'); const roomCount = document.getElementById('gp-room-count'); if (playerCount) { playerCount.textContent = this.players.size; } if (roomCount) { roomCount.textContent = this.rooms.size; } } setupEventListeners() { document.querySelectorAll('.gp-tab').forEach(tab => { tab.addEventListener('click', () => { document.querySelectorAll('.gp-tab').forEach(t => t.classList.remove('active')); tab.classList.add('active'); this.activeTab = tab.dataset.tab; if (this.activeTab === 'rooms') { document.getElementById('gp-player-list').style.display = 'none'; document.getElementById('gp-room-list').style.display = 'block'; this.updateRoomList(); } else { document.getElementById('gp-room-list').style.display = 'none'; document.getElementById('gp-player-list').style.display = 'block'; this.updatePlayerList(); } }); }); } startUpdateCycle() { setInterval(() => { this.cleanupOldData(); if (this.activeTab === 'players') { this.updatePlayerList(); } else { this.updateRoomList(); } this.updateStats(); }, 5000); } cleanupOldData() { const now = Date.now(); for (const [id, player] of this.players) { if (now - player.lastSeen > 30000) { this.players.delete(id); } } for (const [code, room] of this.rooms) { if (now - room.timestamp > 30000) { this.rooms.delete(code); } } } } if (window.top === window.self) { const app = new GarticPro(); window.garticPro = app; if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => app.init()); } else { app.init(); } } })();