// ==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;
}
})();