Pastel Live

Gartic.io Pastel Live – watch rooms, players and chat in real time.

// ==UserScript==
// @name             Pastel Live
// @namespace        https://github.com/GameSketchers/Pastel-Live
// @version          0.1
// @description      Gartic.io Pastel Live – watch rooms, players and chat in real time.
// @description:tr   Gartic.io Pastel Live – odaları, oyuncuları ve sohbeti anlık takip et.
// @author           Qwyua
// @match            *://gartic.io/*
// @match            *://www.croxyproxy.com/*
// @match            *://*/__cpi.php?s=*
// @run-at           document-start
// @grant            GM_addStyle
// @grant            GM_setValue
// @grant            GM_getValue
// @grant            GM_addValueChangeListener
// @grant            GM_removeValueChangeListener
// @license          MIT
// ==/UserScript==

// ==>>>> https://gartic.io/live <<<<== //

// ===== Versiyon Notlari ♢ 0.1 ======= //
// ● zaten bir cok cok guzel calisan benzeri userscriptler (https://gf.qytechs.cn/tr/scripts/524297, https://gf.qytechs.cn/en/scripts/499148) var onlardan ilham alinmistir
// ● tek farki proxy ile harmanlanmis ustune biraz sos dokulmus ve servis edilmek uzere masaniza konulmustur maksat oyle ugras olsun
// ● mumkun olan en temel kodu paylastim belki alt yapisi alip kendi projelerine entegre edenler olur
// ==================================== //

const [GM_onMessage,GM_sendMessage,getCookie,onBodyReady,observer,rand]=[
    (k,c)=>GM_addValueChangeListener(k,(_,__,o)=>c(...o)),
    (k,...d)=>GM_setValue(k,d),
    c=>document.cookie.split("; ").find(e=>e.startsWith(c+"="))?.split("=")[1],
    cb=>document.body?cb():new MutationObserver((_,o)=>document.body&&(o.disconnect(),cb())).observe(document.documentElement,{childList:1}),
    (s,c)=>{const w=()=>{const e=document.querySelector(s);e&&(c(e),o.disconnect())};const o=new MutationObserver(w);document.body?o.observe(document.body,{childList:1,subtree:1}):new MutationObserver((_,m)=>{document.body&&(m.disconnect(),o.observe(document.body,{childList:1,subtree:1}))}).observe(document.documentElement,{childList:1})},
    _=>Math.floor(Math.random()*100)+1
];

document.URL.includes("/__cpi")&&window.self!==window.top&&(async()=>{try{GM_sendMessage('iframe-msg',`succes`,window.location.href)}catch(e){console.error("w",e)}})();
document.URL.includes("pr0xy")&&window.self!==window.top&&(window.name=window.location.search.split('=').pop())|observer('.fa.fa-arrow-right',btn=>btn.dispatchEvent(new MouseEvent("click",{bubbles:true,button:0})));
document.URL.includes("/servers")&&window.self!==window.top&&observer('input[name=proxyServerId]',e=>{e.value=window.name;e.parentElement.submit()});
document.URL.includes("/requests?fso=")&&window.self!==window.top&&(console.log("wwww")|GM_sendMessage('iframe-msg', 'error'));
document.URL.includes("https://gartic.io/live") && (() => {
    GM_addStyle(`*{margin:0;padding:0;box-sizing:border-box}body{font-family:"Segoe UI",sans-serif;height:100vh;display:flex;flex-direction:column;background:linear-gradient(145deg,#1b1a2e,#161429 70%,#12101f);color:#eee;overflow:hidden}.PastelLive-mainTitle{font-size:2em;font-weight:700;text-align:center;padding:20px;background:linear-gradient(90deg,#c2a6f0,#8bbcd4);-webkit-background-clip:text;-webkit-text-fill-color:transparent;text-shadow:0 0 12px #221b3a,0 0 20px #2e224f}#PastelLive-startScreen {position: fixed;inset: 0;display: flex;flex-direction: column;justify-content: center;align-items: center;gap: 25px;background: linear-gradient(160deg, #0f0c29, #302b63, #24243e);color: #fff;z-index: 9999;font-family: "Segoe UI", sans-serif;animation: fadeIn 1s ease;user-select: none;}#PastelLive-startScreen h1 {font-size: 3.5em;color: #ff4e50;position: relative;display: inline-block;letter-spacing: 1px;}#PastelLive-startScreen h1 {font-size: 3.5em;background: linear-gradient(270deg, #fffb7d 20%, #ff4e50 40%, #f9d423 60%, #fffb7d 80%);background-size: 400% 100%;-webkit-background-clip: text;-webkit-text-fill-color: transparent;animation: shine 7s linear infinite;letter-spacing: 1px;}#PastelLive-version {margin-left:-17px;font-size: 0.20em;bottom: 0;right: 0;color: #ccc;text-shadow: none;}@keyframes shine {0% {background-position: 0% 0;}100% {background-position: 100% 0;}}.PastelLive-subtitle {font-size: 1.2em;color: #ccc;text-shadow: 0 0 6px #302b63;}#PastelLive-startScreen select {padding: 10px 20px;font-size: 1.1em;font-weight: 600;background: #1b1a2e;color: #fff;border: none;outline: none;box-shadow: inset 0 0 5px #ff4e50;transition: 0.2s;}#PastelLive-startScreen select:hover {box-shadow: inset 0 0 12px #f9d423;transform: scale(1.03);}.PastelLive-btn-row {display: flex;gap: 15px;}#PastelLive-startScreen button {padding: 12px 28px;font-size: 1.1em;font-weight: 700;background: linear-gradient(90deg, #ff4e50, #f9d423);color: #0f0c29;border: none;outline: none;cursor: pointer;transition: 0.25s;box-shadow: 0 0 8px #ff4e50, 0 0 12px #f9d423;}#PastelLive-startScreen button:hover {transform: scale(1.05);box-shadow: 0 0 15px #ff4e50, 0 0 20px #f9d423;}#PastelLive-startBtn:disabled {background: #3a3a4f;color: #999;cursor: not-allowed;box-shadow: inset 0 0 8px #000;}#PastelLive-proxyBtn {background: #1b1a2e;color: #f9d423;box-shadow: 0 0 8px #f9d423;transition: 0.25s;cursor: pointer;opacity: 0.9;}#PastelLive-proxyBtn:hover {transform: scale(1.05);box-shadow: 0 0 15px #f9d423, 0 0 20px #ff4e50;}#PastelLive-proxyBtn:disabled {background: #3a3a4f;color: #999;cursor: not-allowed;box-shadow: inset 0 0 8px #000;pointer-events: none;opacity: 0.7;}@keyframes fadeIn {from {opacity:0;}to {opacity:1;}}main.PastelLive-main{flex:1;display:none;grid-template-columns:2fr 1fr;gap:20px;padding:15px;overflow:hidden;opacity:0;animation:fadeIn 0.8s forwards}.PastelLive-left{display:flex;flex-direction:column;gap:12px;overflow:hidden}.PastelLive-filters{display:flex;gap:10px;align-items:center;background:#2a2a3e;padding:10px;border-radius:25px;box-shadow:0 0 8px #8bbcd4;flex-wrap:wrap;justify-content:space-between;opacity:0;animation:fadeIn 0.8s 0.2s forwards}.PastelLive-filters input{flex:2;padding:10px 14px;border-radius:20px;border:none;background:#212133;color:#eee;box-shadow:inset 0 0 6px #8bbcd4}.PastelLive-filters select{flex:1;padding:10px 14px;border-radius:20px;border:none;background:#212133;color:#eee;box-shadow:inset 0 0 6px #8bbcd4}#PastelLive-stats{display:flex;gap:10px;flex-direction:column;color:#c8b6ff;font-size:0.85em}.PastelLive-players{flex:1;overflow-y:auto;display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:15px;padding-right:5px;opacity:0;animation:fadeIn 0.8s 0.4s forwards}.PastelLive-card{background:#212133;border-radius:15px;padding:15px;text-align:center;cursor:pointer;transition:.25s;box-shadow:0 3px 10px #0007;display:flex;flex-direction:column;align-items:center}.PastelLive-card:hover{transform:translateY(-4px) scale(1.03);box-shadow:0 0 15px #c2a6f0,0 0 22px #8bbcd4}.PastelLive-avatar{width:60px;height:60px;border-radius:50%;margin-bottom:10px;background:linear-gradient(135deg,#c2a6f0,#8bbcd4);display:flex;align-items:center;justify-content:center;font-size:1.4em;color:#fff;text-shadow:0 0 3px #000}.PastelLive-name{font-size:1.05em;font-weight:600;margin-bottom:4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}.PastelLive-status{font-size:.85em}.PastelLive-online{color:#7fffd4}.PastelLive-offline{color:#ff9aa2}.PastelLive-chat{background:#1f1f30;border-radius:15px;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 0 12px #000a;opacity:0;animation:fadeIn 0.8s 0.6s forwards}.PastelLive-chatHeader{background:linear-gradient(90deg, #5510c7, #210d65);padding:10px;color:#000;font-weight:600;font-size:1.1em;display:flex;justify-content:space-between;align-items:center;border-radius:12px 12px 0 0;box-shadow:0 0 10px #000a}.PastelLive-chatButtons{display:flex;gap:5px}.PastelLive-chatButtons button{padding:5px 10px;border:none;border-radius:15px;background:#2a2a3e;color:#fff;cursor:pointer;transition:.2s;box-shadow:0 0 6px #8bbcd4}.PastelLive-chatButtons button:hover{transform:scale(1.05);box-shadow:0 0 10px #c2a6f0}.PastelLive-searchMsg{padding:8px;background:#2a2a3e}.PastelLive-searchMsg input{width:100%;padding:10px;border-radius:20px;border:none;background:#212133;color:#eee;box-shadow:inset 0 0 6px #8bbcd4}.PastelLive-msgs{flex:1;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:10px}.PastelLive-msg{background:#212133;border-radius:12px;padding:8px 12px;max-width:100%;box-shadow:0 0 5px #0007}.PastelLive-msg b{color:#c8b6ff;margin-right:6px}.PastelLive-players::-webkit-scrollbar,.PastelLive-msgs::-webkit-scrollbar{width:10px}.PastelLive-players::-webkit-scrollbar-track,.PastelLive-msgs::-webkit-scrollbar-track{background:#1f1f30;border-radius:10px}.PastelLive-players::-webkit-scrollbar-thumb,.PastelLive-msgs::-webkit-scrollbar-thumb{background:linear-gradient(180deg,#8bbcd4,#c2a6f0);border-radius:10px}.PastelLive-players::-webkit-scrollbar-thumb:hover,.PastelLive-msgs::-webkit-scrollbar-thumb:hover{background:linear-gradient(180deg,#c2a6f0,#8bbcd4)}@media(max-width:900px){main.PastelLive-main,#PastelLive-app.PastelLive-main {grid-template-columns:1fr;grid-template-rows:1fr 1fr;}}@media(max-width:500px){.PastelLive-players,#PastelLive-players {grid-template-columns:repeat(auto-fill,minmax(140px,1fr));}.PastelLive-avatar{width:55px;height:55px;font-size:1.2em;}}.PastelLive-credit{position:absolute;bottom:10px;left:10px;font-size:15px;color:#bbb;font-family:"Segoe UI",sans-serif;opacity:0.8}.PastelLive-credit a{color:#6cf;text-decoration:none;font-weight:600}.PastelLive-credit a:hover{text-decoration:underline}.PastelLive-credit .heart{color:#e25555;margin:0 2px}`)
    const overlay = Object.assign(document.createElement('div'), {id:'PastelLiveOverlay', style:'position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:999;background:#161429;overflow:hidden;display:flex;flex-direction:column;font-family:"Segoe UI",sans-serif;color:#eee;'});
    overlay.innerHTML = `
          <div class="PastelLive-mainTitle">Pastel Live</div>
        <div id="PastelLive-startScreen">
        <h1>Pastel Live
            <span id="PastelLive-version">v0.1</span>
        </h1>
         <p class="PastelLive-subtitle">Select Language & Start</p>
            <select id="PastelLive-lang">
              <option value="8">Türkçe</option>
              <option value="2">English</option>
              <option value="1">Português</option>
              <option value="45">Indonesia</option>
              <option value="7">Русский</option>
              <option value="3">Español</option>
              <option value="19">العربية</option>
        </select>
         <div class="PastelLive-btn-row">
           <button id="PastelLive-startBtn">Başlat</button>
           <button id="PastelLive-proxyBtn">Get Proxy (0)</button>
        </div>
  <div class="PastelLive-credit">
    GameSketchers • by <a href="https://github.com/GameSketchers/Pastel-Live" target="_blank">Qwyua</a>  <span class="heart">♥</span> with love
  </div>
      </div>
        <main id="PastelLive-app" class="PastelLive-main" style="display:none;flex:1;grid-template-columns:2fr 1fr;gap:20px;padding:15px;overflow:hidden;">
            <div class="PastelLive-left">
                <div class="PastelLive-filters">
                    <input id="PastelLive-search" placeholder="oyuncu ara">
                    <input id="PastelLive-roomCode" placeholder="oda kodu">
                    <div id="PastelLive-stats">
                        <span id="PastelLive-activePlayers">Aktif Oyuncular: 0</span>
                        <span id="PastelLive-activeRooms">Aktif Odalar: 0</span>
                    </div>
                </div>
                <div class="PastelLive-players" id="PastelLive-players"></div>
            </div>
            <div class="PastelLive-chat">
                <div class="PastelLive-chatHeader">Chat
                    <div class="PastelLive-chatButtons">
                        <button id="PastelLive-saveChat">Kaydet</button>
                        <button id="PastelLive-resetChat">Reset</button>
                    </div>
                </div>
                <div class="PastelLive-searchMsg"><input id="PastelLive-msgSearch" placeholder="mesaj ara"></div>
                <div class="PastelLive-msgs" id="PastelLive-msgs">
                </div>
            </div>
        </main>
    `;
    document.documentElement.prepend(overlay);

    class PastelLive {
        proxies = GM_getValue("PastelLive-Proxies");
        constructor(overlay) {
            this.overlay = overlay;
            this.playersContainer = overlay.querySelector("#PastelLive-players");
            this.messagesContainer = overlay.querySelector("#PastelLive-msgs");
            this.startScreen = overlay.querySelector("#PastelLive-startScreen");
            this.app = overlay.querySelector("#PastelLive-app");
            this.activePlayersSpan = overlay.querySelector("#PastelLive-activePlayers");
            this.activeRoomsSpan = overlay.querySelector("#PastelLive-activeRooms");
            this.startButton = overlay.querySelector("#PastelLive-startBtn");
            this.proxyButton = overlay.querySelector("#PastelLive-proxyBtn")
            this.activePlayers = overlay.querySelector("#PastelLive-activePlayers")
            this.activeRooms = overlay.querySelector("#PastelLive-activeRooms")
            this.searchInput = overlay.querySelector("#PastelLive-search");
            this.roomInput = overlay.querySelector("#PastelLive-roomCode");
            this.msgSearchInput = overlay.querySelector("#PastelLive-msgSearch");
            this.initEvents();
            if (this.proxies === undefined){this.proxies=[];GM_setValue("PastelLive-Proxies", this.proxies)}
            if (Array.isArray(this.proxies)&&this.proxies.length===0){
                this.startButton.disabled=true;
                this.proxies=[];
                GM_setValue("PastelLive-Proxies",this.proxies);
            }
            Array.isArray(this.proxies)&&this.proxies.length&&(()=>{
                const ws = new WebSocket(`wss://${this.proxies[0]}/__cpw.php?u=d3NzOi8vc2VydmVyMDYuZ2FydGljLmlvL3NvY2tldC5pby8/RUlPPTMmdHJhbnNwb3J0PXdlYnNvY2tldA==&o=aHR0cHM6Ly9nYXJ0aWMuaW8=`);
                ws.onopen=()=>{ws.close()};
                ws.onerror=()=>{GM_setValue("PastelLive-Proxies",this.proxies=[]);location.reload()};
                //ws.onclose=e=>console.log('kapandi',e.code);
            })();
            this.proxyButton.textContent = `Get Proxy (${this.proxies?.length||0})`;

        }

        initEvents() {
            this.searchInput.addEventListener("input", () => this.filterPlayers());
            this.roomInput.addEventListener("input", () => this.filterPlayers());
            this.msgSearchInput.addEventListener("input", () => {
                this.filterChat()
            });

            this.startButton.addEventListener("click", () => {
                this.startScreen.style.display = "none";
                this.app.style.display = "grid";
                const lang = document.querySelector("#PastelLive-lang").value;
                this.playerSearchGO(lang);
            });

            this.proxyButton.addEventListener('click',()=>{
                this.startButton.disabled = true;
                this.proxyButton.disabled = true;
                this.addNewProxy()
            });

        }

        filterChat(){
            [...this.messagesContainer.children].forEach(msg => {
                msg.style.display = msg.textContent.toLowerCase().includes(this.msgSearchInput.value.toLowerCase()) ? "block" : "none";
            });

        }
        async addNewProxy() {
            try {
                //bunun sistemine zaten asinaydim
                //https://github.com/Qwyua/ProxyModule
                const data = await GM_getValue('PastelLive');
                const list = data?.ProxyList ?? [];
                if (!list.length) return console.log('!asd');
                const existing = new Set(this.proxies ?? []);
                const limit = existing.size ? 15 : 10;
                const newProxies = list.map(x => x.ip).filter(ip => !existing.has(ip)).slice(0, limit);
                if (!newProxies.length) return console.log('!!asd');
                const spawn=l=>l.forEach(x=>document.body.append(Object.assign(document.createElement`iframe`,{id:'proxy-iframe-'+x.id,style:'display:none!important;width:0;height:0;opacity:0;pointer-events:none;position:absolute;top:0;left:0;z-index:-9999;',src:`https://www.croxyproxy.com/?pr0xy=true&proxyid=${x.id}`,sandbox:'allow-scripts allow-same-origin allow-forms allow-top-navigation'})));
                const targets=list.filter(x=>newProxies.includes(x.ip)).slice(0,15);
                spawn(targets);
                const collect=(n,g=3e4,t=n-4)=>new Promise(r=>{
                    const h=new Set;let d;const f=_=>(GM_removeValueChangeListener?.('iframe-msg',m),r([...h]))
                    ,s=_=>(clearTimeout(d),d=setTimeout(f,g))
                    ,m=(_,u)=>(h.add(new URL(u).hostname),h.size>=n?f():h.size>=t&&s());
                    GM_onMessage('iframe-msg',m)
                })
                const collected = await collect(targets.length, 8000,targets.length-4);
                this.proxies ??= [];
                const filtered = collected.filter(ip => !ip.startsWith('51.'));
                this.proxies.push(...filtered);
                GM_setValue("PastelLive-Proxies", this.proxies);
                //console.log('fffff:', this.proxies);
                this.proxyButton.textContent = `Get Proxy (${this.proxies?.length||0})`;
                this.proxyButton.disabled = false;
                this.startButton.disabled = false;
                document.querySelectorAll('iframe')?.forEach(f => f.remove());
            } catch (error) {
                console.error(error);
            }
        }

        filterPlayers() {
            const q = this.searchInput.value.toLowerCase();
            const r = this.roomInput.value.toLowerCase();
            [...this.playersContainer.children].forEach(card => {
                const name = card.querySelector(".PastelLive-name").textContent.toLowerCase();
                const room = card.querySelector(".PastelLive-status").textContent.toLowerCase();
                card.style.display = (name.includes(q) && room.includes(r)) ? "flex" : "none";
            });
            const visible = [...this.playersContainer.children].filter(c => c.style.display !== "none");
            this.activePlayersSpan.textContent = `Aktif Oyuncular: ${visible.length}`;
            this.activeRoomsSpan.textContent = `Aktif Odalar: ${new Set(visible.map(c => c.querySelector(".PastelLive-status").textContent)).size}`;
        }

        addPlayer(avatar,name, room) {
            const card = Object.assign(document.createElement("div"), { className: "PastelLive-card" });
            card.append(
                Object.assign(document.createElement("img"),{className:"PastelLive-avatar",src:avatar}),
                Object.assign(document.createElement("div"),{className:"PastelLive-name",textContent:name}),
                Object.assign(document.createElement("div"),{className:"PastelLive-status",textContent:`Oda Kodu: ${room}`})
            );
            this.playersContainer.appendChild(card);
            this.filterPlayers();
        }

        addMessage(author, text) {
            const msg = document.createElement("div");
            msg.className = "PastelLive-msg";
            msg.innerHTML = `<b>${author}:</b> ${text}`;
            this.messagesContainer.appendChild(msg);
            this.filterChat()
        }

        async playerSearchGO(languagecode) {
            var odalar=0;
            var oyuncular=0;
            try {
                const response = await fetch(`https://gartic.io/req/list?search=&language[]=${languagecode}`);
                const rooms = await response.json();
                const roomCodes = rooms.map(r => r.code);
                const proxyToRooms = roomCodes.map((room, index) => ({
                    room,
                    proxy: this.proxies[index % this.proxies.length]
                }));
                //console.log(proxyToRooms);
                const servers = {"server07":"c","server06":"Y","server05":"U","server04":"Q","server03":"M","server02":"I","server01":"E"};
                class PlayerSt {
                    map = new Map();
                    constructor(list = []) { list.forEach(p => this.map.set(String(p.id), p.name)); }
                    add = p => this.map.set(String(p.id), p.name);
                    getName = id => this.map.get(String(id)) ?? null;
                    remove = id => this.map.delete(String(id));
                    update = (id, name) => this.map.has(String(id)) && this.map.set(String(id), name);
                }
                const plst = new PlayerSt([]);


                proxyToRooms.forEach(proxy => {
                    const roomid = proxy.room.substring(2);
                    Object.entries(servers).forEach(([_,scode])=>{ //
                        const websocket = new WebSocket(`wss://${proxy.proxy}/__cpw.php?u=d3NzOi8vc2VydmVyMD${scode}uZ2FydGljLmlvL3NvY2tldC5pby8/RUlPPTMmdHJhbnNwb3J0PXdlYnNvY2tldA==&o=aHR0cHM6Ly9nYXJ0aWMuaW8=`);
                        websocket.onopen = () => {
                            websocket.send(`42[12,{"v":20000,"sala":"${roomid}"}]`);
                            websocket.send(`42[46,0]`);
                            const ping = () => {
                                if (websocket.readyState === WebSocket.OPEN) {
                                    websocket.send("2");
                                    setTimeout(ping, 8000);
                                }
                            };
                            ping();
                        };

                        websocket.onmessage = e => {
                            try {
                                if (!e.data.startsWith("42")) return;
                                const data = JSON.parse(e.data.slice(2));
                                if (data[0] === "5") {
                                    for (const player of data[5]) {
                                        plst.add({id:player.id,name:player.nick})
                                        this.addPlayer(
                                            player.foto ?? `https://gartic.io/static/images/avatar/svg/${player.avatar}.svg`,
                                            player.nick,
                                            proxy.room
                                        );
                                        this.activePlayers.innerText = `oyuncular: ${++oyuncular}`;
                                    }
                                    this.activeRooms.innerText = `odalar: ${++odalar}`;
                                }
                                data[0]==="6"&&data[1]==6&&websocket.close()
                                data[0]==="11"&&this.addMessage(plst.getName(data[1]),data[2])
                            } catch {}
                        };
                        websocket.onerror=e=>{};
                    });
                });



            }catch(e){}
        }


        async initProxies() {
            try {
                const existing = await GM_getValue('PastelLive');
                if (existing) return console.log('PastelLive running...');
                const json = await (await fetch('https://raw.githubusercontent.com/Qwyua/ProxyModule/main/src/ProxyModuleList.json')).json();
                const newProxies = json?.proxyList?.filter(p=>p.active&&!p.ip.startsWith('51.'))??[];
                const data = { ProxyList: newProxies, runProxy: true };
                await GM_setValue('PastelLive', data);
                console.log('PastelLive Started.', data);
            } catch (e) {
                console.error('PastelLive error!', e);
            }
        }
    }

    (async () => {
        const overlay = document.querySelector('#PastelLiveOverlay');
        const live = new PastelLive(overlay);
        await live.initProxies();
    })();


})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址