Roblox Multi-Feature User Panel.

Download Roblox thumbnails, game info, badge info, and user info and the images for all those!

当前为 2024-11-17 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Roblox Multi-Feature User Panel.
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.6
  5. // @description Download Roblox thumbnails, game info, badge info, and user info and the images for all those!
  6. // @author NotRoblox
  7. // @match https://www.roblox.com/userpanel
  8. // @match https://www.roblox.com/getgameinfo
  9. // @match https://www.roblox.com/getbadgeinfo
  10. // @match https://www.roblox.com/getuserinfo
  11. // @match https://www.roblox.com/getgroupinfo
  12. // @grant GM_cookie
  13. // @connect promocodes.roblox.com
  14. // @connect auth.roblox.com
  15. // @connect www.roblox.com
  16. // @match https://www.roblox.com/*
  17. // @match https://promocodes.roblox.com/*
  18. // @match https://auth.roblox.com/*
  19. // @grant GM_xmlhttpRequest
  20. // @grant Gm_download
  21. // @license MIT
  22. // ==/UserScript==
  23.  
  24. (function() {
  25. 'use strict';
  26.  
  27. const style = document.createElement('style');
  28. style.textContent = `
  29. body {
  30. font-family: Arial, sans-serif;
  31. background-color: #f4f7f6;
  32. margin: 0;
  33. padding: 0;
  34. }
  35.  
  36. .main-content-wrapper {
  37. width: 100%;
  38. padding: 20px;
  39. margin-bottom: 120px;
  40. display: flex;
  41. flex-direction: column;
  42. align-items: center;
  43. min-height: calc(100vh - 200px);
  44. }
  45.  
  46. .form-container {
  47. background-color: #ffffff;
  48. padding: 20px;
  49. border-radius: 8px;
  50. box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
  51. width: 100%;
  52. max-width: 400px;
  53. text-align: center;
  54. margin: 20px auto;
  55. position: relative;
  56. z-index: 1;
  57. }
  58.  
  59. .input-field {
  60. width: 100%;
  61. padding: 10px;
  62. margin: 10px 0;
  63. border: 2px solid #ddd;
  64. border-radius: 4px;
  65. font-size: 16px;
  66. }
  67.  
  68. .submit-button, .panel-button {
  69. background-color: #4CAF50;
  70. color: white;
  71. padding: 12px 20px;
  72. border: none;
  73. border-radius: 4px;
  74. cursor: pointer;
  75. width: 100%;
  76. font-size: 16px;
  77. margin: 10px 0;
  78. }
  79.  
  80. .submit-button:hover, .panel-button:hover {
  81. background-color: #45a049;
  82. }
  83.  
  84. .result-container {
  85. background-color: #ffffff;
  86. padding: 20px;
  87. border-radius: 8px;
  88. box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
  89. width: 100%;
  90. max-width: 800px;
  91. margin: 20px auto 120px auto;
  92. position: relative;
  93. z-index: 1;
  94. }
  95.  
  96. .image-container {
  97. display: flex;
  98. flex-wrap: wrap;
  99. gap: 20px;
  100. justify-content: center;
  101. margin: 20px 0;
  102. }
  103.  
  104. .image-item {
  105. text-align: center;
  106. }
  107.  
  108. .image-item img {
  109. max-width: 200px;
  110. border-radius: 8px;
  111. margin-bottom: 10px;
  112. }
  113.  
  114. .info-text {
  115. margin: 10px 0;
  116. font-size: 16px;
  117. }
  118.  
  119. .error-message {
  120. color: #ff0000;
  121. margin: 10px 0;
  122. }
  123.  
  124. .success-message {
  125. color: #4CAF50;
  126. margin: 10px 0;
  127. }
  128. `;
  129. document.head.appendChild(style);
  130.  
  131. async function getUserIdFromUsername(username) {
  132. const response = await fetch(`https://users.roblox.com/v1/usernames/users`, {
  133. method: 'POST',
  134. headers: { 'Content-Type': 'application/json' },
  135. body: JSON.stringify({ usernames: [username] })
  136. });
  137. const data = await response.json();
  138. if (!data.data || data.data.length === 0) throw new Error('User not found');
  139. return data.data[0].id;
  140. }
  141.  
  142. function createBasicForm(placeholder, buttonText) {
  143. const container = document.createElement('div');
  144. container.className = 'form-container';
  145.  
  146. const input = document.createElement('input');
  147. input.type = 'text';
  148. input.className = 'input-field';
  149. input.placeholder = placeholder;
  150.  
  151. const button = document.createElement('button');
  152. button.className = 'submit-button';
  153. button.textContent = buttonText;
  154.  
  155. container.appendChild(input);
  156. container.appendChild(button);
  157.  
  158. return { container, input, button };
  159. }
  160.  
  161. function displayMessage(message, isError = false) {
  162. const messageDiv = document.createElement('div');
  163. messageDiv.className = isError ? 'error-message' : 'success-message';
  164. messageDiv.textContent = message;
  165. document.querySelector('.form-container').appendChild(messageDiv);
  166. setTimeout(() => messageDiv.remove(), 5000);
  167. }
  168.  
  169. function createResultContainer() {
  170. const container = document.createElement('div');
  171. container.className = 'result-container';
  172. return container;
  173. }
  174.  
  175. async function initializeGameInfo() {
  176. const mainWrapper = document.createElement('div');
  177. mainWrapper.className = 'main-content-wrapper';
  178. document.body.appendChild(mainWrapper);
  179.  
  180. const { container, input, button } = createBasicForm('Enter Game ID', 'Get Game Info');
  181. mainWrapper.appendChild(container);
  182.  
  183. const refreshContent = async (gameId) => {
  184. const existingResults = mainWrapper.querySelectorAll('.result-container');
  185. existingResults.forEach(result => result.remove());
  186.  
  187. try {
  188. // Get the universe ID first
  189. const placeResponse = await fetch(`https://apis.roblox.com/universes/v1/places/${gameId}/universe`);
  190. const placeData = await placeResponse.json();
  191. const universeId = placeData.universeId;
  192.  
  193. // Fetch all required data
  194. const [gameResponse, iconResponse, thumbnailResponse, votesResponse, favoritesResponse] = await Promise.all([
  195. fetch(`https://games.roblox.com/v1/games?universeIds=${universeId}`),
  196. fetch(`https://thumbnails.roblox.com/v1/games/icons?universeIds=${universeId}&size=512x512&format=Png&isCircular=false`),
  197. fetch(`https://thumbnails.roblox.com/v1/games/${universeId}/thumbnails?size=768x432&format=Png&limit=10`),
  198. fetch(`https://games.roblox.com/v1/games/${universeId}/votes`),
  199. fetch(`https://games.roblox.com/v1/games/${universeId}/favorites/count`),
  200. ]);
  201.  
  202. const [gameData, iconData, thumbnailData, votesData, favoritesData] = await Promise.all([
  203. gameResponse.json(),
  204. iconResponse.json(),
  205. thumbnailResponse.json(),
  206. votesResponse.json(),
  207. favoritesResponse.json()
  208. ]);
  209.  
  210. const resultContainer = createResultContainer();
  211.  
  212. // Create image container for all images
  213. const imageContainer = document.createElement('div');
  214. imageContainer.className = 'image-container';
  215.  
  216. // Display game icon
  217. if (iconData.data && iconData.data[0]) {
  218. const iconDiv = document.createElement('div');
  219. iconDiv.className = 'image-item';
  220.  
  221. const iconImg = document.createElement('img');
  222. iconImg.src = iconData.data[0].imageUrl;
  223. iconImg.alt = 'Game Icon';
  224. iconImg.className = 'game-icon';
  225.  
  226. const downloadIconBtn = document.createElement('button');
  227. downloadIconBtn.className = 'submit-button';
  228. downloadIconBtn.textContent = 'Download Icon';
  229. downloadIconBtn.onclick = () => GM_download({
  230. url: iconData.data[0].imageUrl,
  231. name: `game_${gameId}_icon.png`
  232. });
  233.  
  234. iconDiv.appendChild(iconImg);
  235. iconDiv.appendChild(downloadIconBtn);
  236. imageContainer.appendChild(iconDiv);
  237. }
  238.  
  239. // Display thumbnails
  240. if (thumbnailData.data) {
  241. thumbnailData.data.forEach((thumb, index) => {
  242. const thumbDiv = document.createElement('div');
  243. thumbDiv.className = 'image-item';
  244.  
  245. const thumbImg = document.createElement('img');
  246. thumbImg.src = thumb.imageUrl;
  247. thumbImg.alt = `Game Thumbnail ${index + 1}`;
  248. thumbImg.className = 'thumbnail-image';
  249.  
  250. const downloadThumbBtn = document.createElement('button');
  251. downloadThumbBtn.className = 'submit-button';
  252. downloadThumbBtn.textContent = `Download Thumbnail ${index + 1}`;
  253. downloadThumbBtn.onclick = () => GM_download({
  254. url: thumb.imageUrl,
  255. name: `game_${gameId}_thumbnail_${index + 1}.png`
  256. });
  257.  
  258. thumbDiv.appendChild(thumbImg);
  259. thumbDiv.appendChild(downloadThumbBtn);
  260. imageContainer.appendChild(thumbDiv);
  261. });
  262. }
  263.  
  264. // Display game information
  265. if (gameData.data && gameData.data[0]) {
  266. const game = gameData.data[0];
  267. const likes = votesData.upVotes || 0;
  268. const dislikes = votesData.downVotes || 0;
  269. const totalVotes = likes + dislikes;
  270. const likeRatio = totalVotes > 0 ? ((likes / totalVotes) * 100).toFixed(1) : 0;
  271. resultContainer.appendChild(imageContainer);
  272.  
  273. const gameInfo = document.createElement('div');
  274. gameInfo.className = 'info-text';
  275. gameInfo.innerHTML = `
  276. <h2>${game.name}</h2>
  277. <div class="game-stats">
  278. <div class="stat-item">
  279. <h4>👥 Player Stats</h4>
  280. <p>Current Players: ${game.playing?.toLocaleString() || 0}</p>
  281. <p>Total Visits: ${game.visits?.toLocaleString() || 0}</p>
  282. <p>Max Players: ${game.maxPlayers || 'Unknown'}</p>
  283. </div>
  284. <div class="stat-item">
  285. <h4>👍 Ratings</h4>
  286. <p>Likes: ${likes.toLocaleString()}</p>
  287. <p>Dislikes: ${dislikes.toLocaleString()}</p>
  288. <p>Like Ratio: ${likeRatio}%</p>
  289. <p>Favorites: ${favoritesData.favoritesCount?.toLocaleString() || 0}</p>
  290. </div>
  291. <div class="stat-item">
  292. <h4>ℹ️ Details</h4>
  293. <p>Created: ${new Date(game.created).toLocaleDateString()}</p>
  294. <p>Last Updated: ${new Date(game.updated).toLocaleDateString()}</p>
  295. <p>Genre: ${game.genre || 'Not specified'}</p>
  296. <p>Allowed Gear Types: ${game.allowedGearTypes?.join(', ') || 'None'}</p>
  297. </div>
  298. </div>
  299. <div class="game-description">
  300. <h4>📝 Description</h4>
  301. <p>${game.description || 'No description available'}</p>
  302. </div>
  303. <p class="game-link"><a href="https://www.roblox.com/games/${gameId}" target="_blank">🎮 View Game Page</a></p>
  304. `;
  305. resultContainer.appendChild(gameInfo);
  306. }
  307.  
  308.  
  309. mainWrapper.appendChild(resultContainer);
  310. } catch (error) {
  311. console.error(error);
  312. displayMessage(error.message, true);
  313. }
  314. };
  315.  
  316.  
  317. button.onclick = async () => {
  318. const gameId = input.value.trim();
  319. if (!gameId) {
  320. displayMessage('Please enter a game ID', true);
  321. return;
  322. }
  323. await refreshContent(gameId);
  324. };
  325. }
  326.  
  327. async function initializeBadgeInfo() {
  328. const mainWrapper = document.createElement('div');
  329. mainWrapper.className = 'main-content-wrapper';
  330. document.body.appendChild(mainWrapper);
  331.  
  332. const { container, input, button } = createBasicForm('Enter Badge ID', 'Get Badge Info');
  333. mainWrapper.appendChild(container);
  334.  
  335. const refreshContent = async (badgeId) => {
  336. // Remove any existing result containers
  337. const existingResults = mainWrapper.querySelectorAll('.result-container');
  338. existingResults.forEach(result => result.remove());
  339.  
  340. try {
  341. // Fetch badge info with proper error handling
  342. const infoResponse = await fetch(`https://badges.roblox.com/v1/badges/${badgeId}`, {
  343. method: 'GET',
  344. credentials: 'include'
  345. });
  346.  
  347. if (!infoResponse.ok) {
  348. throw new Error('Failed to fetch badge information');
  349. }
  350.  
  351. const badgeInfo = await infoResponse.json();
  352.  
  353. // Fetch badge statistics
  354. const statsResponse = await fetch(`https://badges.roblox.com/v1/badges/${badgeId}/statistics`, {
  355. method: 'GET',
  356. credentials: 'include'
  357. });
  358.  
  359. const statsData = await statsResponse.json();
  360.  
  361. // Fetch badge icon
  362. const iconResponse = await fetch(`https://thumbnails.roblox.com/v1/badges/icons?badgeIds=${badgeId}&size=150x150&format=Png`, {
  363. method: 'GET',
  364. credentials: 'include'
  365. });
  366.  
  367. const iconData = await iconResponse.json();
  368.  
  369. const resultContainer = createResultContainer();
  370.  
  371. // Create image container
  372. const imageContainer = document.createElement('div');
  373. imageContainer.className = 'image-container';
  374.  
  375. // Display badge icon if available
  376. if (iconData.data && iconData.data[0]) {
  377. const iconDiv = document.createElement('div');
  378. iconDiv.className = 'image-item';
  379.  
  380. const iconImg = document.createElement('img');
  381. iconImg.src = iconData.data[0].imageUrl;
  382. iconImg.alt = 'Badge Icon';
  383. iconImg.style.maxWidth = '150px';
  384.  
  385. const downloadBtn = document.createElement('button');
  386. downloadBtn.className = 'submit-button';
  387. downloadBtn.textContent = 'Download Badge Icon';
  388. downloadBtn.onclick = () => GM_download({
  389. url: iconData.data[0].imageUrl,
  390. name: `badge_${badgeId}.png`
  391. });
  392.  
  393. iconDiv.appendChild(iconImg);
  394. iconDiv.appendChild(downloadBtn);
  395. imageContainer.appendChild(iconDiv);
  396. }
  397.  
  398. // Display badge information
  399. const infoDiv = document.createElement('div');
  400. infoDiv.className = 'info-text';
  401. infoDiv.innerHTML = `
  402. <h3>${badgeInfo.name || 'Unknown Badge'}</h3>
  403. <div style="background: #f5f5f5; padding: 15px; border-radius: 8px; margin: 10px 0;">
  404. <h4>Badge Details</h4>
  405. <p><strong>Description:</strong> ${badgeInfo.description || 'No description available'}</p>
  406. <p><strong>Enabled:</strong> ${badgeInfo.enabled ? '✅ Yes' : '❌ No'}</p>
  407. <p><strong>Created:</strong> ${new Date(badgeInfo.created).toLocaleDateString()}</p>
  408. <p><strong>Updated:</strong> ${new Date(badgeInfo.updated).toLocaleDateString()}</p>
  409. </div>
  410.  
  411. <div style="background: #f5f5f5; padding: 15px; border-radius: 8px; margin: 10px 0;">
  412. <h4>Statistics</h4>
  413. <p><strong>Win Rate:</strong> ${statsData.winRatePercentage?.toFixed(2) || 0}%</p>
  414. <p><strong>Awarded:</strong> ${statsData.awardedCount?.toLocaleString() || 0} times</p>
  415. </div>
  416.  
  417. <div style="background: #f5f5f5; padding: 15px; border-radius: 8px; margin: 10px 0;">
  418. <h4>Links</h4>
  419. <p><a href="https://www.roblox.com/badges/${badgeId}" target="_blank">View Badge Page</a></p>
  420. ${badgeInfo.awardingUniverse ?
  421. `<p><a href="https://www.roblox.com/games/${badgeInfo.awardingUniverse.id}" target="_blank">View Game Page</a></p>`
  422. : ''}
  423. </div>
  424. `;
  425.  
  426. resultContainer.appendChild(imageContainer);
  427. resultContainer.appendChild(infoDiv);
  428. mainWrapper.appendChild(resultContainer);
  429. displayMessage('Badge information fetched successfully!');
  430. } catch (error) {
  431. const resultContainer = createResultContainer();
  432. resultContainer.innerHTML = `
  433. <div class="error-message" style="padding: 15px; margin-top: 20px; border-radius: 4px;">
  434. <h3>❌ Error</h3>
  435. <p>Failed to fetch badge information: ${error.message}</p>
  436. <p>Please make sure the badge ID is valid and try again.</p>
  437. </div>
  438. `;
  439. mainWrapper.appendChild(resultContainer);
  440. displayMessage(error.message, true);
  441. }
  442. };
  443.  
  444. button.onclick = async () => {
  445. const badgeId = input.value.trim();
  446. if (!badgeId) {
  447. displayMessage('Please enter a badge ID', true);
  448. return;
  449. }
  450. await refreshContent(badgeId);
  451. };
  452. }
  453.  
  454. async function initializeUserInfo() {
  455. const mainWrapper = document.createElement('div');
  456. mainWrapper.className = 'main-content-wrapper';
  457. document.body.appendChild(mainWrapper);
  458.  
  459. const { container, input, button } = createBasicForm('Enter Username', 'Get User Info');
  460. mainWrapper.appendChild(container);
  461.  
  462. const resultContainer = createResultContainer();
  463. resultContainer.style.display = 'none';
  464. mainWrapper.appendChild(resultContainer);
  465.  
  466. button.onclick = async () => {
  467. try {
  468. const username = input.value.trim();
  469. if (!username) throw new Error('Please enter a username');
  470.  
  471. const userId = await getUserIdFromUsername(username);
  472.  
  473. const [
  474. userInfoResponse,
  475. presenceResponse,
  476. friendsResponse,
  477. followersResponse,
  478. thumbnailResponse,
  479. bustResponse,
  480. headshotResponse
  481. ] = await Promise.all([
  482. fetch(`https://users.roblox.com/v1/users/${userId}`),
  483. fetch(`https://presence.roblox.com/v1/presence/users`, {
  484. method: 'POST',
  485. headers: { 'Content-Type': 'application/json' },
  486. body: JSON.stringify({ userIds: [userId] })
  487. }),
  488. fetch(`https://friends.roblox.com/v1/users/${userId}/friends/count`),
  489. fetch(`https://friends.roblox.com/v1/users/${userId}/followers/count`),
  490. fetch(`https://thumbnails.roblox.com/v1/users/avatar?userIds=${userId}&size=420x420&format=Png`),
  491. fetch(`https://thumbnails.roblox.com/v1/users/avatar-bust?userIds=${userId}&size=420x420&format=Png`),
  492. fetch(`https://thumbnails.roblox.com/v1/users/avatar-headshot?userIds=${userId}&size=420x420&format=Png`)
  493. ]);
  494.  
  495. const [userInfo, presence, friends, followers, thumbnail, bust, headshot] = await Promise.all([
  496. userInfoResponse.json(),
  497. presenceResponse.json(),
  498. friendsResponse.json(),
  499. followersResponse.json(),
  500. thumbnailResponse.json(),
  501. bustResponse.json(),
  502. headshotResponse.json()
  503. ]);
  504.  
  505. resultContainer.innerHTML = '';
  506.  
  507. // Create thumbnails section
  508. const imageContainer = document.createElement('div');
  509. imageContainer.className = 'image-container';
  510.  
  511. const createImageSection = (data, type) => {
  512. if (data.data && data.data[0]) {
  513. const div = document.createElement('div');
  514. div.className = 'image-item';
  515.  
  516. const img = document.createElement('img');
  517. img.src = data.data[0].imageUrl;
  518. img.alt = `${type} thumbnail`;
  519.  
  520. const downloadBtn = document.createElement('button');
  521. downloadBtn.className = 'submit-button';
  522. downloadBtn.textContent = `Download ${type}`;
  523. downloadBtn.onclick = () => GM_download({
  524. url: data.data[0].imageUrl,
  525. name: `${username}_${type}.png`
  526. });
  527.  
  528. div.appendChild(img);
  529. div.appendChild(downloadBtn);
  530. imageContainer.appendChild(div);
  531. }
  532. };
  533.  
  534. createImageSection(thumbnail, 'Full Avatar');
  535. createImageSection(bust, 'Bust');
  536. createImageSection(headshot, 'Headshot');
  537.  
  538. // Create user info section
  539. const userInfoDiv = document.createElement('div');
  540. userInfoDiv.className = 'info-text';
  541.  
  542. const userPresence = presence.userPresences[0];
  543. userInfoDiv.innerHTML = `
  544. <h3>User Information for ${userInfo.displayName} (${userInfo.name})</h3>
  545. <p>User ID: ${userId}</p>
  546. <p>Display Name: ${userInfo.displayName}</p>
  547. <p>Username: ${userInfo.name}</p>
  548. <p>Description: ${userInfo.description ? userInfo.description : 'No description available'}</p>
  549. <p>Account Age: ${userInfo.age} days</p>
  550. <p>Join Date: ${new Date(userInfo.created).toLocaleDateString()}</p>
  551. <p>Online Status: ${userPresence.userPresenceType === 0 ? 'Offline' : userPresence.userPresenceType === 1 ? 'Online' : 'Playing'}</p>
  552. <p>Friends Count: ${friends.count}</p>
  553. <p>Followers Count: ${followers.count}</p>
  554. <p>Profile Link: <a href="https://www.roblox.com/users/${userId}/profile" target="_blank">View Profile</a></p>
  555. ${userPresence.userPresenceType !== 0 ? `<p>Last Location: ${userPresence.lastLocation}</p>` : ''}
  556. <p>
  557. <a href="https://www.roblox.com/abusereport/userprofile?id=${userId}" target="_blank">Report User</a> |
  558. <a href="https://www.roblox.com/illegal-content-reporting" target="_blank">Report to DMCA</a>
  559. </p>
  560. `;
  561.  
  562. // Create container for dynamic content
  563. const dynamicContentDiv = document.createElement('div');
  564. dynamicContentDiv.id = 'dynamic-content';
  565.  
  566. // Create buttons container
  567. const buttonsDiv = document.createElement('div');
  568. buttonsDiv.className = 'buttons-container';
  569.  
  570. // Username History Button
  571. const historyButton = document.createElement('button');
  572. historyButton.className = 'submit-button';
  573. historyButton.textContent = 'Show Username History';
  574. historyButton.onclick = async () => {
  575. try {
  576. const historyResponse = await fetch(`https://users.roblox.com/v1/users/${userId}/username-history`);
  577. const historyData = await historyResponse.json();
  578.  
  579. const historyList = historyData.data.map((entry) => `<li>${entry.name}</li>`).join('');
  580. dynamicContentDiv.innerHTML = `<h4>Username History:</h4><ul>${historyList || '<li>No username changes found</li>'}</ul>`;
  581. } catch (error) {
  582. displayMessage('Failed to fetch username history', true);
  583. }
  584. };
  585.  
  586. // Outfits Button
  587. const outfitsButton = document.createElement('button');
  588. outfitsButton.className = 'submit-button';
  589. outfitsButton.textContent = 'Show User Outfits';
  590. outfitsButton.onclick = async () => {
  591. try {
  592. const outfitsResponse = await fetch(`https://avatar.roblox.com/v1/users/${userId}/outfits?page=1&itemsPerPage=50`);
  593. const outfitsData = await outfitsResponse.json();
  594.  
  595. if (!outfitsData.data || outfitsData.data.length === 0) {
  596. dynamicContentDiv.innerHTML = '<p>No outfits found</p>';
  597. return;
  598. }
  599.  
  600. // Create outfits grid container
  601. const outfitsGrid = document.createElement('div');
  602. outfitsGrid.style.display = 'grid';
  603. outfitsGrid.style.gridTemplateColumns = 'repeat(auto-fill, minmax(200px, 1fr))';
  604. outfitsGrid.style.gap = '20px';
  605. outfitsGrid.style.padding = '20px';
  606. outfitsGrid.style.marginTop = '20px';
  607.  
  608. // Get outfit thumbnails
  609. const outfitIds = outfitsData.data.map(outfit => outfit.id).filter(id => id); // Filter out any null/undefined IDs
  610. if (outfitIds.length === 0) {
  611. dynamicContentDiv.innerHTML = '<p>No valid outfits found</p>';
  612. return;
  613. }
  614.  
  615. try {
  616. const thumbnailResponse = await fetch(`https://thumbnails.roblox.com/v1/users/outfits?userOutfitIds=${outfitIds.join(',')}&size=420x420&format=Png`);
  617. const thumbnailData = await thumbnailResponse.json();
  618.  
  619. // Create a map of outfit IDs to their thumbnails
  620. const thumbnailMap = new Map(thumbnailData.data.map(item => [item.targetId, item.imageUrl]));
  621.  
  622. // Create outfit cards
  623. outfitsData.data.forEach(outfit => {
  624. if (!outfit || !outfit.id) return; // Skip invalid outfits
  625.  
  626. const outfitCard = document.createElement('div');
  627. outfitCard.style.textAlign = 'center';
  628. outfitCard.style.border = '1px solid #ccc';
  629. outfitCard.style.borderRadius = '8px';
  630. outfitCard.style.padding = '10px';
  631. outfitCard.style.backgroundColor = '#ffffff';
  632. outfitCard.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)';
  633.  
  634. const thumbnailUrl = thumbnailMap.get(outfit.id) || 'placeholder-url';
  635.  
  636. outfitCard.innerHTML = `
  637. <img src="${thumbnailUrl}" alt="${outfit.name}" style="width: 200px; height: 200px; object-fit: contain; border-radius: 4px;">
  638. <h4 style="margin: 10px 0; color: #333;">${outfit.name}</h4>
  639. `;
  640. outfitsGrid.appendChild(outfitCard);
  641. });
  642.  
  643. // Clear previous content and add new grid
  644. dynamicContentDiv.innerHTML = '<h4>User Outfits:</h4>';
  645. dynamicContentDiv.appendChild(outfitsGrid);
  646. } catch (thumbnailError) {
  647. console.error('Error fetching thumbnails:', thumbnailError);
  648. dynamicContentDiv.innerHTML = '<p>Error loading outfit thumbnails</p>';
  649. }
  650. } catch (error) {
  651. console.error('Error fetching outfits:', error);
  652. dynamicContentDiv.innerHTML = '<p>Failed to fetch outfits</p>';
  653. displayMessage('Failed to fetch outfits', true);
  654. }
  655. };
  656.  
  657. // Current Assets Button
  658. const currentAssetsButton = document.createElement('button');
  659. currentAssetsButton.className = 'submit-button';
  660. currentAssetsButton.textContent = 'Show Current Assets';
  661. currentAssetsButton.onclick = async () => {
  662. try {
  663. const currentWearingResponse = await fetch(`https://avatar.roblox.com/v1/users/${userId}/currently-wearing`);
  664. const currentWearingData = await currentWearingResponse.json();
  665.  
  666. const assetsList = await Promise.all(currentWearingData.assetIds.map(async (assetId) => {
  667. const assetResponse = await fetch(`https://catalog.roblox.com/v1/catalog/items/${assetId}/details`);
  668. const assetData = await assetResponse.json();
  669. return `
  670. <li style="margin-bottom: 10px;">
  671. <div>${assetData.name || `Asset #${assetId}`}</div>
  672. <a href="https://www.roblox.com/catalog/${assetId}" target="_blank">View Asset</a>
  673. </li>
  674. `;
  675. }));
  676.  
  677. dynamicContentDiv.innerHTML = `
  678. <h4>Currently Wearing:</h4>
  679. <ul style="list-style: none; padding: 0;">${assetsList.join('') || '<li>No assets found</li>'}</ul>
  680. `;
  681. } catch (error) {
  682. displayMessage('Failed to fetch current assets', true);
  683. }
  684. };
  685.  
  686. // Add buttons to container
  687. buttonsDiv.appendChild(historyButton);
  688. buttonsDiv.appendChild(outfitsButton);
  689. buttonsDiv.appendChild(currentAssetsButton);
  690.  
  691. // Append everything to result container
  692. resultContainer.appendChild(imageContainer);
  693. resultContainer.appendChild(userInfoDiv);
  694. resultContainer.appendChild(buttonsDiv);
  695. resultContainer.appendChild(dynamicContentDiv);
  696. resultContainer.style.display = 'block';
  697.  
  698. displayMessage('User information fetched successfully!');
  699. } catch (error) {
  700. displayMessage(error.message, true);
  701. }
  702. };
  703. }
  704.  
  705. // Updated getOutfitAssets function
  706. async function getOutfitAssets(outfitId) {
  707. try {
  708. const response = await fetch(`https://catalog.roblox.com/v1/outfits/${outfitId}/get-outfit-details`);
  709. const data = await response.json();
  710.  
  711. if (!data.assetIds || data.assetIds.length === 0) {
  712. throw new Error('No assets found');
  713. }
  714.  
  715. const assetsList = await Promise.all(data.assetIds.map(async (assetId) => {
  716. try {
  717. const assetResponse = await fetch(`https://catalog.roblox.com/v1/catalog/items/${assetId}/details`);
  718. const assetData = await assetResponse.json();
  719. return `
  720. <li style="margin-bottom: 10px;">
  721. <div>${assetData.name || 'Unknown Asset'}</div>
  722. <a href="https://www.roblox.com/catalog/${assetId}" target="_blank">View Asset (ID: ${assetId})</a>
  723. </li>
  724. `;
  725. } catch (err) {
  726. return `
  727. <li style="margin-bottom: 10px;">
  728. <div>Asset ID: ${assetId}</div>
  729. <a href="https://www.roblox.com/catalog/${assetId}" target="_blank">View Asset</a>
  730. </li>
  731. `;
  732. }
  733. }));
  734.  
  735. const dynamicContentDiv = document.getElementById('dynamic-content');
  736. dynamicContentDiv.innerHTML = `
  737. <h4>Outfit Assets:</h4>
  738. <ul style="list-style: none; padding: 0;">${assetsList.join('') || '<li>No assets found</li>'}</ul>
  739. <button class="submit-button" onclick="document.querySelector('[data-action=\'Show User Outfits\']').click()">Back to Outfits</button>
  740. `;
  741. } catch (error) {
  742. console.error('Error fetching outfit assets:', error);
  743. displayMessage('Failed to fetch outfit assets: ' + error.message, true);
  744. }
  745. }
  746. async function initializeGroupInfo() {
  747. const mainWrapper = document.createElement('div');
  748. mainWrapper.className = 'main-content-wrapper';
  749. document.body.appendChild(mainWrapper);
  750.  
  751. const { container, input, button } = createBasicForm('Enter Group ID', 'Get Group Info');
  752. mainWrapper.appendChild(container);
  753.  
  754. const refreshContent = async (groupId) => {
  755. // Remove any existing result containers
  756. const existingResults = mainWrapper.querySelectorAll('.result-container');
  757. existingResults.forEach(result => result.remove());
  758.  
  759. try {
  760. // Fetch all group data in parallel
  761. const [
  762. groupResponse,
  763. membersResponse,
  764. iconResponse,
  765. rolesResponse,
  766. wallResponse,
  767. settingsResponse,
  768. socialLinksResponse,
  769. recentPosts
  770. ] = await Promise.all([
  771. fetch(`https://groups.roblox.com/v1/groups/${groupId}`),
  772. fetch(`https://groups.roblox.com/v1/groups/${groupId}/membership`),
  773. fetch(`https://thumbnails.roblox.com/v1/groups/icons?groupIds=${groupId}&size=420x420&format=Png`),
  774. fetch(`https://groups.roblox.com/v1/groups/${groupId}/roles`),
  775. fetch(`https://groups.roblox.com/v2/groups/${groupId}/wall/posts?limit=10&sortOrder=Desc`),
  776. fetch(`https://groups.roblox.com/v1/groups/${groupId}/settings`),
  777. fetch(`https://groups.roblox.com/v1/groups/${groupId}/social-links`),
  778. fetch(`https://groups.roblox.com/v2/groups/${groupId}/wall/posts?limit=5&sortOrder=Desc`)
  779. ]);
  780.  
  781. const [groupInfo, membersInfo, iconData, rolesInfo, wallData, settings, socialLinks, recentPostsData] = await Promise.all([
  782. groupResponse.json(),
  783. membersResponse.json(),
  784. iconResponse.json(),
  785. rolesResponse.json(),
  786. wallResponse.json(),
  787. settingsResponse.json(),
  788. socialLinksResponse.json(),
  789. recentPosts.json()
  790. ]);
  791.  
  792. const resultContainer = createResultContainer();
  793.  
  794. // Create image container for group icon
  795. const imageContainer = document.createElement('div');
  796. imageContainer.className = 'image-container';
  797.  
  798. // Display group icon
  799. if (iconData.data && iconData.data[0]) {
  800. const iconDiv = document.createElement('div');
  801. iconDiv.className = 'image-item';
  802.  
  803. const iconImg = document.createElement('img');
  804. iconImg.src = iconData.data[0].imageUrl;
  805. iconImg.alt = 'Group Icon';
  806.  
  807. const downloadBtn = document.createElement('button');
  808. downloadBtn.className = 'submit-button';
  809. downloadBtn.textContent = 'Download Group Icon';
  810. downloadBtn.onclick = () => GM_download({
  811. url: iconData.data[0].imageUrl,
  812. name: `group_${groupId}_icon.png`
  813. });
  814.  
  815. iconDiv.appendChild(iconImg);
  816. iconDiv.appendChild(downloadBtn);
  817. imageContainer.appendChild(iconDiv);
  818. }
  819.  
  820. // Calculate group age in days
  821. const groupAge = Math.floor((new Date() - new Date(groupInfo.created)) / (1000 * 60 * 60 * 24));
  822.  
  823. // Format roles with more details
  824. const rolesHtml = rolesInfo.roles.map(role => `
  825. <li style="margin-bottom: 10px; padding: 5px; border: 1px solid #eee; border-radius: 4px;">
  826. <strong>${role.name}</strong><br>
  827. Members: ${role.memberCount}<br>
  828. Rank: ${role.rank}<br>
  829. ${role.description ? `Description: ${role.description}<br>` : ''}
  830. </li>
  831. `).join('');
  832.  
  833. // Format recent posts
  834. const recentPostsHtml = recentPostsData.data ? recentPostsData.data.map(post => `
  835. <li style="margin-bottom: 15px; padding: 10px; border: 1px solid #eee; border-radius: 4px;">
  836. <strong>${post.poster.username}</strong> - ${new Date(post.created).toLocaleString()}<br>
  837. ${post.body}
  838. </li>
  839. `).join('') : '';
  840.  
  841. // Format social links
  842. const socialLinksHtml = socialLinks.data ? socialLinks.data.map(link => `
  843. <li><strong>${link.title}</strong>: <a href="${link.url}" target="_blank">${link.url}</a></li>
  844. `).join('') : '';
  845.  
  846. // Display group information
  847. const infoDiv = document.createElement('div');
  848. infoDiv.className = 'info-text';
  849. infoDiv.innerHTML = `
  850. <h2 style="color: #333; margin-bottom: 20px;">${groupInfo.name}</h2>
  851.  
  852. <div style="background: #f5f5f5; padding: 15px; border-radius: 8px; margin-bottom: 20px;">
  853. <h3>Basic Information</h3>
  854. <p><strong>Group ID:</strong> ${groupId}</p>
  855. <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>
  856. <p><strong>Created:</strong> ${new Date(groupInfo.created).toLocaleString()} (${groupAge} days ago)</p>
  857. <p><strong>Member Count:</strong> ${membersInfo.memberCount?.toLocaleString() || 0}</p>
  858. <p><strong>Description:</strong> ${groupInfo.description || 'No description'}</p>
  859. </div>
  860.  
  861. <div style="background: #f5f5f5; padding: 15px; border-radius: 8px; margin-bottom: 20px;">
  862. <h3>Group Settings</h3>
  863. <p><strong>Public Entry:</strong> ${groupInfo.publicEntryAllowed ? 'Yes' : 'No'}</p>
  864. <p><strong>Group Status:</strong> ${groupInfo.isLocked ? 'Locked' : 'Active'}</p>
  865. <p><strong>Membership Type:</strong> ${groupInfo.publicEntryAllowed ? 'Anyone can join' : 'Approval required'}</p>
  866. <p><strong>Verified:</strong> ${groupInfo.hasVerifiedBadge ? 'Yes' : 'No'}</p>
  867. </div>
  868.  
  869. ${socialLinks.data && socialLinks.data.length > 0 ? `
  870. <div style="background: #f5f5f5; padding: 15px; border-radius: 8px; margin-bottom: 20px;">
  871. <h3>Social Links</h3>
  872. <ul style="list-style-type: none; padding-left: 0;">
  873. ${socialLinksHtml}
  874. </ul>
  875. </div>
  876. ` : ''}
  877.  
  878. <div style="background: #f5f5f5; padding: 15px; border-radius: 8px; margin-bottom: 20px;">
  879. <h3>Quick Links</h3>
  880. <p><a href="https://www.roblox.com/groups/${groupId}" target="_blank">View Group Page</a></p>
  881. <p><a href="https://www.roblox.com/groups/${groupId}/membership" target="_blank">View Members</a></p>
  882. <p><a href="https://www.roblox.com/abusereport/group?id=${groupId}" target="_blank">Report Group</a></p>
  883. </div>
  884.  
  885. <div style="background: #f5f5f5; padding: 15px; border-radius: 8px; margin-bottom: 20px;">
  886. <h3>Roles (${rolesInfo.roles.length})</h3>
  887. <ul style="list-style-type: none; padding-left: 0;">
  888. ${rolesHtml}
  889. </ul>
  890. </div>
  891.  
  892. ${recentPostsData.data && recentPostsData.data.length > 0 ? `
  893. <div style="background: #f5f5f5; padding: 15px; border-radius: 8px; margin-bottom: 20px;">
  894. <h3>Recent Wall Posts</h3>
  895. <ul style="list-style-type: none; padding-left: 0;">
  896. ${recentPostsHtml}
  897. </ul>
  898. </div>
  899. ` : ''}
  900. `;
  901.  
  902. resultContainer.appendChild(imageContainer);
  903. resultContainer.appendChild(infoDiv);
  904. mainWrapper.appendChild(resultContainer);
  905. displayMessage('Group information fetched successfully!');
  906. } catch (error) {
  907. displayMessage(error.message, true);
  908. }
  909. };
  910.  
  911. button.onclick = async () => {
  912. const groupId = input.value.trim();
  913. if (!groupId) {
  914. displayMessage('Please enter a group ID', true);
  915. return;
  916. }
  917. await refreshContent(groupId);
  918. };
  919. }
  920.  
  921. async function initializeCodeRedemption() {
  922. const mainWrapper = document.createElement('div');
  923. mainWrapper.className = 'main-content-wrapper';
  924. document.body.appendChild(mainWrapper);
  925.  
  926. const { container, input, button } = createBasicForm('Enter Code', 'Redeem Code');
  927.  
  928. const autoRedeemButton = document.createElement('button');
  929. autoRedeemButton.className = 'submit-button';
  930. autoRedeemButton.style.backgroundColor = '#ff4444';
  931. autoRedeemButton.textContent = 'Auto-Redeem All Active Codes';
  932. container.appendChild(autoRedeemButton);
  933.  
  934. const showCodesButton = document.createElement('button');
  935. showCodesButton.className = 'submit-button';
  936. showCodesButton.style.backgroundColor = '#4a90e2'; // Blue color to distinguish it
  937. showCodesButton.textContent = 'Show Available Codes';
  938. container.appendChild(showCodesButton);
  939.  
  940. // Known codes list with additional information
  941. const availableCodes = [
  942. {
  943. code: 'SPIDERCOLA',
  944. reward: 'Spider Cola Shoulder Pet',
  945. expires: 'No expiration'
  946. },
  947. {
  948. code: 'TWEETROBLOX',
  949. reward: 'The Bird Says Shoulder Pet',
  950. expires: 'No expiration'
  951. },
  952. {
  953. code: 'ROBLOXEDU2023',
  954. reward: 'School Backpack Accessory',
  955. expires: 'No expiration'
  956. },
  957. {
  958. code: 'AMAZONFRIEND2024',
  959. reward: 'Amazon Prime Gaming Reward',
  960. expires: 'March 31, 2024'
  961. },
  962. {
  963. code: 'BRICKMASTER2024',
  964. reward: 'Special Avatar Item',
  965. expires: 'December 31, 2024'
  966. },
  967. {
  968. code: 'ROADTO100K',
  969. reward: 'Special Avatar Accessory',
  970. expires: 'No expiration'
  971. },
  972. {
  973. code: 'VANITYXBOY',
  974. reward: 'Vanity Backpack',
  975. expires: 'No expiration'
  976. },
  977. {
  978. code: 'SHINYJOKER',
  979. reward: 'Shiny Joker Mask',
  980. expires: 'No expiration'
  981. },
  982. {
  983. code: 'ICYGLOW',
  984. reward: 'Glowing Ice Crown',
  985. expires: 'No expiration'
  986. },
  987. {
  988. code: 'DARKBLOOD',
  989. reward: 'Dark Blood Cape',
  990. expires: 'No expiration'
  991. },
  992. {
  993. code: 'BOOMEXPLOSION',
  994. reward: 'Boom Explosion Mask',
  995. expires: 'No expiration'
  996. },
  997. {
  998. code: 'BLOXYPARTY',
  999. reward: 'Bloxyparty Hat',
  1000. expires: 'No expiration'
  1001. },
  1002. {
  1003. code: 'WATERFALL2024',
  1004. reward: 'Waterfall Back Bling',
  1005. expires: 'No expiration'
  1006. },
  1007. {
  1008. code: 'MAYDAY2024',
  1009. reward: 'May Day Hat',
  1010. expires: 'May 1, 2024'
  1011. },
  1012. {
  1013. code: 'PARTYBEAN2024',
  1014. reward: 'Party Bean Hat',
  1015. expires: 'July 1, 2024'
  1016. }
  1017. ];
  1018.  
  1019.  
  1020. // Show Available Codes functionality
  1021. showCodesButton.onclick = () => {
  1022. resultContainer.innerHTML = '<h3>Available Roblox Codes:</h3>';
  1023. const codesTable = document.createElement('div');
  1024. codesTable.style.padding = '15px';
  1025. codesTable.style.backgroundColor = '#ffffff';
  1026. codesTable.style.borderRadius = '8px';
  1027. codesTable.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)';
  1028. codesTable.style.margin = '10px 0';
  1029.  
  1030. // Create table header
  1031. codesTable.innerHTML = `
  1032. <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;">
  1033. <div>Code</div>
  1034. <div>Reward</div>
  1035. <div>Expires</div>
  1036. </div>
  1037. `;
  1038.  
  1039. // Add each code to the table
  1040. availableCodes.forEach(codeInfo => {
  1041. const codeRow = document.createElement('div');
  1042. codeRow.style.display = 'grid';
  1043. codeRow.style.gridTemplateColumns = '1fr 2fr 1fr';
  1044. codeRow.style.gap = '10px';
  1045. codeRow.style.padding = '10px';
  1046. codeRow.style.borderBottom = '1px solid #eee';
  1047. codeRow.innerHTML = `
  1048. <div style="font-family: monospace; font-weight: bold;">${codeInfo.code}</div>
  1049. <div>${codeInfo.reward}</div>
  1050. <div>${codeInfo.expires}</div>
  1051. `;
  1052. codesTable.appendChild(codeRow);
  1053. });
  1054.  
  1055. resultContainer.appendChild(codesTable);
  1056.  
  1057. // Add note about codes
  1058. const note = document.createElement('p');
  1059. note.style.marginTop = '15px';
  1060. note.style.padding = '10px';
  1061. note.style.backgroundColor = '#fff3cd';
  1062. note.style.borderRadius = '4px';
  1063. note.style.color = '#856404';
  1064. note.innerHTML = '⚠️ Note: These codes are current as of our last update. Some codes might expire without notice.';
  1065. resultContainer.appendChild(note);
  1066. };
  1067.  
  1068. mainWrapper.appendChild(container);
  1069.  
  1070. const resultContainer = createResultContainer();
  1071. mainWrapper.appendChild(resultContainer);
  1072.  
  1073. async function getXCSRFToken() {
  1074. return new Promise((resolve, reject) => {
  1075. GM_xmlhttpRequest({
  1076. method: "POST",
  1077. url: "https://auth.roblox.com/v2/logout",
  1078. headers: {
  1079. "Content-Type": "application/json",
  1080. },
  1081. withCredentials: true,
  1082. onload: function(response) {
  1083. const token = response.responseHeaders.match(/x-csrf-token: (.+)/i)?.[1];
  1084. resolve(token);
  1085. },
  1086. onerror: function(error) {
  1087. reject(new Error('Failed to get CSRF token'));
  1088. }
  1089. });
  1090. });
  1091. }
  1092.  
  1093. async function redeemCode(code, token) {
  1094. return new Promise((resolve, reject) => {
  1095. GM_xmlhttpRequest({
  1096. method: "POST",
  1097. url: "https://promocodes.roblox.com/v1/promocodes/redeem",
  1098. headers: {
  1099. "Content-Type": "application/json",
  1100. "X-CSRF-TOKEN": token
  1101. },
  1102. data: JSON.stringify({ code: code }),
  1103. withCredentials: true,
  1104. onload: function(response) {
  1105. try {
  1106. const result = JSON.parse(response.responseText);
  1107. if (response.status === 200) {
  1108. resolve({
  1109. code: code,
  1110. success: true,
  1111. message: result.message || 'Code redeemed successfully'
  1112. });
  1113. } else {
  1114. resolve({
  1115. code: code,
  1116. success: false,
  1117. message: result.message || result.errors?.[0]?.message || 'Failed to redeem code'
  1118. });
  1119. }
  1120. } catch (e) {
  1121. resolve({
  1122. code: code,
  1123. success: false,
  1124. message: 'Invalid response from server'
  1125. });
  1126. }
  1127. },
  1128. onerror: function(error) {
  1129. resolve({
  1130. code: code,
  1131. success: false,
  1132. message: 'Network request failed'
  1133. });
  1134. }
  1135. });
  1136. });
  1137. }
  1138.  
  1139. // Known codes list
  1140. const knownCodes = [
  1141. 'SPIDERCOLA', 'TWEETROBLOX', 'ROBLOXEDU2023',
  1142. 'AMAZONFRIEND2024', 'BRICKMASTER2024', 'ROADTO100K'
  1143. ];
  1144.  
  1145. // Auto-redeem functionality
  1146. autoRedeemButton.onclick = async () => {
  1147. try {
  1148. resultContainer.innerHTML = '<h3>Auto-Redeem Results:</h3>';
  1149. const resultsList = document.createElement('ul');
  1150. resultsList.style.listStyle = 'none';
  1151. resultsList.style.padding = '10px';
  1152.  
  1153. // Get CSRF token once before starting
  1154. const token = await getXCSRFToken();
  1155. if (!token) {
  1156. throw new Error('Failed to get authentication token. Please ensure you are logged in.');
  1157. }
  1158.  
  1159. for (const code of knownCodes) {
  1160. // Add delay between attempts
  1161. if (knownCodes.indexOf(code) > 0) {
  1162. await new Promise(resolve => setTimeout(resolve, 2000));
  1163. }
  1164.  
  1165. const result = await redeemCode(code, token);
  1166. const listItem = document.createElement('li');
  1167. listItem.style.padding = '10px';
  1168. listItem.style.margin = '5px 0';
  1169. listItem.style.borderRadius = '4px';
  1170. listItem.style.backgroundColor = result.success ? '#e8f5e9' : '#ffebee';
  1171. listItem.innerHTML = `
  1172. <strong>${code}:</strong> ${result.success ? '✅' : '❌'}
  1173. ${result.message}
  1174. `;
  1175. resultsList.appendChild(listItem);
  1176. resultContainer.appendChild(resultsList);
  1177. }
  1178. } catch (error) {
  1179. resultContainer.innerHTML = `
  1180. <div class="error-message" style="padding: 15px; margin-top: 20px; border-radius: 4px;">
  1181. <h3>❌ Error</h3>
  1182. <p>${error.message}</p>
  1183. </div>
  1184. `;
  1185. }
  1186. };
  1187.  
  1188. // Single code redemption
  1189. button.onclick = async () => {
  1190. try {
  1191. const code = input.value.trim();
  1192. if (!code) {
  1193. displayMessage('Please enter a code', true);
  1194. return;
  1195. }
  1196.  
  1197. const token = await getXCSRFToken();
  1198. if (!token) {
  1199. throw new Error('Failed to get authentication token. Please ensure you are logged in.');
  1200. }
  1201.  
  1202. const result = await redeemCode(code, token);
  1203. resultContainer.innerHTML = `
  1204. <div class="${result.success ? 'success-message' : 'error-message'}"
  1205. style="padding: 15px; margin-top: 20px; border-radius: 4px;">
  1206. <h3>${result.success ? '✅ Success!' : '❌ Error'}</h3>
  1207. <p>${result.message}</p>
  1208. </div>
  1209. `;
  1210. } catch (error) {
  1211. resultContainer.innerHTML = `
  1212. <div class="error-message" style="padding: 15px; margin-top: 20px; border-radius: 4px;">
  1213. <h3>❌ Error</h3>
  1214. <p>${error.message}</p>
  1215. </div>
  1216. `;
  1217. }
  1218. };
  1219. }
  1220.  
  1221. // Add this new function before the createPanel() function
  1222. async function initializeRobloxInstallers() {
  1223. const mainWrapper = document.createElement('div');
  1224. mainWrapper.className = 'main-content-wrapper';
  1225. document.body.appendChild(mainWrapper);
  1226.  
  1227. const container = document.createElement('div');
  1228. container.className = 'form-container';
  1229. mainWrapper.appendChild(container);
  1230.  
  1231. const title = document.createElement('h2');
  1232. title.textContent = 'Roblox Installers';
  1233. container.appendChild(title);
  1234.  
  1235. // Define installer categories and their links
  1236. const installers = {
  1237. 'Current Installers': [
  1238. {
  1239. name: 'Roblox Player (Windows)',
  1240. url: 'https://setup.rbxcdn.com/version-2e66f0f5ee7944ce/RobloxPlayerLauncher.exe',
  1241. icon: '🎮'
  1242. },
  1243. {
  1244. name: 'Roblox Player (Mac)',
  1245. url: 'https://setup.rbxcdn.com/mac/version-2e66f0f5ee7944ce/RobloxPlayer.dmg',
  1246. icon: '🎮'
  1247. },
  1248. {
  1249. name: 'Roblox Studio (Windows)',
  1250. url: 'https://setup.rbxcdn.com/version-2e66f0f5ee7944ce/RobloxStudioLauncherBeta.exe',
  1251. icon: '🛠️'
  1252. },
  1253. {
  1254. name: 'Roblox Studio (Mac)',
  1255. url: 'https://setup.rbxcdn.com/mac/version-2e66f0f5ee7944ce/RobloxStudio.dmg',
  1256. icon: '🛠️'
  1257. }
  1258. ],
  1259. 'Chinese Version': [
  1260. {
  1261. name: 'Roblox China Player',
  1262. url: 'https://setup.rbxcdn.cn/version-2e66f0f5ee7944ce/RobloxPlayerLauncher.exe',
  1263. icon: '🇨🇳'
  1264. },
  1265. {
  1266. name: 'Roblox China Studio',
  1267. url: 'https://setup.rbxcdn.cn/version-2e66f0f5ee7944ce/RobloxStudioLauncherBeta.exe',
  1268. icon: '🇨🇳'
  1269. }
  1270. ],
  1271. 'Legacy Versions': [
  1272. {
  1273. name: 'Roblox 2016 Player',
  1274. url: 'https://setup.rbxcdn.com/version-30b898b26e4d4b8b/RobloxPlayerLauncher.exe',
  1275. icon: '📜'
  1276. },
  1277. {
  1278. name: 'Roblox 2017 Player',
  1279. url: 'https://setup.rbxcdn.com/version-47c9e5c1190a4d7b/RobloxPlayerLauncher.exe',
  1280. icon: '📜'
  1281. },
  1282. {
  1283. name: 'Roblox 2018 Player',
  1284. url: 'https://setup.rbxcdn.com/version-60622c8b13104d53/RobloxPlayerLauncher.exe',
  1285. icon: '📜'
  1286. }
  1287. ]
  1288. };
  1289.  
  1290. // Create sections for each category
  1291. Object.entries(installers).forEach(([category, items]) => {
  1292. const section = document.createElement('div');
  1293. section.style.marginBottom = '20px';
  1294. section.style.backgroundColor = '#ffffff';
  1295. section.style.padding = '15px';
  1296. section.style.borderRadius = '8px';
  1297. section.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)';
  1298.  
  1299. const categoryTitle = document.createElement('h3');
  1300. categoryTitle.textContent = category;
  1301. categoryTitle.style.marginBottom = '10px';
  1302. section.appendChild(categoryTitle);
  1303.  
  1304. items.forEach(item => {
  1305. const button = document.createElement('button');
  1306. button.className = 'panel-button';
  1307. button.style.marginBottom = '10px';
  1308. button.innerHTML = `${item.icon} ${item.name}`;
  1309. button.onclick = () => {
  1310. window.location.href = item.url;
  1311. };
  1312. section.appendChild(button);
  1313. });
  1314.  
  1315. container.appendChild(section);
  1316. });
  1317.  
  1318. // Add warning message
  1319. const warning = document.createElement('div');
  1320. warning.style.backgroundColor = '#fff3cd';
  1321. warning.style.color = '#856404';
  1322. warning.style.padding = '15px';
  1323. warning.style.borderRadius = '8px';
  1324. warning.style.marginTop = '20px';
  1325. warning.innerHTML = `
  1326. <h4>⚠️ Important Notes:</h4>
  1327. <ul style="margin-left: 20px;">
  1328. <li>Legacy versions may not work with current Roblox services</li>
  1329. <li>Chinese version requires a Chinese Roblox account</li>
  1330. <li>Some versions might require specific operating systems</li>
  1331. <li>Always download from trusted sources</li>
  1332. </ul>
  1333. `;
  1334. container.appendChild(warning);
  1335. }
  1336.  
  1337. // Panel Implementation
  1338. function createPanel() {
  1339. const mainWrapper = document.createElement('div');
  1340. mainWrapper.className = 'main-content-wrapper';
  1341. document.body.appendChild(mainWrapper);
  1342.  
  1343. const container = document.createElement('div');
  1344. container.className = 'form-container';
  1345.  
  1346. const title = document.createElement('h2');
  1347. title.textContent = 'Roblox Multi-Feature Tool';
  1348. container.appendChild(title);
  1349.  
  1350. const buttons = [
  1351. { text: 'Game Information', url: '/getgameinfo' },
  1352. { text: 'Badge Information', url: '/getbadgeinfo' },
  1353. { text: 'User Information', url: '/getuserinfo' },
  1354. { text: 'Group Information', url: '/getgroupinfo' },
  1355. { text: 'Code Redemption', url: '/redeemcode' },
  1356. { text: 'Roblox Installers', url: '/installers' }
  1357. ];
  1358.  
  1359. buttons.forEach(button => {
  1360. const btn = document.createElement('button');
  1361. btn.className = 'panel-button';
  1362. btn.textContent = button.text;
  1363. btn.onclick = () => window.location.href = 'https://www.roblox.com' + button.url;
  1364. container.appendChild(btn);
  1365. });
  1366.  
  1367. mainWrapper.appendChild(container);
  1368. }
  1369.  
  1370. // Initialize based on current page
  1371. const currentPath = window.location.pathname;
  1372. switch(currentPath) {
  1373. case '/userpanel':
  1374. createPanel();
  1375. break;
  1376. case '/getgameinfo':
  1377. initializeGameInfo();
  1378. break;
  1379. case '/getbadgeinfo':
  1380. initializeBadgeInfo();
  1381. break;
  1382. case '/getuserinfo':
  1383. initializeUserInfo();
  1384. break;
  1385. case '/getgroupinfo':
  1386. initializeGroupInfo();
  1387. break;
  1388. case '/redeemcode':
  1389. initializeCodeRedemption();
  1390. break;
  1391. case '/installers':
  1392. initializeRobloxInstallers();
  1393. break;
  1394. }
  1395. })();