您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Add people and join their lobbies when they're online.
// ==UserScript== // @name Friends List // @namespace http://tampermonkey.net/ // @version 0.1 // @description Add people and join their lobbies when they're online. // @author Two // @match https://sketchful.io/ // @icon https://www.google.com/s2/favicons?domain=sketchful.io // @require https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.4/socket.io.min.js // @grant none // @run-at document-start // ==/UserScript== (function () { 'use strict'; var log = false, bindn = false, upd, url = "https://cors.bridged.cc/https://sk-fl.glitch.me/flist", tb, id, pages = ["mainp", "settingp", "infop"], titles = ["Friends List", "Settings", "Information"], logged = `<tr> <th></th><th></th> <th> </th> <th></th></tr> <tr><td style=" font-size: 10px; "><span style=" white-space: nowrap; ">You are currently logged out.</span><hr><span style=" white-space: nowrap; display: block; ">How to login:</span><ol><li>Login to Sketchful</li><li>Refresh the page or play a round.</li></ol></td> </tr>`, nof = `<tr> <th></th><th></th> <th> </th> <th></th></tr> <tr><td style=" font-size: 10px; width: 100%; "><span style=" white-space: nowrap; ">You have no friends :(</span><hr><span style=" white-space: nowrap; display: block; ">Adding Friends:</span><ol><li>Click the gear on the bottom right.</li><li>Add someone using their ID.</li></ol></td> </tr>`, room, name; try { let ls = JSON.parse(window.localStorage.getItem("id")) var nc = setInterval(async function () { if (document.querySelector(".fname input")) { document.querySelector(".fname input").value = ls.name; clearInterval(nc) } }, 100) name = ls.name; } catch (e) { window.localStorage.setItem("id", JSON.stringify({})) } bind(); function bind() { if (log) return; if (bindn) return; bindn = true; Function.prototype.bind2 = Function.prototype.bind; Function.prototype.bind = function (o, t, th, f, fi) { if (t) return this.bind2(o, t, th) let i = o if (o.game) i = o.game; if (i.login) { setTimeout(() => { if (i.login.data) { try { if (id) return this.bind2(o); id = o.login.data.uuid connect() bindn = false; Function.prototype.bind = Function.prototype.bind2 } catch (e) {} } }, 100) } return this.bind2(o) } } function setItem(c, n) { let prev = JSON.parse(window.localStorage.getItem("id")); prev[c] = n; window.localStorage.setItem("id", JSON.stringify(prev)); } document.addEventListener("DOMContentLoaded", function (event) { document.body.insertAdjacentHTML('beforeend', `<div> <style> ::-webkit-scrollbar { width: 5px; } ::-webkit-scrollbar-track { background: #f1f1f1; } ::-webkit-scrollbar-thumb { background: #888; } ::-webkit-scrollbar-thumb:hover { background: #555; } .hide { display: none !important; } .fixed{ display: block; height: 107px; overflow-y: scroll; overflow-x: hidden; } .flist{ border-radius: 10px; border: 2px solid black; right: 10px; position: absolute; background: lightgray; bottom: -160px; width: 149px; height: 190px; z-index: 100; transition: transform 1s; overflow: hidden; } .open { -webkit-transform-origin: 100% 50%; transform: translateY(-160px); } .close2 { -webkit-transform-origin: 100% 50%; transform: translateY(0px); } .overflow { text-overflow: ellipsis; white-space: nowrap; overflow: hidden; max-width: 70px; } .overflow:hover { overflow: visible; } .overflow:hover a { position: relative; background-color: white; box-shadow: 0 0 4px 0 black; border-radius: 1px; } .setting img{ width: 25px; height: 25px; cursor: pointer; } .setting{ position: absolute; bottom: 0;right: 0; } .logout{ position: absolute; bottom: 1px;left: 4px; } .logout img{ width: 13px; height: 17px; cursor: pointer; } .info{ margin-top:-18px; margin-left:3px; position: sticky; z-index:1; } .info img{ width: 17px; height: 17px; cursor: pointer; } .settingp{ width: 100%; height: 100%; position: absolute; top: 30px; } .settingp div p{ position: absolute; display: contents; } .settingp div input{ width: 81px; height: 20px; right: 0px; position: absolute; } .add img{ width: 15px; margin-top: -5px; margin-left: 5px; cursor: pointer; } .add input{ width: 120px !important; } .fid{ font-size: larger; padding: 0 25px; } #flistb{ cursor: pointer; width: 100%; height: 15%; } #ftitle{ text-align: center; padding: 4px 31px; margin-top: 10px; border: 2px solid black; white-space: nowrap; margin-left: -2px; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .status{ cursor: pointer; } .status:hover span { display:none } .status:hover:before { content:"⭕" } </style><div class="flist open" id="flist"><div> <div id="flistb"><span id="ftitle">Friends List</span></div> <br><div id="pages"><div class="info"><img src="https://i.imgur.com/lWJ0olo.png"></div><table class="mainp fixed"> <colgroup> <col width="40"> <col width="70"> <col width="30"> <col width="40"> <col width="20"> </colgroup> <tbody id="tb">` + logged + `</tbody></table><div class="settingp hide"> <strong class="fid">ID: </strong> <div class="fname"><p>Name</p><input></div> <div class="add"><img src="https://i.imgur.com/9KXko34.png"><input type="number"></div> </div><div class="infop hide"> <div style=" font-size: 10px; max-height: 101px; overflow-y: auto; overflow-x: hidden; "><ul style=" display: contents; "><li>1. Sometimes you have to refresh twice to login. Keep that in mind.</li><br><li>2. You can friend someone by adding them with their ID in the settings.</li><br><li>3. To join a friend's lobby, you both must have eachother added. Then click their name when it goes blue.</li><br><li>4. Friends can be removed by clicking their status icon.</li> <br><li>5. You can change your name with the "Name" textbox in the settings.</li><br><li>6. Please keep in mind that there might be some bugs :)</li><br><li>7. If something went wrong, please contact Two#2131 on Discord. Although, after some time he'll probably stop responding.</li></ul></div> </div></div> <div class="logout"><img src="https://i.imgur.com/pMzdGB1.png"></div> <div class="setting"><img src="https://static.thenounproject.com/png/1524589-200.png"></div></div></div></div>`); let settingi = document.getElementsByClassName("setting")[0] let infoi = document.getElementsByClassName("info")[0] tb = document.getElementById("tb") String.prototype.replace2 = String.prototype.replace; String.prototype.replace = function (o, t, tr) { if (o == "#" && this.toString().length == 6) { let tmproom = this.toString().replace2("#", "") setTimeout(() => { if (tmproom == document.getElementById("roomInfoLink").value.split("/")[2]) { room = tmproom; updateRoom(); } }, 3000) } return this.toString().replace2(o, t) } settingi.onclick = function (f) { let isOn = getPage("setting").classList.contains("hide") ? changePage("settingp") : changePage("mainp") } infoi.onclick = function (f) { let isOn = getPage("info").classList.contains("hide") ? changePage("infop") : changePage("mainp") } document.getElementById("flistb").onclick = function (f) { let flist = document.getElementById("flist") if (flist.classList.contains('close2')) { flist.classList.remove('close2') flist.classList.add('open'); } else { flist.classList.remove('open') flist.classList.add('close2'); } } document.getElementsByClassName("logout")[0].onclick = function (f) { logout(); } document.querySelector(".add img").onclick = function (f) { let input = document.querySelector(".add input") if (input.value && /^\d+$/.test(input.value)) add(input.value, "add"); } var timer; document.querySelector(".fname input").onkeyup = function (f) { let input = document.querySelector(".fname input") clearTimeout(timer); if (input.value) { timer = setTimeout(() => { name = input.value; setItem("name", name); }, 5000); } } }) function getPage(page) { return document.getElementsByClassName([page + "p"])[0] } function changePage(page) { let pad = ["31px", "43px", "31px"] let title = document.getElementById("ftitle") let pageC = document.getElementsByClassName(page)[0] pageC.classList.remove("hide") title.style.padding = "4px " + pad[pages.indexOf(page)] title.innerHTML = titles[pages.indexOf(page)] for (var i = 0; i < pages.length; i++) { if (pages[i] != page) document.getElementsByClassName(pages[i])[0].classList.add("hide") } } function updateFriends(friends) { tb.innerHTML = ""; friends = friends.filter(Boolean) if (!friends.length) tb.innerHTML = nof; for (var i = 0; i < friends.length; i++) { tb.insertAdjacentHTML('beforeend', ` <tr> <td title="${friends[i].status}" class="status" data-id="${friends[i].uuid}"><span>${friends[i].status=="Online"?"🟢":"⚫"}</span></td><td class="overflow"><a title='#${friends[i].lobby.code}' ${(friends[i].lobby.players||0)>0?`href="#" onclick="lobbyConnect('#${friends[i].lobby.code}')`:""}">${friends[i].name}</a></td> <td> </td> <td>${(friends[i].lobby.players||0)+"/?"}</td></tr>`) document.getElementsByClassName("status")[i].onclick = function (f) { add(this.getAttribute("data-id"), "remove"); } } if (friends.length) sortTable(1); } async function connect() { var chk = setInterval(async function () { if (document.getElementsByClassName("fid").length) { clearInterval(chk); let exists = await (await fetch(url, { method: 'POST', body: JSON.stringify({ "type": "setid", "id": id }), headers: { 'Content-Type': 'application/json' } })).json(); await login(id); } }, 2000) } async function login(id) { let login = await (await fetch(url, { method: 'POST', body: JSON.stringify({ "type": "login", "id": id }), headers: { 'Content-Type': 'application/json' } })).json(); document.getElementsByClassName("fid")[0].innerHTML = "ID: " + login.uuid; updateName(login.name); log = true; let friends = login.friends; updateFriends(friends); upd = setInterval(() => { if (log) updateRoom() }, 10000); } async function updateRoom() { if (!log) return; let players = Array.from(document.querySelector("#gamePlayersList").children).length; if (!players) room = "" let login = await (await fetch(url, { method: 'POST', body: JSON.stringify({ "type": "update", "ping": Date.now(), "id": id, "players": players, "room": room, "name": name || "no name" }), headers: { 'Content-Type': 'application/json' } })).json(); updateName(login.name); updateFriends(login.friends); } async function updateName(n) { if (n != name) { name = n; document.querySelector(".fname input").value = n; setItem("name", n) } } async function logout() { clearInterval(upd); log = false; window.localStorage.setItem("id", JSON.stringify({})) tb.innerHTML = logged let logout = await (await fetch(url, { method: 'POST', body: JSON.stringify({ "type": "logout", "id": id }), headers: { 'Content-Type': 'application/json' } })).json(); id = "" await bind(); } async function add(num, t) { let add = await (await fetch(url, { method: 'POST', body: JSON.stringify({ "type": ((t == "add") ? "add" : "remove"), "id": id, "added": num }), headers: { 'Content-Type': 'application/json' } })).json(); if (add.error) alert(add.error) if (!add.error && t=="add") alert("Added!") await updateRoom(); } async function sortTable(n) { let tb = document.getElementById("tb"); Array.from(tb.rows).sort((a, b) => { let st = a.firstElementChild.firstElementChild.innerHTML let st2 = b.firstElementChild.firstElementChild.innerHTML if (st.charCodeAt(0) > st2.charCodeAt(0)) { return -1; } else if (st.charCodeAt(0) < st2.charCodeAt(0)) { return 1; } return 0; }).sort((a, b) => { if (a.firstElementChild.firstElementChild.innerHTML == "⚫") return 0; let st = a.children[1].firstElementChild.innerHTML let st2 = b.children[1].firstElementChild.innerHTML if (st.charCodeAt(0) < st2.charCodeAt(0)) { return -1; } else if (st.charCodeAt(0) > st2.charCodeAt(0)) { return 1; } return 0; }).sort((a, b) => { if (a.firstElementChild.firstElementChild.innerHTML == "🟢") return 1; let st = a.children[1].firstElementChild.innerHTML let st2 = b.children[1].firstElementChild.innerHTML if (st.charCodeAt(0) < st2.charCodeAt(0)) { return 0; } else { return -1; } return 0; }).forEach((tr, i) => { if (i == 0) { tb.innerHTML = "" } tb.appendChild(tr) }) } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址