您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Download Roblox thumbnails, game icons, badge icons, and user info
当前为
// ==UserScript== // @name Roblox Multi-Feature User Panel. // @namespace http://tampermonkey.net/ // @version 0.4 // @description Download Roblox thumbnails, game icons, badge icons, and user info // @author NotRoblox // @match https://www.roblox.com/userpanel // @match https://www.roblox.com/getgameinfo // @match https://www.roblox.com/getbadgeinfo // @match https://www.roblox.com/getuserinfo // @match https://www.roblox.com/getgroupinfo // @grant GM_xmlhttpRequest // @grant Gm_download // @license MIT // ==/UserScript== (function() { 'use strict'; const style = document.createElement('style'); style.textContent = ` body { font-family: Arial, sans-serif; background-color: #f4f7f6; margin: 0; padding: 0; } .main-content-wrapper { width: 100%; padding: 20px; margin-bottom: 120px; display: flex; flex-direction: column; align-items: center; min-height: calc(100vh - 200px); } .form-container { background-color: #ffffff; padding: 20px; border-radius: 8px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); width: 100%; max-width: 400px; text-align: center; margin: 20px auto; position: relative; z-index: 1; } .input-field { width: 100%; padding: 10px; margin: 10px 0; border: 2px solid #ddd; border-radius: 4px; font-size: 16px; } .submit-button, .panel-button { background-color: #4CAF50; color: white; padding: 12px 20px; border: none; border-radius: 4px; cursor: pointer; width: 100%; font-size: 16px; margin: 10px 0; } .submit-button:hover, .panel-button:hover { background-color: #45a049; } .result-container { background-color: #ffffff; padding: 20px; border-radius: 8px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); width: 100%; max-width: 800px; margin: 20px auto 120px auto; position: relative; z-index: 1; } .image-container { display: flex; flex-wrap: wrap; gap: 20px; justify-content: center; margin: 20px 0; } .image-item { text-align: center; } .image-item img { max-width: 200px; border-radius: 8px; margin-bottom: 10px; } .info-text { margin: 10px 0; font-size: 16px; } .error-message { color: #ff0000; margin: 10px 0; } .success-message { color: #4CAF50; margin: 10px 0; } `; document.head.appendChild(style); async function getUserIdFromUsername(username) { const response = await fetch(`https://users.roblox.com/v1/usernames/users`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ usernames: [username] }) }); const data = await response.json(); if (!data.data || data.data.length === 0) throw new Error('User not found'); return data.data[0].id; } function createBasicForm(placeholder, buttonText) { const container = document.createElement('div'); container.className = 'form-container'; const input = document.createElement('input'); input.type = 'text'; input.className = 'input-field'; input.placeholder = placeholder; const button = document.createElement('button'); button.className = 'submit-button'; button.textContent = buttonText; container.appendChild(input); container.appendChild(button); return { container, input, button }; } function displayMessage(message, isError = false) { const messageDiv = document.createElement('div'); messageDiv.className = isError ? 'error-message' : 'success-message'; messageDiv.textContent = message; document.querySelector('.form-container').appendChild(messageDiv); setTimeout(() => messageDiv.remove(), 5000); } function createResultContainer() { const container = document.createElement('div'); container.className = 'result-container'; return container; } async function initializeGameInfo() { const mainWrapper = document.createElement('div'); mainWrapper.className = 'main-content-wrapper'; document.body.appendChild(mainWrapper); const { container, input, button } = createBasicForm('Enter Game ID', 'Get Game Info'); mainWrapper.appendChild(container); const refreshContent = async (gameId) => { const existingResults = mainWrapper.querySelectorAll('.result-container'); existingResults.forEach(result => result.remove()); try { // Get the universe ID first const placeResponse = await fetch(`https://apis.roblox.com/universes/v1/places/${gameId}/universe`); const placeData = await placeResponse.json(); const universeId = placeData.universeId; // Now fetch all data with the universe ID const [gameResponse, thumbnailResponse, iconResponse] = await Promise.all([ fetch(`https://games.roblox.com/v1/games?universeIds=${universeId}`), fetch(`https://thumbnails.roblox.com/v1/games/${universeId}/thumbnails?size=768x432&format=Png&limit=10`), fetch(`https://thumbnails.roblox.com/v1/games/icons?universeIds=${universeId}&size=512x512&format=Png&isCircular=false`) ]); const [gameData, thumbnailData, iconData] = await Promise.all([ gameResponse.json(), thumbnailResponse.json(), iconResponse.json() ]); const resultContainer = createResultContainer(); // Create image container for all images const imageContainer = document.createElement('div'); imageContainer.className = 'image-container'; // Display game icon first if (iconData.data && iconData.data[0]) { const iconDiv = document.createElement('div'); iconDiv.className = 'image-item'; const iconImg = document.createElement('img'); iconImg.src = iconData.data[0].imageUrl; iconImg.alt = 'Game Icon'; const downloadIconBtn = document.createElement('button'); downloadIconBtn.className = 'submit-button'; downloadIconBtn.textContent = 'Download Icon'; downloadIconBtn.onclick = () => GM_download({ url: iconData.data[0].imageUrl, name: `game_${gameId}_icon.png` }); iconDiv.appendChild(iconImg); iconDiv.appendChild(downloadIconBtn); imageContainer.appendChild(iconDiv); } // Display game information if (gameData.data && gameData.data[0]) { const gameInfo = document.createElement('div'); gameInfo.className = 'info-text'; gameInfo.innerHTML = ` <h3>${gameData.data[0].name}</h3> <p>Description: ${gameData.data[0].description || 'No description'}</p> <p>Created: ${new Date(gameData.data[0].created).toLocaleDateString()}</p> <p>Updated: ${new Date(gameData.data[0].updated).toLocaleDateString()}</p> <p>Playing: ${gameData.data[0].playing || 0}</p> <p>Visits: ${gameData.data[0].visits || 0}</p> <p><a href="https://www.roblox.com/games/${gameId}" target="_blank">View Game Page</a></p> `; resultContainer.appendChild(gameInfo); } // Display all thumbnails if (thumbnailData.data) { thumbnailData.data.forEach((thumb, index) => { const thumbDiv = document.createElement('div'); thumbDiv.className = 'image-item'; const thumbImg = document.createElement('img'); thumbImg.src = thumb.imageUrl; thumbImg.alt = `Game Thumbnail ${index + 1}`; const downloadThumbBtn = document.createElement('button'); downloadThumbBtn.className = 'submit-button'; downloadThumbBtn.textContent = `Download Thumbnail ${index + 1}`; downloadThumbBtn.onclick = () => GM_download({ url: thumb.imageUrl, name: `game_${gameId}_thumbnail_${index + 1}.png` }); thumbDiv.appendChild(thumbImg); thumbDiv.appendChild(downloadThumbBtn); imageContainer.appendChild(thumbDiv); }); } resultContainer.appendChild(imageContainer); mainWrapper.appendChild(resultContainer); displayMessage('Game information fetched successfully!'); } catch (error) { displayMessage(error.message, true); } }; button.onclick = async () => { const gameId = input.value.trim(); if (!gameId) { displayMessage('Please enter a game ID', true); return; } await refreshContent(gameId); }; } async function initializeBadgeInfo() { const mainWrapper = document.createElement('div'); mainWrapper.className = 'main-content-wrapper'; document.body.appendChild(mainWrapper); const { container, input, button } = createBasicForm('Enter Badge ID', 'Get Badge Info'); mainWrapper.appendChild(container); const refreshContent = async (badgeId) => { // Remove any existing result containers const existingResults = mainWrapper.querySelectorAll('.result-container'); existingResults.forEach(result => result.remove()); try { // Fetch badge info const infoResponse = await fetch(`https://badges.roblox.com/v1/badges/${badgeId}`); const badgeInfo = await infoResponse.json(); // Fetch badge icon const iconResponse = await fetch(`https://thumbnails.roblox.com/v1/badges/icons?badgeIds=${badgeId}&size=150x150&format=Png`); const iconData = await iconResponse.json(); const resultContainer = createResultContainer(); // Create image container first const imageContainer = document.createElement('div'); imageContainer.className = 'image-container'; // Display badge icon if (iconData.data && iconData.data[0]) { const iconDiv = document.createElement('div'); iconDiv.className = 'image-item'; const iconImg = document.createElement('img'); iconImg.src = iconData.data[0].imageUrl; iconImg.alt = 'Badge Icon'; const downloadBtn = document.createElement('button'); downloadBtn.className = 'submit-button'; downloadBtn.textContent = 'Download Badge Icon'; downloadBtn.onclick = () => GM_download({ url: iconData.data[0].imageUrl, name: `badge_${badgeId}.png` }); iconDiv.appendChild(iconImg); iconDiv.appendChild(downloadBtn); imageContainer.appendChild(iconDiv); } // Display badge information const infoDiv = document.createElement('div'); infoDiv.className = 'info-text'; infoDiv.innerHTML = ` <h3>${badgeInfo.name}</h3> <p>${badgeInfo.description}</p> <p>Enabled: ${badgeInfo.enabled}</p> <p>Statistics:</p> <p>- Created: ${new Date(badgeInfo.created).toLocaleDateString()}</p> <p>- Updated: ${new Date(badgeInfo.updated).toLocaleDateString()}</p> `; resultContainer.appendChild(imageContainer); resultContainer.appendChild(infoDiv); mainWrapper.appendChild(resultContainer); displayMessage('Badge information fetched successfully!'); } catch (error) { displayMessage(error.message, true); } }; button.onclick = async () => { const badgeId = input.value.trim(); if (!badgeId) { displayMessage('Please enter a badge ID', true); return; } await refreshContent(badgeId); }; } async function initializeUserInfo() { const mainWrapper = document.createElement('div'); mainWrapper.className = 'main-content-wrapper'; document.body.appendChild(mainWrapper); const { container, input, button } = createBasicForm('Enter Username', 'Get User Info'); mainWrapper.appendChild(container); // Create a result container to hold the user info, initially hidden const resultContainer = createResultContainer(); resultContainer.style.display = 'none'; // Hide initially mainWrapper.appendChild(resultContainer); button.onclick = async () => { try { const username = input.value.trim(); if (!username) throw new Error('Please enter a username'); const userId = await getUserIdFromUsername(username); // Fetch all data in parallel const [ presenceResponse, friendsResponse, followersResponse, thumbnailResponse, bustResponse, headshotResponse ] = await Promise.all([ fetch(`https://presence.roblox.com/v1/presence/users`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ userIds: [userId] }) }), fetch(`https://friends.roblox.com/v1/users/${userId}/friends/count`), fetch(`https://friends.roblox.com/v1/users/${userId}/followers/count`), fetch(`https://thumbnails.roblox.com/v1/users/avatar?userIds=${userId}&size=420x420&format=Png`), fetch(`https://thumbnails.roblox.com/v1/users/avatar-bust?userIds=${userId}&size=420x420&format=Png`), fetch(`https://thumbnails.roblox.com/v1/users/avatar-headshot?userIds=${userId}&size=420x420&format=Png`) ]); const [presence, friends, followers, thumbnail, bust, headshot] = await Promise.all([ presenceResponse.json(), friendsResponse.json(), followersResponse.json(), thumbnailResponse.json(), bustResponse.json(), headshotResponse.json() ]); // Clear previous content in the result container resultContainer.innerHTML = ''; // Create thumbnails section const imageContainer = document.createElement('div'); imageContainer.className = 'image-container'; // Helper function to create image sections const createImageSection = (data, type) => { if (data.data && data.data[0]) { const div = document.createElement('div'); div.className = 'image-item'; const img = document.createElement('img'); img.src = data.data[0].imageUrl; img.alt = `${type} thumbnail`; const downloadBtn = document.createElement('button'); downloadBtn.className = 'submit-button'; downloadBtn.textContent = `Download ${type}`; downloadBtn.onclick = () => GM_download({ url: data.data[0].imageUrl, name: `${username}_${type}.png` }); div.appendChild(img); div.appendChild(downloadBtn); imageContainer.appendChild(div); } }; // Add all thumbnails createImageSection(thumbnail, 'Full Avatar'); createImageSection(bust, 'Bust'); createImageSection(headshot, 'Headshot'); // Create user info section const userInfo = document.createElement('div'); userInfo.className = 'info-text'; const userPresence = presence.userPresences[0]; userInfo.innerHTML = ` <h3>User Information for ${username}</h3> <p>User ID: ${userId}</p> <p>Online Status: ${userPresence.userPresenceType === 0 ? 'Offline' : 'Online'}</p> <p>Friends Count: ${friends.count}</p> <p>Followers Count: ${followers.count}</p> <p>Profile Link: <a href="https://www.roblox.com/users/${userId}/profile" target="_blank">View Profile</a></p> ${userPresence.userPresenceType !== 0 ? `<p>Last Location: ${userPresence.lastLocation}</p>` : ''} `; // Append the new content to the result container resultContainer.appendChild(imageContainer); resultContainer.appendChild(userInfo); resultContainer.style.display = 'block'; // Show the result container displayMessage('User information fetched successfully!'); } catch (error) { displayMessage(error.message, true); } }; } // Add this new function: async function initializeGroupInfo() { const mainWrapper = document.createElement('div'); mainWrapper.className = 'main-content-wrapper'; document.body.appendChild(mainWrapper); const { container, input, button } = createBasicForm('Enter Group ID', 'Get Group Info'); mainWrapper.appendChild(container); const refreshContent = async (groupId) => { // Remove any existing result containers const existingResults = mainWrapper.querySelectorAll('.result-container'); existingResults.forEach(result => result.remove()); try { // Fetch all group data in parallel const [ groupResponse, membersResponse, iconResponse, rolesResponse ] = await Promise.all([ fetch(`https://groups.roblox.com/v1/groups/${groupId}`), fetch(`https://groups.roblox.com/v1/groups/${groupId}/membership`), fetch(`https://thumbnails.roblox.com/v1/groups/icons?groupIds=${groupId}&size=420x420&format=Png`), fetch(`https://groups.roblox.com/v1/groups/${groupId}/roles`) ]); const [groupInfo, membersInfo, iconData, rolesInfo] = await Promise.all([ groupResponse.json(), membersResponse.json(), iconResponse.json(), rolesResponse.json() ]); const resultContainer = createResultContainer(); // Create image container for group icon const imageContainer = document.createElement('div'); imageContainer.className = 'image-container'; // Display group icon if (iconData.data && iconData.data[0]) { const iconDiv = document.createElement('div'); iconDiv.className = 'image-item'; const iconImg = document.createElement('img'); iconImg.src = iconData.data[0].imageUrl; iconImg.alt = 'Group Icon'; const downloadBtn = document.createElement('button'); downloadBtn.className = 'submit-button'; downloadBtn.textContent = 'Download Group Icon'; downloadBtn.onclick = () => GM_download({ url: iconData.data[0].imageUrl, name: `group_${groupId}_icon.png` }); iconDiv.appendChild(iconImg); iconDiv.appendChild(downloadBtn); imageContainer.appendChild(iconDiv); } // Display group information const infoDiv = document.createElement('div'); infoDiv.className = 'info-text'; infoDiv.innerHTML = ` <h3>${groupInfo.name}</h3> <p>Description: ${groupInfo.description || 'No description'}</p> <p>Owner: ${groupInfo.owner ? groupInfo.owner.username : 'No owner'}</p> <p>Member Count: ${membersInfo.memberCount || 0}</p> <p>Created: ${new Date(groupInfo.created).toLocaleDateString()}</p> <p>Public Entry: ${groupInfo.publicEntryAllowed ? 'Yes' : 'No'}</p> <p><a href="https://www.roblox.com/groups/${groupId}" target="_blank">View Group Page</a></p> <h4>Roles:</h4> <ul> ${rolesInfo.roles.map(role => ` <li>${role.name} (${role.memberCount} members)</li> `).join('')} </ul> `; resultContainer.appendChild(imageContainer); resultContainer.appendChild(infoDiv); mainWrapper.appendChild(resultContainer); displayMessage('Group information fetched successfully!'); } catch (error) { displayMessage(error.message, true); } }; button.onclick = async () => { const groupId = input.value.trim(); if (!groupId) { displayMessage('Please enter a group ID', true); return; } await refreshContent(groupId); }; } // Panel Implementation function createPanel() { const mainWrapper = document.createElement('div'); mainWrapper.className = 'main-content-wrapper'; document.body.appendChild(mainWrapper); const container = document.createElement('div'); container.className = 'form-container'; const title = document.createElement('h2'); title.textContent = 'Roblox Multi-Feature Tool'; container.appendChild(title); const buttons = [ { text: 'Game Information', url: '/getgameinfo' }, { text: 'Badge Information', url: '/getbadgeinfo' }, { text: 'User Information', url: '/getuserinfo' }, { text: 'Group Information', url: '/getgroupinfo' } ]; buttons.forEach(button => { const btn = document.createElement('button'); btn.className = 'panel-button'; btn.textContent = button.text; btn.onclick = () => window.location.href = 'https://www.roblox.com' + button.url; container.appendChild(btn); }); mainWrapper.appendChild(container); } // Initialize based on current page const currentPath = window.location.pathname; switch(currentPath) { case '/userpanel': createPanel(); break; case '/getgameinfo': initializeGameInfo(); break; case '/getbadgeinfo': initializeBadgeInfo(); break; case '/getuserinfo': initializeUserInfo(); break; case '/getgroupinfo': initializeGroupInfo(); break; } })();