您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Easily manage your project and studio ads with a quick access button on all Scratch pages except ad pages. Made by VIGARPAST_777.
// ==UserScript== // @name VIGARPAST_777 Scratch Ad Manager with Quick Button // @namespace https://scratch.mit.edu/ // @version 2.9 // @description Easily manage your project and studio ads with a quick access button on all Scratch pages except ad pages. Made by VIGARPAST_777. // @match https://scratch.mit.edu/* // @grant GM_xmlhttpRequest // @connect scratchadvertise-f787.restdb.io // ==/UserScript== (async function() { 'use strict'; const excludedPaths = ["/advertise", "/advertise-projects", "/advertise-studios"]; const path = window.location.pathname; if (!excludedPaths.includes(path)) { const btn = document.createElement("a"); btn.textContent = "Advertise Here"; btn.href = "/advertise"; btn.style.position = "fixed"; btn.style.top = "10px"; btn.style.left = "10px"; btn.style.background = "#007bff"; btn.style.color = "white"; btn.style.padding = "8px 12px"; btn.style.borderRadius = "6px"; btn.style.fontWeight = "bold"; btn.style.textDecoration = "none"; btn.style.zIndex = "9999"; btn.style.boxShadow = "0 2px 6px rgba(0,0,0,0.2)"; btn.style.cursor = "pointer"; document.body.appendChild(btn); } const API_SCRATCH_USER_PROJECTS = "https://api.scratch.mit.edu/users/VIGARPAST_777/projects"; const encryptedKey = [ 99,100,54,51,97,97,101,55,102,48,56,53,53,55,98,52, 100,102,98,48,50,51,53,52,99,97,51,48,48,101,57,97, 99,102,102,48,100 ]; const API_KEY = encryptedKey.map(c => String.fromCharCode(c)).join(''); const API_PROJECTS_URL = "https://scratchadvertise-f787.restdb.io/rest/projects"; const API_STUDIOS_URL = "https://scratchadvertise-f787.restdb.io/rest/studios"; const HEADERS = { "x-apikey": API_KEY, "Content-Type": "application/json" }; async function addCredits() { const topBar = document.createElement("div"); topBar.style.textAlign = "center"; topBar.style.padding = "10px 0"; topBar.style.fontWeight = "bold"; topBar.style.fontSize = "18px"; topBar.innerHTML = ` Made By <a href="https://scratch.mit.edu/users/VIGARPAST_777/" target="_blank" style="color:#007bff;">VIGARPAST_777</a> `; const profileDiv = document.createElement("div"); profileDiv.style.textAlign = "center"; profileDiv.style.padding = "10px 0"; const img = document.createElement("img"); img.src = "https://uploads.scratch.mit.edu/users/avatars/69475709.png"; img.style.width = "80px"; img.style.height = "80px"; img.style.borderRadius = "50%"; img.style.boxShadow = "0 0 8px rgba(0,0,0,0.2)"; img.alt = "Profile picture"; const followText = document.createElement("div"); followText.textContent = "Don’t forget to follow me!"; followText.style.marginTop = "8px"; followText.style.fontSize = "16px"; profileDiv.appendChild(img); profileDiv.appendChild(followText); const projectsDiv = document.createElement("div"); projectsDiv.style.maxWidth = "900px"; projectsDiv.style.margin = "10px auto"; projectsDiv.style.textAlign = "left"; topBar.appendChild(profileDiv); topBar.appendChild(projectsDiv); document.body.prepend(topBar); try { const resp = await fetch(API_SCRATCH_USER_PROJECTS); if (!resp.ok) throw "Failed to fetch projects"; const projects = await resp.json(); const lastFive = projects .filter(p => p.public === true) .sort((a,b) => new Date(b.history.modified) - new Date(a.history.modified)) .slice(0,3); const projHtml = lastFive.map(p => { return ` <div style="display:flex; align-items:center; gap:10px; margin-bottom:8px;"> <a href="https://scratch.mit.edu/projects/${p.id}" target="_blank" style="display:flex; align-items:center; text-decoration:none; color:#000;"> <img src="https://uploads.scratch.mit.edu/projects/thumbnails/${p.id}.png" alt="${p.title}" style="float:left; width:80px; height:60px; object-fit:cover; border-radius:6px; box-shadow:0 0 4px rgba(0,0,0,0.15);"> <span style="margin-left:10px; font-weight:600;">${p.title}</span> </a> </div> `; }).join(""); projectsDiv.innerHTML = `<h3>My Latest Projects</h3>` + projHtml; } catch(e) { projectsDiv.innerHTML = "<em>Could not load projects.</em>"; } } if (excludedPaths.includes(path)) { if (path === "/advertise") { if (!sessionStorage.getItem("adScriptDisclaimerShown")) { setTimeout(() => { alert("This script is not affiliated with Scratch. Please be sure to follow all community rules and not use this tool for spam or mass promotion."); }, 0); sessionStorage.setItem("adScriptDisclaimerShown", "true"); } document.body.innerHTML = ` <style> body { font-family: sans-serif; background:#f5f5f5; height:100vh; margin:0; display:flex; justify-content:center; align-items:center; flex-direction:column; } #box { background:white; padding:30px; border-radius:8px; text-align:center; box-shadow:0 4px 8px rgba(0,0,0,0.1); } select, button { font-size:18px; padding:10px; margin: 10px 0; } a.button { text-decoration: none; background: #ccc; padding: 10px 20px; border-radius: 6px; display: inline-block; } </style> <div id="box"> <h2>What would you like to advertise?</h2> <select id="sel"> <option disabled selected>Select...</option> <option value="projects">🚀 Projects</option> <option value="studios">🎬 Studios</option> </select><br> <a href="https://scratch.mit.edu" class="button">← Back to Scratch</a> </div> `; document.getElementById("sel").addEventListener("change", e => { window.location.href = e.target.value === "projects" ? "/advertise-projects" : "/advertise-studios"; }); await addCredits(); return; } const isProjects = path === "/advertise-projects"; const apiURL = isProjects ? API_PROJECTS_URL : API_STUDIOS_URL; const label = isProjects ? "Project" : "Studio"; document.body.innerHTML = ` <style> body { font-family: sans-serif; background:#f0f0f0; margin:20px; } .box { background:white; padding:20px; border-radius:8px; max-width:900px; margin:auto; box-shadow:0 3px 6px rgba(0,0,0,0.1); } input, button { width:100%; padding:12px; margin-top:10px; font-size:16px; border-radius:6px; border:1px solid #ccc; } button { background:#007bff; color:white; border:none; cursor:pointer; } button:disabled { background:#aaa; cursor:default } .item { display:flex; gap:12px; margin-top:15px; padding:10px; background:#fafafa; border-radius:6px; align-items: flex-start; word-wrap: break-word; } .item img { width:100px; height:75px; object-fit:cover; border-radius:4px; flex-shrink: 0; } .item .info { flex: 1; overflow-wrap: break-word; word-break: break-word; font-size: 14px; line-height: 1.4; } .top-links { display: flex; justify-content: space-between; margin-bottom: 10px; } .top-links a { background: #ddd; padding: 8px 16px; text-decoration: none; border-radius: 5px; font-size: 14px; } </style> <div class="box"> <div class="top-links"> <a href="/advertise">← Change type</a> <a href="https://scratch.mit.edu">← Back to Scratch</a> </div> <h2>Submit a ${label}</h2> <input type="text" id="inpId" placeholder="Enter ${label} ID"> <button id="btn">Submit</button> <div id="list"><em>Loading ${label.toLowerCase()}s...</em></div> </div> `; function req(method, url, data) { return new Promise((res, rej) => { GM_xmlhttpRequest({ method, url, headers: HEADERS, data: data ? JSON.stringify(data) : null, onload: r => r.status >= 200 && r.status < 300 ? res(r.responseText) : rej(r), onerror: rej }); }); } async function loadList() { const txt = await req("GET", apiURL); const arr = JSON.parse(txt).sort((a,b)=> new Date(b.date)-new Date(a.date)); const html = arr.map(o => { const id = isProjects ? o.projectId : o.studioId; const usr = isProjects ? o.author : (o.curator || "unknown"); const image = isProjects ? `https://uploads.scratch.mit.edu/projects/thumbnails/${id}.png` : `https://cdn2.scratch.mit.edu/get_image/gallery/${id}_170x100.png`; return ` <div class="item"> <img src="${image}" onerror="this.src='https://upload.wikimedia.org/wikipedia/commons/6/65/No-Image-Placeholder.svg'"> <div class="info"> <strong>${o.title}</strong><br> 👤 ${usr}<br> 📅 ${new Date(o.date).toLocaleString()}<br> <a href="https://scratch.mit.edu/${isProjects ? 'projects' : 'studios'}/${id}" target="_blank">View on Scratch</a> </div> </div>`; }).join(""); document.getElementById("list").innerHTML = ` <h3>Total ${label}s: ${arr.length}</h3> ${html || `<em>No ${label.toLowerCase()}s yet.</em>`} `; } document.getElementById("btn").addEventListener("click", async ()=>{ const val = document.getElementById("inpId").value.trim(); if (!/^\d+$/.test(val)) { alert("Invalid ID."); return; } document.getElementById("btn").disabled = true; try { const apiEnt = isProjects ? `projects/${val}` : `studios/${val}`; const resp = await fetch(`https://api.scratch.mit.edu/${apiEnt}`); if (!resp.ok) throw "Invalid ID"; const dat = await resp.json(); const existTxt = await req("GET", apiURL); const existArr = JSON.parse(existTxt); const exists = existArr.find(e => isProjects ? e.projectId == val : e.studioId == val ); if (exists) { await req("PATCH", `${apiURL}/${exists._id}`, {date: new Date().toISOString()}); alert("Date updated."); } else { const payload = isProjects ? { projectId: dat.id, title: dat.title, author: dat.author.username, date: new Date().toISOString() } : { studioId: dat.id, title: dat.title, curator: dat.curator?.username || "unknown", date: new Date().toISOString() }; await req("POST", apiURL, payload); alert("Saved."); } document.getElementById("inpId").value = ""; loadList(); } catch(e) { alert(e); } finally { document.getElementById("btn").disabled = false; } }); await loadList(); await addCredits(); return; } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址