您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Download Roblox thumbnails, game info, badge info, and user info and the images for all those!
当前为
- // ==UserScript==
- // @name Roblox Multi-Feature User Panel.
- // @namespace http://tampermonkey.net/
- // @version 0.6
- // @description Download Roblox thumbnails, game info, badge info, and user info and the images for all those!
- // @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_cookie
- // @connect promocodes.roblox.com
- // @connect auth.roblox.com
- // @connect www.roblox.com
- // @match https://www.roblox.com/*
- // @match https://promocodes.roblox.com/*
- // @match https://auth.roblox.com/*
- // @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;
- // Fetch all required data
- const [gameResponse, iconResponse, thumbnailResponse, votesResponse, favoritesResponse] = await Promise.all([
- fetch(`https://games.roblox.com/v1/games?universeIds=${universeId}`),
- fetch(`https://thumbnails.roblox.com/v1/games/icons?universeIds=${universeId}&size=512x512&format=Png&isCircular=false`),
- fetch(`https://thumbnails.roblox.com/v1/games/${universeId}/thumbnails?size=768x432&format=Png&limit=10`),
- fetch(`https://games.roblox.com/v1/games/${universeId}/votes`),
- fetch(`https://games.roblox.com/v1/games/${universeId}/favorites/count`),
- ]);
- const [gameData, iconData, thumbnailData, votesData, favoritesData] = await Promise.all([
- gameResponse.json(),
- iconResponse.json(),
- thumbnailResponse.json(),
- votesResponse.json(),
- favoritesResponse.json()
- ]);
- const resultContainer = createResultContainer();
- // Create image container for all images
- const imageContainer = document.createElement('div');
- imageContainer.className = 'image-container';
- // Display game 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 = 'Game Icon';
- iconImg.className = '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 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}`;
- thumbImg.className = 'thumbnail-image';
- 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);
- });
- }
- // Display game information
- if (gameData.data && gameData.data[0]) {
- const game = gameData.data[0];
- const likes = votesData.upVotes || 0;
- const dislikes = votesData.downVotes || 0;
- const totalVotes = likes + dislikes;
- const likeRatio = totalVotes > 0 ? ((likes / totalVotes) * 100).toFixed(1) : 0;
- resultContainer.appendChild(imageContainer);
- const gameInfo = document.createElement('div');
- gameInfo.className = 'info-text';
- gameInfo.innerHTML = `
- <h2>${game.name}</h2>
- <div class="game-stats">
- <div class="stat-item">
- <h4>👥 Player Stats</h4>
- <p>Current Players: ${game.playing?.toLocaleString() || 0}</p>
- <p>Total Visits: ${game.visits?.toLocaleString() || 0}</p>
- <p>Max Players: ${game.maxPlayers || 'Unknown'}</p>
- </div>
- <div class="stat-item">
- <h4>👍 Ratings</h4>
- <p>Likes: ${likes.toLocaleString()}</p>
- <p>Dislikes: ${dislikes.toLocaleString()}</p>
- <p>Like Ratio: ${likeRatio}%</p>
- <p>Favorites: ${favoritesData.favoritesCount?.toLocaleString() || 0}</p>
- </div>
- <div class="stat-item">
- <h4>ℹ️ Details</h4>
- <p>Created: ${new Date(game.created).toLocaleDateString()}</p>
- <p>Last Updated: ${new Date(game.updated).toLocaleDateString()}</p>
- <p>Genre: ${game.genre || 'Not specified'}</p>
- <p>Allowed Gear Types: ${game.allowedGearTypes?.join(', ') || 'None'}</p>
- </div>
- </div>
- <div class="game-description">
- <h4>📝 Description</h4>
- <p>${game.description || 'No description available'}</p>
- </div>
- <p class="game-link"><a href="https://www.roblox.com/games/${gameId}" target="_blank">🎮 View Game Page</a></p>
- `;
- resultContainer.appendChild(gameInfo);
- }
- mainWrapper.appendChild(resultContainer);
- } catch (error) {
- console.error(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 with proper error handling
- const infoResponse = await fetch(`https://badges.roblox.com/v1/badges/${badgeId}`, {
- method: 'GET',
- credentials: 'include'
- });
- if (!infoResponse.ok) {
- throw new Error('Failed to fetch badge information');
- }
- const badgeInfo = await infoResponse.json();
- // Fetch badge statistics
- const statsResponse = await fetch(`https://badges.roblox.com/v1/badges/${badgeId}/statistics`, {
- method: 'GET',
- credentials: 'include'
- });
- const statsData = await statsResponse.json();
- // Fetch badge icon
- const iconResponse = await fetch(`https://thumbnails.roblox.com/v1/badges/icons?badgeIds=${badgeId}&size=150x150&format=Png`, {
- method: 'GET',
- credentials: 'include'
- });
- const iconData = await iconResponse.json();
- const resultContainer = createResultContainer();
- // Create image container
- const imageContainer = document.createElement('div');
- imageContainer.className = 'image-container';
- // Display badge icon if available
- 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';
- iconImg.style.maxWidth = '150px';
- 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 || 'Unknown Badge'}</h3>
- <div style="background: #f5f5f5; padding: 15px; border-radius: 8px; margin: 10px 0;">
- <h4>Badge Details</h4>
- <p><strong>Description:</strong> ${badgeInfo.description || 'No description available'}</p>
- <p><strong>Enabled:</strong> ${badgeInfo.enabled ? '✅ Yes' : '❌ No'}</p>
- <p><strong>Created:</strong> ${new Date(badgeInfo.created).toLocaleDateString()}</p>
- <p><strong>Updated:</strong> ${new Date(badgeInfo.updated).toLocaleDateString()}</p>
- </div>
- <div style="background: #f5f5f5; padding: 15px; border-radius: 8px; margin: 10px 0;">
- <h4>Statistics</h4>
- <p><strong>Win Rate:</strong> ${statsData.winRatePercentage?.toFixed(2) || 0}%</p>
- <p><strong>Awarded:</strong> ${statsData.awardedCount?.toLocaleString() || 0} times</p>
- </div>
- <div style="background: #f5f5f5; padding: 15px; border-radius: 8px; margin: 10px 0;">
- <h4>Links</h4>
- <p><a href="https://www.roblox.com/badges/${badgeId}" target="_blank">View Badge Page</a></p>
- ${badgeInfo.awardingUniverse ?
- `<p><a href="https://www.roblox.com/games/${badgeInfo.awardingUniverse.id}" target="_blank">View Game Page</a></p>`
- : ''}
- </div>
- `;
- resultContainer.appendChild(imageContainer);
- resultContainer.appendChild(infoDiv);
- mainWrapper.appendChild(resultContainer);
- displayMessage('Badge information fetched successfully!');
- } catch (error) {
- const resultContainer = createResultContainer();
- resultContainer.innerHTML = `
- <div class="error-message" style="padding: 15px; margin-top: 20px; border-radius: 4px;">
- <h3>❌ Error</h3>
- <p>Failed to fetch badge information: ${error.message}</p>
- <p>Please make sure the badge ID is valid and try again.</p>
- </div>
- `;
- mainWrapper.appendChild(resultContainer);
- 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);
- const resultContainer = createResultContainer();
- resultContainer.style.display = 'none';
- 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);
- const [
- userInfoResponse,
- presenceResponse,
- friendsResponse,
- followersResponse,
- thumbnailResponse,
- bustResponse,
- headshotResponse
- ] = await Promise.all([
- fetch(`https://users.roblox.com/v1/users/${userId}`),
- 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 [userInfo, presence, friends, followers, thumbnail, bust, headshot] = await Promise.all([
- userInfoResponse.json(),
- presenceResponse.json(),
- friendsResponse.json(),
- followersResponse.json(),
- thumbnailResponse.json(),
- bustResponse.json(),
- headshotResponse.json()
- ]);
- resultContainer.innerHTML = '';
- // Create thumbnails section
- const imageContainer = document.createElement('div');
- imageContainer.className = 'image-container';
- 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);
- }
- };
- createImageSection(thumbnail, 'Full Avatar');
- createImageSection(bust, 'Bust');
- createImageSection(headshot, 'Headshot');
- // Create user info section
- const userInfoDiv = document.createElement('div');
- userInfoDiv.className = 'info-text';
- const userPresence = presence.userPresences[0];
- userInfoDiv.innerHTML = `
- <h3>User Information for ${userInfo.displayName} (${userInfo.name})</h3>
- <p>User ID: ${userId}</p>
- <p>Display Name: ${userInfo.displayName}</p>
- <p>Username: ${userInfo.name}</p>
- <p>Description: ${userInfo.description ? userInfo.description : 'No description available'}</p>
- <p>Account Age: ${userInfo.age} days</p>
- <p>Join Date: ${new Date(userInfo.created).toLocaleDateString()}</p>
- <p>Online Status: ${userPresence.userPresenceType === 0 ? 'Offline' : userPresence.userPresenceType === 1 ? 'Online' : 'Playing'}</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>` : ''}
- <p>
- <a href="https://www.roblox.com/abusereport/userprofile?id=${userId}" target="_blank">Report User</a> |
- <a href="https://www.roblox.com/illegal-content-reporting" target="_blank">Report to DMCA</a>
- </p>
- `;
- // Create container for dynamic content
- const dynamicContentDiv = document.createElement('div');
- dynamicContentDiv.id = 'dynamic-content';
- // Create buttons container
- const buttonsDiv = document.createElement('div');
- buttonsDiv.className = 'buttons-container';
- // Username History Button
- const historyButton = document.createElement('button');
- historyButton.className = 'submit-button';
- historyButton.textContent = 'Show Username History';
- historyButton.onclick = async () => {
- try {
- const historyResponse = await fetch(`https://users.roblox.com/v1/users/${userId}/username-history`);
- const historyData = await historyResponse.json();
- const historyList = historyData.data.map((entry) => `<li>${entry.name}</li>`).join('');
- dynamicContentDiv.innerHTML = `<h4>Username History:</h4><ul>${historyList || '<li>No username changes found</li>'}</ul>`;
- } catch (error) {
- displayMessage('Failed to fetch username history', true);
- }
- };
- // Outfits Button
- const outfitsButton = document.createElement('button');
- outfitsButton.className = 'submit-button';
- outfitsButton.textContent = 'Show User Outfits';
- outfitsButton.onclick = async () => {
- try {
- const outfitsResponse = await fetch(`https://avatar.roblox.com/v1/users/${userId}/outfits?page=1&itemsPerPage=50`);
- const outfitsData = await outfitsResponse.json();
- if (!outfitsData.data || outfitsData.data.length === 0) {
- dynamicContentDiv.innerHTML = '<p>No outfits found</p>';
- return;
- }
- // Create outfits grid container
- const outfitsGrid = document.createElement('div');
- outfitsGrid.style.display = 'grid';
- outfitsGrid.style.gridTemplateColumns = 'repeat(auto-fill, minmax(200px, 1fr))';
- outfitsGrid.style.gap = '20px';
- outfitsGrid.style.padding = '20px';
- outfitsGrid.style.marginTop = '20px';
- // Get outfit thumbnails
- const outfitIds = outfitsData.data.map(outfit => outfit.id).filter(id => id); // Filter out any null/undefined IDs
- if (outfitIds.length === 0) {
- dynamicContentDiv.innerHTML = '<p>No valid outfits found</p>';
- return;
- }
- try {
- const thumbnailResponse = await fetch(`https://thumbnails.roblox.com/v1/users/outfits?userOutfitIds=${outfitIds.join(',')}&size=420x420&format=Png`);
- const thumbnailData = await thumbnailResponse.json();
- // Create a map of outfit IDs to their thumbnails
- const thumbnailMap = new Map(thumbnailData.data.map(item => [item.targetId, item.imageUrl]));
- // Create outfit cards
- outfitsData.data.forEach(outfit => {
- if (!outfit || !outfit.id) return; // Skip invalid outfits
- const outfitCard = document.createElement('div');
- outfitCard.style.textAlign = 'center';
- outfitCard.style.border = '1px solid #ccc';
- outfitCard.style.borderRadius = '8px';
- outfitCard.style.padding = '10px';
- outfitCard.style.backgroundColor = '#ffffff';
- outfitCard.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)';
- const thumbnailUrl = thumbnailMap.get(outfit.id) || 'placeholder-url';
- outfitCard.innerHTML = `
- <img src="${thumbnailUrl}" alt="${outfit.name}" style="width: 200px; height: 200px; object-fit: contain; border-radius: 4px;">
- <h4 style="margin: 10px 0; color: #333;">${outfit.name}</h4>
- `;
- outfitsGrid.appendChild(outfitCard);
- });
- // Clear previous content and add new grid
- dynamicContentDiv.innerHTML = '<h4>User Outfits:</h4>';
- dynamicContentDiv.appendChild(outfitsGrid);
- } catch (thumbnailError) {
- console.error('Error fetching thumbnails:', thumbnailError);
- dynamicContentDiv.innerHTML = '<p>Error loading outfit thumbnails</p>';
- }
- } catch (error) {
- console.error('Error fetching outfits:', error);
- dynamicContentDiv.innerHTML = '<p>Failed to fetch outfits</p>';
- displayMessage('Failed to fetch outfits', true);
- }
- };
- // Current Assets Button
- const currentAssetsButton = document.createElement('button');
- currentAssetsButton.className = 'submit-button';
- currentAssetsButton.textContent = 'Show Current Assets';
- currentAssetsButton.onclick = async () => {
- try {
- const currentWearingResponse = await fetch(`https://avatar.roblox.com/v1/users/${userId}/currently-wearing`);
- const currentWearingData = await currentWearingResponse.json();
- const assetsList = await Promise.all(currentWearingData.assetIds.map(async (assetId) => {
- const assetResponse = await fetch(`https://catalog.roblox.com/v1/catalog/items/${assetId}/details`);
- const assetData = await assetResponse.json();
- return `
- <li style="margin-bottom: 10px;">
- <div>${assetData.name || `Asset #${assetId}`}</div>
- <a href="https://www.roblox.com/catalog/${assetId}" target="_blank">View Asset</a>
- </li>
- `;
- }));
- dynamicContentDiv.innerHTML = `
- <h4>Currently Wearing:</h4>
- <ul style="list-style: none; padding: 0;">${assetsList.join('') || '<li>No assets found</li>'}</ul>
- `;
- } catch (error) {
- displayMessage('Failed to fetch current assets', true);
- }
- };
- // Add buttons to container
- buttonsDiv.appendChild(historyButton);
- buttonsDiv.appendChild(outfitsButton);
- buttonsDiv.appendChild(currentAssetsButton);
- // Append everything to result container
- resultContainer.appendChild(imageContainer);
- resultContainer.appendChild(userInfoDiv);
- resultContainer.appendChild(buttonsDiv);
- resultContainer.appendChild(dynamicContentDiv);
- resultContainer.style.display = 'block';
- displayMessage('User information fetched successfully!');
- } catch (error) {
- displayMessage(error.message, true);
- }
- };
- }
- // Updated getOutfitAssets function
- async function getOutfitAssets(outfitId) {
- try {
- const response = await fetch(`https://catalog.roblox.com/v1/outfits/${outfitId}/get-outfit-details`);
- const data = await response.json();
- if (!data.assetIds || data.assetIds.length === 0) {
- throw new Error('No assets found');
- }
- const assetsList = await Promise.all(data.assetIds.map(async (assetId) => {
- try {
- const assetResponse = await fetch(`https://catalog.roblox.com/v1/catalog/items/${assetId}/details`);
- const assetData = await assetResponse.json();
- return `
- <li style="margin-bottom: 10px;">
- <div>${assetData.name || 'Unknown Asset'}</div>
- <a href="https://www.roblox.com/catalog/${assetId}" target="_blank">View Asset (ID: ${assetId})</a>
- </li>
- `;
- } catch (err) {
- return `
- <li style="margin-bottom: 10px;">
- <div>Asset ID: ${assetId}</div>
- <a href="https://www.roblox.com/catalog/${assetId}" target="_blank">View Asset</a>
- </li>
- `;
- }
- }));
- const dynamicContentDiv = document.getElementById('dynamic-content');
- dynamicContentDiv.innerHTML = `
- <h4>Outfit Assets:</h4>
- <ul style="list-style: none; padding: 0;">${assetsList.join('') || '<li>No assets found</li>'}</ul>
- <button class="submit-button" onclick="document.querySelector('[data-action=\'Show User Outfits\']').click()">Back to Outfits</button>
- `;
- } catch (error) {
- console.error('Error fetching outfit assets:', error);
- displayMessage('Failed to fetch outfit assets: ' + error.message, true);
- }
- }
- 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,
- wallResponse,
- settingsResponse,
- socialLinksResponse,
- recentPosts
- ] = 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`),
- fetch(`https://groups.roblox.com/v2/groups/${groupId}/wall/posts?limit=10&sortOrder=Desc`),
- fetch(`https://groups.roblox.com/v1/groups/${groupId}/settings`),
- fetch(`https://groups.roblox.com/v1/groups/${groupId}/social-links`),
- fetch(`https://groups.roblox.com/v2/groups/${groupId}/wall/posts?limit=5&sortOrder=Desc`)
- ]);
- const [groupInfo, membersInfo, iconData, rolesInfo, wallData, settings, socialLinks, recentPostsData] = await Promise.all([
- groupResponse.json(),
- membersResponse.json(),
- iconResponse.json(),
- rolesResponse.json(),
- wallResponse.json(),
- settingsResponse.json(),
- socialLinksResponse.json(),
- recentPosts.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);
- }
- // Calculate group age in days
- const groupAge = Math.floor((new Date() - new Date(groupInfo.created)) / (1000 * 60 * 60 * 24));
- // Format roles with more details
- const rolesHtml = rolesInfo.roles.map(role => `
- <li style="margin-bottom: 10px; padding: 5px; border: 1px solid #eee; border-radius: 4px;">
- <strong>${role.name}</strong><br>
- Members: ${role.memberCount}<br>
- Rank: ${role.rank}<br>
- ${role.description ? `Description: ${role.description}<br>` : ''}
- </li>
- `).join('');
- // Format recent posts
- const recentPostsHtml = recentPostsData.data ? recentPostsData.data.map(post => `
- <li style="margin-bottom: 15px; padding: 10px; border: 1px solid #eee; border-radius: 4px;">
- <strong>${post.poster.username}</strong> - ${new Date(post.created).toLocaleString()}<br>
- ${post.body}
- </li>
- `).join('') : '';
- // Format social links
- const socialLinksHtml = socialLinks.data ? socialLinks.data.map(link => `
- <li><strong>${link.title}</strong>: <a href="${link.url}" target="_blank">${link.url}</a></li>
- `).join('') : '';
- // Display group information
- const infoDiv = document.createElement('div');
- infoDiv.className = 'info-text';
- infoDiv.innerHTML = `
- <h2 style="color: #333; margin-bottom: 20px;">${groupInfo.name}</h2>
- <div style="background: #f5f5f5; padding: 15px; border-radius: 8px; margin-bottom: 20px;">
- <h3>Basic Information</h3>
- <p><strong>Group ID:</strong> ${groupId}</p>
- <p><strong>Owner:</strong> ${groupInfo.owner ? `<a href="https://www.roblox.com/users/${groupInfo.owner.userId}/profile" target="_blank">${groupInfo.owner.username}</a>` : 'No owner'}</p>
- <p><strong>Created:</strong> ${new Date(groupInfo.created).toLocaleString()} (${groupAge} days ago)</p>
- <p><strong>Member Count:</strong> ${membersInfo.memberCount?.toLocaleString() || 0}</p>
- <p><strong>Description:</strong> ${groupInfo.description || 'No description'}</p>
- </div>
- <div style="background: #f5f5f5; padding: 15px; border-radius: 8px; margin-bottom: 20px;">
- <h3>Group Settings</h3>
- <p><strong>Public Entry:</strong> ${groupInfo.publicEntryAllowed ? 'Yes' : 'No'}</p>
- <p><strong>Group Status:</strong> ${groupInfo.isLocked ? 'Locked' : 'Active'}</p>
- <p><strong>Membership Type:</strong> ${groupInfo.publicEntryAllowed ? 'Anyone can join' : 'Approval required'}</p>
- <p><strong>Verified:</strong> ${groupInfo.hasVerifiedBadge ? 'Yes' : 'No'}</p>
- </div>
- ${socialLinks.data && socialLinks.data.length > 0 ? `
- <div style="background: #f5f5f5; padding: 15px; border-radius: 8px; margin-bottom: 20px;">
- <h3>Social Links</h3>
- <ul style="list-style-type: none; padding-left: 0;">
- ${socialLinksHtml}
- </ul>
- </div>
- ` : ''}
- <div style="background: #f5f5f5; padding: 15px; border-radius: 8px; margin-bottom: 20px;">
- <h3>Quick Links</h3>
- <p><a href="https://www.roblox.com/groups/${groupId}" target="_blank">View Group Page</a></p>
- <p><a href="https://www.roblox.com/groups/${groupId}/membership" target="_blank">View Members</a></p>
- <p><a href="https://www.roblox.com/abusereport/group?id=${groupId}" target="_blank">Report Group</a></p>
- </div>
- <div style="background: #f5f5f5; padding: 15px; border-radius: 8px; margin-bottom: 20px;">
- <h3>Roles (${rolesInfo.roles.length})</h3>
- <ul style="list-style-type: none; padding-left: 0;">
- ${rolesHtml}
- </ul>
- </div>
- ${recentPostsData.data && recentPostsData.data.length > 0 ? `
- <div style="background: #f5f5f5; padding: 15px; border-radius: 8px; margin-bottom: 20px;">
- <h3>Recent Wall Posts</h3>
- <ul style="list-style-type: none; padding-left: 0;">
- ${recentPostsHtml}
- </ul>
- </div>
- ` : ''}
- `;
- 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);
- };
- }
- async function initializeCodeRedemption() {
- const mainWrapper = document.createElement('div');
- mainWrapper.className = 'main-content-wrapper';
- document.body.appendChild(mainWrapper);
- const { container, input, button } = createBasicForm('Enter Code', 'Redeem Code');
- const autoRedeemButton = document.createElement('button');
- autoRedeemButton.className = 'submit-button';
- autoRedeemButton.style.backgroundColor = '#ff4444';
- autoRedeemButton.textContent = 'Auto-Redeem All Active Codes';
- container.appendChild(autoRedeemButton);
- const showCodesButton = document.createElement('button');
- showCodesButton.className = 'submit-button';
- showCodesButton.style.backgroundColor = '#4a90e2'; // Blue color to distinguish it
- showCodesButton.textContent = 'Show Available Codes';
- container.appendChild(showCodesButton);
- // Known codes list with additional information
- const availableCodes = [
- {
- code: 'SPIDERCOLA',
- reward: 'Spider Cola Shoulder Pet',
- expires: 'No expiration'
- },
- {
- code: 'TWEETROBLOX',
- reward: 'The Bird Says Shoulder Pet',
- expires: 'No expiration'
- },
- {
- code: 'ROBLOXEDU2023',
- reward: 'School Backpack Accessory',
- expires: 'No expiration'
- },
- {
- code: 'AMAZONFRIEND2024',
- reward: 'Amazon Prime Gaming Reward',
- expires: 'March 31, 2024'
- },
- {
- code: 'BRICKMASTER2024',
- reward: 'Special Avatar Item',
- expires: 'December 31, 2024'
- },
- {
- code: 'ROADTO100K',
- reward: 'Special Avatar Accessory',
- expires: 'No expiration'
- },
- {
- code: 'VANITYXBOY',
- reward: 'Vanity Backpack',
- expires: 'No expiration'
- },
- {
- code: 'SHINYJOKER',
- reward: 'Shiny Joker Mask',
- expires: 'No expiration'
- },
- {
- code: 'ICYGLOW',
- reward: 'Glowing Ice Crown',
- expires: 'No expiration'
- },
- {
- code: 'DARKBLOOD',
- reward: 'Dark Blood Cape',
- expires: 'No expiration'
- },
- {
- code: 'BOOMEXPLOSION',
- reward: 'Boom Explosion Mask',
- expires: 'No expiration'
- },
- {
- code: 'BLOXYPARTY',
- reward: 'Bloxyparty Hat',
- expires: 'No expiration'
- },
- {
- code: 'WATERFALL2024',
- reward: 'Waterfall Back Bling',
- expires: 'No expiration'
- },
- {
- code: 'MAYDAY2024',
- reward: 'May Day Hat',
- expires: 'May 1, 2024'
- },
- {
- code: 'PARTYBEAN2024',
- reward: 'Party Bean Hat',
- expires: 'July 1, 2024'
- }
- ];
- // Show Available Codes functionality
- showCodesButton.onclick = () => {
- resultContainer.innerHTML = '<h3>Available Roblox Codes:</h3>';
- const codesTable = document.createElement('div');
- codesTable.style.padding = '15px';
- codesTable.style.backgroundColor = '#ffffff';
- codesTable.style.borderRadius = '8px';
- codesTable.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)';
- codesTable.style.margin = '10px 0';
- // Create table header
- codesTable.innerHTML = `
- <div style="display: grid; grid-template-columns: 1fr 2fr 1fr; gap: 10px; margin-bottom: 10px; font-weight: bold; padding: 10px; background-color: #f5f5f5; border-radius: 4px;">
- <div>Code</div>
- <div>Reward</div>
- <div>Expires</div>
- </div>
- `;
- // Add each code to the table
- availableCodes.forEach(codeInfo => {
- const codeRow = document.createElement('div');
- codeRow.style.display = 'grid';
- codeRow.style.gridTemplateColumns = '1fr 2fr 1fr';
- codeRow.style.gap = '10px';
- codeRow.style.padding = '10px';
- codeRow.style.borderBottom = '1px solid #eee';
- codeRow.innerHTML = `
- <div style="font-family: monospace; font-weight: bold;">${codeInfo.code}</div>
- <div>${codeInfo.reward}</div>
- <div>${codeInfo.expires}</div>
- `;
- codesTable.appendChild(codeRow);
- });
- resultContainer.appendChild(codesTable);
- // Add note about codes
- const note = document.createElement('p');
- note.style.marginTop = '15px';
- note.style.padding = '10px';
- note.style.backgroundColor = '#fff3cd';
- note.style.borderRadius = '4px';
- note.style.color = '#856404';
- note.innerHTML = '⚠️ Note: These codes are current as of our last update. Some codes might expire without notice.';
- resultContainer.appendChild(note);
- };
- mainWrapper.appendChild(container);
- const resultContainer = createResultContainer();
- mainWrapper.appendChild(resultContainer);
- async function getXCSRFToken() {
- return new Promise((resolve, reject) => {
- GM_xmlhttpRequest({
- method: "POST",
- url: "https://auth.roblox.com/v2/logout",
- headers: {
- "Content-Type": "application/json",
- },
- withCredentials: true,
- onload: function(response) {
- const token = response.responseHeaders.match(/x-csrf-token: (.+)/i)?.[1];
- resolve(token);
- },
- onerror: function(error) {
- reject(new Error('Failed to get CSRF token'));
- }
- });
- });
- }
- async function redeemCode(code, token) {
- return new Promise((resolve, reject) => {
- GM_xmlhttpRequest({
- method: "POST",
- url: "https://promocodes.roblox.com/v1/promocodes/redeem",
- headers: {
- "Content-Type": "application/json",
- "X-CSRF-TOKEN": token
- },
- data: JSON.stringify({ code: code }),
- withCredentials: true,
- onload: function(response) {
- try {
- const result = JSON.parse(response.responseText);
- if (response.status === 200) {
- resolve({
- code: code,
- success: true,
- message: result.message || 'Code redeemed successfully'
- });
- } else {
- resolve({
- code: code,
- success: false,
- message: result.message || result.errors?.[0]?.message || 'Failed to redeem code'
- });
- }
- } catch (e) {
- resolve({
- code: code,
- success: false,
- message: 'Invalid response from server'
- });
- }
- },
- onerror: function(error) {
- resolve({
- code: code,
- success: false,
- message: 'Network request failed'
- });
- }
- });
- });
- }
- // Known codes list
- const knownCodes = [
- 'SPIDERCOLA', 'TWEETROBLOX', 'ROBLOXEDU2023',
- 'AMAZONFRIEND2024', 'BRICKMASTER2024', 'ROADTO100K'
- ];
- // Auto-redeem functionality
- autoRedeemButton.onclick = async () => {
- try {
- resultContainer.innerHTML = '<h3>Auto-Redeem Results:</h3>';
- const resultsList = document.createElement('ul');
- resultsList.style.listStyle = 'none';
- resultsList.style.padding = '10px';
- // Get CSRF token once before starting
- const token = await getXCSRFToken();
- if (!token) {
- throw new Error('Failed to get authentication token. Please ensure you are logged in.');
- }
- for (const code of knownCodes) {
- // Add delay between attempts
- if (knownCodes.indexOf(code) > 0) {
- await new Promise(resolve => setTimeout(resolve, 2000));
- }
- const result = await redeemCode(code, token);
- const listItem = document.createElement('li');
- listItem.style.padding = '10px';
- listItem.style.margin = '5px 0';
- listItem.style.borderRadius = '4px';
- listItem.style.backgroundColor = result.success ? '#e8f5e9' : '#ffebee';
- listItem.innerHTML = `
- <strong>${code}:</strong> ${result.success ? '✅' : '❌'}
- ${result.message}
- `;
- resultsList.appendChild(listItem);
- resultContainer.appendChild(resultsList);
- }
- } catch (error) {
- resultContainer.innerHTML = `
- <div class="error-message" style="padding: 15px; margin-top: 20px; border-radius: 4px;">
- <h3>❌ Error</h3>
- <p>${error.message}</p>
- </div>
- `;
- }
- };
- // Single code redemption
- button.onclick = async () => {
- try {
- const code = input.value.trim();
- if (!code) {
- displayMessage('Please enter a code', true);
- return;
- }
- const token = await getXCSRFToken();
- if (!token) {
- throw new Error('Failed to get authentication token. Please ensure you are logged in.');
- }
- const result = await redeemCode(code, token);
- resultContainer.innerHTML = `
- <div class="${result.success ? 'success-message' : 'error-message'}"
- style="padding: 15px; margin-top: 20px; border-radius: 4px;">
- <h3>${result.success ? '✅ Success!' : '❌ Error'}</h3>
- <p>${result.message}</p>
- </div>
- `;
- } catch (error) {
- resultContainer.innerHTML = `
- <div class="error-message" style="padding: 15px; margin-top: 20px; border-radius: 4px;">
- <h3>❌ Error</h3>
- <p>${error.message}</p>
- </div>
- `;
- }
- };
- }
- // Add this new function before the createPanel() function
- async function initializeRobloxInstallers() {
- const mainWrapper = document.createElement('div');
- mainWrapper.className = 'main-content-wrapper';
- document.body.appendChild(mainWrapper);
- const container = document.createElement('div');
- container.className = 'form-container';
- mainWrapper.appendChild(container);
- const title = document.createElement('h2');
- title.textContent = 'Roblox Installers';
- container.appendChild(title);
- // Define installer categories and their links
- const installers = {
- 'Current Installers': [
- {
- name: 'Roblox Player (Windows)',
- url: 'https://setup.rbxcdn.com/version-2e66f0f5ee7944ce/RobloxPlayerLauncher.exe',
- icon: '🎮'
- },
- {
- name: 'Roblox Player (Mac)',
- url: 'https://setup.rbxcdn.com/mac/version-2e66f0f5ee7944ce/RobloxPlayer.dmg',
- icon: '🎮'
- },
- {
- name: 'Roblox Studio (Windows)',
- url: 'https://setup.rbxcdn.com/version-2e66f0f5ee7944ce/RobloxStudioLauncherBeta.exe',
- icon: '🛠️'
- },
- {
- name: 'Roblox Studio (Mac)',
- url: 'https://setup.rbxcdn.com/mac/version-2e66f0f5ee7944ce/RobloxStudio.dmg',
- icon: '🛠️'
- }
- ],
- 'Chinese Version': [
- {
- name: 'Roblox China Player',
- url: 'https://setup.rbxcdn.cn/version-2e66f0f5ee7944ce/RobloxPlayerLauncher.exe',
- icon: '🇨🇳'
- },
- {
- name: 'Roblox China Studio',
- url: 'https://setup.rbxcdn.cn/version-2e66f0f5ee7944ce/RobloxStudioLauncherBeta.exe',
- icon: '🇨🇳'
- }
- ],
- 'Legacy Versions': [
- {
- name: 'Roblox 2016 Player',
- url: 'https://setup.rbxcdn.com/version-30b898b26e4d4b8b/RobloxPlayerLauncher.exe',
- icon: '📜'
- },
- {
- name: 'Roblox 2017 Player',
- url: 'https://setup.rbxcdn.com/version-47c9e5c1190a4d7b/RobloxPlayerLauncher.exe',
- icon: '📜'
- },
- {
- name: 'Roblox 2018 Player',
- url: 'https://setup.rbxcdn.com/version-60622c8b13104d53/RobloxPlayerLauncher.exe',
- icon: '📜'
- }
- ]
- };
- // Create sections for each category
- Object.entries(installers).forEach(([category, items]) => {
- const section = document.createElement('div');
- section.style.marginBottom = '20px';
- section.style.backgroundColor = '#ffffff';
- section.style.padding = '15px';
- section.style.borderRadius = '8px';
- section.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)';
- const categoryTitle = document.createElement('h3');
- categoryTitle.textContent = category;
- categoryTitle.style.marginBottom = '10px';
- section.appendChild(categoryTitle);
- items.forEach(item => {
- const button = document.createElement('button');
- button.className = 'panel-button';
- button.style.marginBottom = '10px';
- button.innerHTML = `${item.icon} ${item.name}`;
- button.onclick = () => {
- window.location.href = item.url;
- };
- section.appendChild(button);
- });
- container.appendChild(section);
- });
- // Add warning message
- const warning = document.createElement('div');
- warning.style.backgroundColor = '#fff3cd';
- warning.style.color = '#856404';
- warning.style.padding = '15px';
- warning.style.borderRadius = '8px';
- warning.style.marginTop = '20px';
- warning.innerHTML = `
- <h4>⚠️ Important Notes:</h4>
- <ul style="margin-left: 20px;">
- <li>Legacy versions may not work with current Roblox services</li>
- <li>Chinese version requires a Chinese Roblox account</li>
- <li>Some versions might require specific operating systems</li>
- <li>Always download from trusted sources</li>
- </ul>
- `;
- container.appendChild(warning);
- }
- // 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' },
- { text: 'Code Redemption', url: '/redeemcode' },
- { text: 'Roblox Installers', url: '/installers' }
- ];
- 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;
- case '/redeemcode':
- initializeCodeRedemption();
- break;
- case '/installers':
- initializeRobloxInstallers();
- break;
- }
- })();