punchmade dev - punch queen 2
Ovu skriptu ne treba izravno instalirati. To je biblioteka za druge skripte koje se uključuju u meta direktivu // @require https://update.greatest.deepsurf.us/scripts/574644/1803510/crazy%20nigga%202%20recode%20off%20222.js
// ==UserScript== // @name crazy nigga 2 recode off 222 // @namespace Violentmonkey Scripts // @match *://*.roblox.com/* // @match *://*.rolimons.com/* // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_listValues // @version 5.0 // @author davvid_butler / t.me/larp4cheap // @description punchmade dev - punch queen 2 // @run-at document-start // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/lil-gui.umd.min.js // ==/UserScript== (function () { 'use strict'; let _earlyFields = null; let _earlyLocked = false; let _earlyPendingMeta = null; function _earlyApply(meta) { if (!_earlyFields || !meta) return; for (const [key, val] of Object.entries(_earlyFields)) { try { Object.defineProperty(meta.dataset, key, { get() { return val; }, set() {}, configurable: true, enumerable: true }); } catch { meta.dataset[key] = val; } } meta._spoofLocked = true; _earlyLocked = true; Node.prototype.appendChild = _origAppendChild; Node.prototype.insertBefore = _origInsertBefore; Element.prototype.setAttribute = _origSetAttribute; } function _earlyCheck(node) { if (_earlyLocked) return; if (!node || node.nodeType !== 1) return; const meta = (node.nodeName === 'META' && node.getAttribute('name') === 'user-data') ? node : node.querySelector?.('meta[name="user-data"]'); if (!meta) return; if (_earlyFields) { _earlyApply(meta); } else { _earlyPendingMeta = meta; } } const _origAppendChild = Node.prototype.appendChild; Node.prototype.appendChild = function(node) { const result = _origAppendChild.call(this, node); if (!_earlyLocked) _earlyCheck(node); return result; }; const _origInsertBefore = Node.prototype.insertBefore; Node.prototype.insertBefore = function(node, ref) { const result = _origInsertBefore.call(this, node, ref); if (!_earlyLocked) _earlyCheck(node); return result; }; const _origSetAttribute = Element.prototype.setAttribute; Element.prototype.setAttribute = function(name, value) { _origSetAttribute.call(this, name, value); if (!_earlyLocked && this.nodeName === 'META' && (name === 'name' || name === 'data-userid') && this.getAttribute('name') === 'user-data') { _earlyCheck(this); } }; function _earlyArm(fields) { _earlyFields = fields; if (_earlyPendingMeta && !_earlyLocked) { _earlyApply(_earlyPendingMeta); _earlyPendingMeta = null; } if (!_earlyLocked) { const existing = document.querySelector('meta[name="user-data"]'); if (existing) _earlyApply(existing); } } let isRenderingInProgress = false; let renderingStartTime = null; const MAX_RENDER_WAIT_MS = 35000; const defaultConfig = { enabled: false, spoofUserId: '', fetchedData: null, profile: { name: '', displayName: '', isPremium: true, isVerified: false, isRobloxAdmin: false, description: '', joinDate: '', friendsCount: 0, followersCount: 0, followingsCount: 0 }, robux: { enabled: true, amount: 999999999, buyableHeadless: false }, premium: { subscriptionType: 'RobloxPremium2200', createdAt: null, renewedSince: null }, usd: { enabled: true, amount: 100.00, currencyCode: 'USD', giftCards: [] }, transactions: { salesTotal: 0, purchasesTotal: 0, affiliateSalesTotal: 0, groupPayoutsTotal: 0, currencyPurchasesTotal: 0, premiumStipendsTotal: 0, tradeSystemEarningsTotal: 0, tradeSystemCostsTotal: 0, premiumPayoutsTotal: 0, groupPremiumPayoutsTotal: 0, adSpendTotal: 0, developerExchangeTotal: 0, pendingRobuxTotal: 0, incomingRobuxTotal: 0, outgoingRobuxTotal: 0, individualToGroupTotal: 0, csAdjustmentTotal: 0, adsRevsharePayoutsTotal: 0, groupAdsRevsharePayoutsTotal: 0, subscriptionsRevshareTotal: 0, groupSubscriptionsRevshareTotal: 0, subscriptionsRevshareOutgoingTotal: 0, groupSubscriptionsRevshareOutgoingTotal: 0, publishingAdvanceRebatesTotal: 0, affiliatePayoutTotal: 0, licensingPaymentTotal: 0, licensingPaymentClawbackOutgoingTotal: 0 }, marketplace: { freePurchases: true }, resaleData: {}, purchaseTransactions: [], robuxPurchases: [], fakeFriendRequests: [], inventory: { purchasedItems: [], limitedItems: [], useRealInventory: true }, avatar: { outfit: null, avatarData: null, customThumbnail3d: null, thumbnailGeneratedAt: null, lastRenderedOutfit: null }, users: { spoofedUsers: [] }, account: { emailDomain: '@gmail.com', emailUsername: 'noreply', emailVerified: true, canTrade: true, phoneFeatureEnabled: true, twoStepEnabled: false, accountAge: 365, isPremium: true, canHideInventory: true, userAbove13: true, isEmailOnFile: true, hasValidPasswordSet: true, changeUsernameEnabled: true, robuxRemainingForUsernameChange: 1000, countryName: 'United States' }, perUserConfigs: {}, uiSettings: { backgroundUrl: '', backgroundEnabled: false, backgroundOpacity: 0.18 } }; let config; try { const saved = GM_getValue('roblox_spoofer_config', null); config = saved || defaultConfig; for (const key of ['profile','robux','premium','usd','transactions','marketplace','inventory','avatar','users','account']) { config[key] = { ...defaultConfig[key], ...(config[key] || {}) }; } if (typeof config.resaleData !== 'object' || Array.isArray(config.resaleData)) config.resaleData = {}; if (!Array.isArray(config.purchaseTransactions)) config.purchaseTransactions = []; if (!Array.isArray(config.robuxPurchases)) config.robuxPurchases = []; if (!Array.isArray(config.fakeFriendRequests)) config.fakeFriendRequests = []; if (!config.uiSettings || typeof config.uiSettings !== 'object') config.uiSettings = { backgroundUrl: '', backgroundEnabled: false, backgroundOpacity: 0.18 }; config.uiSettings = { backgroundUrl: '', backgroundEnabled: false, backgroundOpacity: 0.18, ...config.uiSettings }; if (!Array.isArray(config.usd.giftCards)) config.usd.giftCards = []; config.perUserConfigs = config.perUserConfigs || {}; } catch { config = defaultConfig; } const saveConfig = () => GM_setValue('roblox_spoofer_config', config); if (config.enabled && config.spoofUserId) { const p = config.profile; const fd = config.fetchedData; _earlyArm({ userid: config.spoofUserId, name: (p?.name) || fd?.name || '', displayname: (p?.displayName) || fd?.displayName || '', ispremiumuser: String(p?.isPremium ?? true), hasverifiedbadge: String(p?.isVerified ?? false), isunder13: 'false', created: (p?.joinDate) || fd?.created || '', }); } let _cachedUid = config.spoofUserId || null; const USER_SECTIONS = ['robux', 'usd', 'premium', 'account', 'inventory', 'avatar', 'purchaseTransactions', 'robuxPurchases', 'fakeFriendRequests']; const userKey = (uid, sec) => `roblox_spoofer_user_${uid}_${sec}`; const savePerUserData = (uid, sec, data) => uid && GM_setValue(userKey(uid, sec), data); const loadPerUserData = (uid, sec, defaults) => { if (!uid) return defaults; const stored = GM_getValue(userKey(uid, sec), null); return stored ? { ...defaults, ...stored } : defaults; }; const deletePerUserData = uid => { if (!uid) return; for (const sec of USER_SECTIONS) GM_deleteValue(userKey(uid, sec)); }; const getAllStoredUserIds = () => { const ids = new Set(); for (const k of GM_listValues()) { const m = k.match(/^roblox_spoofer_user_(\d+)_/); if (m) ids.add(m[1]); } return [...ids]; }; const getUsersList = () => GM_getValue('roblox_spoofer_users_list', []); const saveUsersList = users => { GM_setValue('roblox_spoofer_users_list', users); if (config?.users) config.users.spoofedUsers = users; }; function saveCurrentUserConfig() { const uid = config.spoofUserId || getCurrentUserId(); if (!uid) return; savePerUserData(uid, 'robux', config.robux); savePerUserData(uid, 'usd', { enabled: config.usd.enabled, amount: config.usd.amount }); savePerUserData(uid, 'premium', config.premium); savePerUserData(uid, 'account', config.account); savePerUserData(uid, 'inventory', { purchasedItems: config.inventory.purchasedItems, limitedItems: config.inventory.limitedItems }); savePerUserData(uid, 'avatar', config.avatar); saveConfig(); } const saveAvatarData = () => { const uid = config.spoofUserId || getCurrentUserId(); if (uid) savePerUserData(uid, 'avatar', config.avatar); saveConfig(); }; function loadUserConfig(userId) { if (!userId) return; config.robux = { ...defaultConfig.robux, ...loadPerUserData(userId, 'robux', defaultConfig.robux) }; config.premium = { ...defaultConfig.premium, ...loadPerUserData(userId, 'premium', defaultConfig.premium) }; config.account = { ...defaultConfig.account, ...loadPerUserData(userId, 'account', defaultConfig.account) }; const savedUsd = loadPerUserData(userId, 'usd', { enabled: defaultConfig.usd.enabled, amount: defaultConfig.usd.amount }); config.usd.enabled = savedUsd.enabled; config.usd.amount = savedUsd.amount; config.avatar = { ...defaultConfig.avatar, ...loadPerUserData(userId, 'avatar', defaultConfig.avatar) }; const savedInv = loadPerUserData(userId, 'inventory', { purchasedItems: [], limitedItems: [] }); config.inventory.purchasedItems = Array.isArray(savedInv.purchasedItems) ? savedInv.purchasedItems : []; config.inventory.limitedItems = Array.isArray(savedInv.limitedItems) ? savedInv.limitedItems : []; const legacy = config.perUserConfigs?.[userId]; if (legacy) { if (legacy.profile) config.profile = { ...defaultConfig.profile, ...legacy.profile }; if (legacy.transactions) config.transactions = { ...defaultConfig.transactions, ...legacy.transactions }; if (legacy.avatar) config.avatar = { ...defaultConfig.avatar, ...legacy.avatar }; if (legacy.robux) savePerUserData(userId, 'robux', legacy.robux); if (legacy.account) savePerUserData(userId, 'account', legacy.account); if (legacy.inventory) savePerUserData(userId, 'inventory', legacy.inventory); delete config.perUserConfigs[userId]; saveConfig(); } } let _cachedMeta = null; function getCurrentUserId() { const meta = _cachedMeta || (_cachedMeta = document.querySelector('meta[name="user-data"]')); if (meta?.dataset.userid) return meta.dataset.userid; return unsafeWindow.Roblox?.CurrentUser?.userId?.toString() || null; } const getTargetUserId = () => config.spoofUserId || getCurrentUserId(); function getEffectiveProfile() { const targetUserId = getTargetUserId(); if (!targetUserId) return null; const fd = config.fetchedData; const p = config.profile; const base = { isPremium: p.isPremium, isVerified: p.isVerified, isRobloxAdmin: p.isRobloxAdmin, friendsCount: p.friendsCount, followersCount: p.followersCount, followingsCount: p.followingsCount, }; if (config.spoofUserId && fd && fd.id === config.spoofUserId) { return { ...base, id: fd.id, name: p.name || fd.name, displayName: p.displayName || fd.displayName, description: p.description || fd.description, created: p.joinDate || fd.created }; } return { ...base, id: targetUserId, name: p.name, displayName: p.displayName, description: p.description, created: p.joinDate }; } function applyFetchedUserData(data) { config.fetchedData = { id: data.id.toString(), name: data.name, displayName: data.displayName, hasVerifiedBadge: data.hasVerifiedBadge || false, created: data.created, description: data.description || '' }; const p = config.profile; if (!p.name) p.name = data.name; if (!p.displayName) p.displayName = data.displayName; p.isVerified = data.hasVerifiedBadge || false; if (!p.description) p.description = data.description || ''; if (!p.joinDate) p.joinDate = data.created; saveConfig(); _cachedUid = data.id.toString(); GM_setValue('spoofed_user_id', data.id.toString()); GM_setValue('spoofed_user_name', data.name); GM_setValue('spoofed_user_display_name', data.displayName); if (config.enabled) { _earlyArm({ userid: data.id.toString(), name: config.profile.name || data.name, displayname: config.profile.displayName || data.displayName, ispremiumuser: String(config.profile.isPremium ?? true), hasverifiedbadge: String(data.hasVerifiedBadge ?? false), isunder13: 'false', created: config.profile.joinDate || data.created || '', }); } if (typeof interceptMetaTag._refresh === 'function') interceptMetaTag._refresh(); if (typeof startTextReplacement._sweep === 'function') startTextReplacement._sweep(); else if (config.enabled) startTextReplacement(); } async function fetchUserData(userId) { userId = userId || getCurrentUserId(); if (!userId) throw new Error('No user ID available'); const res = await fetch(`https://users.roblox.com/v1/users/${userId}`); if (!res.ok) throw new Error('Failed to fetch user data'); applyFetchedUserData(await res.json()); return config.fetchedData; } async function switchToUser(userId) { saveCurrentUserConfig(); const res = await fetch(`https://users.roblox.com/v1/users/${userId}`); if (!res.ok) throw new Error('Failed to fetch user data'); const data = await res.json(); config.spoofUserId = userId.toString(); applyFetchedUserData(data); loadUserConfig(userId.toString()); return data; } async function addSpoofedUser(userId) { const users = getUsersList(); const existing = users.find(u => u.id === userId); if (existing) return existing; const res = await fetch(`https://users.roblox.com/v1/users/${userId}`); if (!res.ok) throw new Error('Failed to fetch user data'); const data = await res.json(); const userData = { id: data.id.toString(), name: data.name, displayName: data.displayName, hasVerifiedBadge: data.hasVerifiedBadge || false }; users.push(userData); saveUsersList(users); return userData; } function removeSpoofedUser(userId) { const users = getUsersList(); const i = users.findIndex(u => u.id === userId); if (i !== -1) { users.splice(i, 1); saveUsersList(users); if (window.gui) refreshUsersList(); } } function saveInventory() { const uid = config.spoofUserId || _cachedUid; saveConfig(); if (uid) savePerUserData(uid, 'inventory', { purchasedItems: config.inventory.purchasedItems, limitedItems: config.inventory.limitedItems }); } const saveLimitedItems = saveInventory; const saveResaleData = saveConfig; function addResaleDataPoint(collectibleItemId, price) { if (!collectibleItemId || !price) return; if (!config.resaleData[collectibleItemId]) config.resaleData[collectibleItemId] = []; config.resaleData[collectibleItemId].unshift({ value: price, date: new Date().toISOString() }); saveConfig(); } function randomHex(len) { const bytes = crypto.getRandomValues(new Uint8Array(Math.ceil(len / 2))); return Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('').slice(0, len); } const randomUUID = () => `${randomHex(8)}-${randomHex(4)}-${randomHex(4)}-${randomHex(4)}-${randomHex(12)}`; function randomIdHash() { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; const bytes = crypto.getRandomValues(new Uint8Array(22)); return Array.from(bytes, b => chars[b % chars.length]).join(''); } function addPurchaseTransaction({ itemName, assetId, price, seller }) { const uid = config.spoofUserId || _cachedUid; const key = uid ? `roblox_spoofer_user_${uid}_purchaseTransactions` : null; const existing = key ? GM_getValue(key, []) : config.purchaseTransactions; existing.unshift({ id: 0, idHash: randomIdHash(), transactionType: 'Purchase', created: new Date().toISOString(), isPending: false, agent: { id: seller?.sellerId ?? 1, type: 'User', name: seller?.name ?? 'Roblox' }, details: { id: assetId ? parseInt(assetId) : 0, name: itemName ?? 'Unknown Item', type: 'Asset' }, currency: { amount: -(price ?? 0), type: 'Robux' }, purchaseToken: randomUUID() }); if (key) GM_setValue(key, existing); else saveConfig(); if (price) { config.transactions.purchasesTotal = (config.transactions.purchasesTotal || 0) + price; config.transactions.outgoingRobuxTotal = (config.transactions.outgoingRobuxTotal || 0) + price; saveConfig(); } } function addRobuxPurchase({ amount, date }) { const uid = config.spoofUserId || _cachedUid; const key = uid ? `roblox_spoofer_user_${uid}_robuxPurchases` : null; const existing = key ? GM_getValue(key, []) : config.robuxPurchases; existing.unshift({ id: 0, idHash: randomIdHash(), transactionType: 'Currency Purchase', created: date || new Date().toISOString(), isPending: false, agent: { id: 1, type: 'User', name: 'Roblox' }, details: null, currency: { amount: amount ?? 0, type: 'Robux' }, purchaseToken: null }); if (key) GM_setValue(key, existing); else saveConfig(); if (amount) { config.transactions.currencyPurchasesTotal = (config.transactions.currencyPurchasesTotal || 0) + amount; config.transactions.incomingRobuxTotal = (config.transactions.incomingRobuxTotal || 0) + amount; if (config.robux.enabled) { config.robux.amount = (config.robux.amount || 0) + amount; const uid = config.spoofUserId || _cachedUid; if (uid) savePerUserData(uid, 'robux', config.robux); } saveConfig(); } } function getRobuxPurchases() { const uid = config.spoofUserId || _cachedUid; const key = uid ? `roblox_spoofer_user_${uid}_robuxPurchases` : null; return key ? GM_getValue(key, []) : config.robuxPurchases; } function getFakeFriendRequests() { const uid = config.spoofUserId || _cachedUid; const key = uid ? `roblox_spoofer_user_${uid}_fakeFriendRequests` : null; return key ? GM_getValue(key, []) : config.fakeFriendRequests; } async function addFakeFriendRequest(userId) { const uid = config.spoofUserId || _cachedUid; const key = uid ? `roblox_spoofer_user_${uid}_fakeFriendRequests` : null; const existing = key ? GM_getValue(key, []) : config.fakeFriendRequests; if (existing.find(r => r.id === userId.toString())) return; const res = await fetch(`https://users.roblox.com/v1/users/${userId}`); if (!res.ok) throw new Error('Failed to fetch user'); const data = await res.json(); existing.push({ id: data.id.toString(), name: data.name, displayName: data.displayName, sentAt: new Date().toISOString() }); if (key) GM_setValue(key, existing); else { config.fakeFriendRequests = existing; saveConfig(); } } function removeFakeFriendRequest(userId) { const uid = config.spoofUserId || _cachedUid; const key = uid ? `roblox_spoofer_user_${uid}_fakeFriendRequests` : null; const arr = key ? GM_getValue(key, []) : config.fakeFriendRequests; const i = arr.findIndex(r => r.id === userId.toString()); if (i !== -1) { arr.splice(i, 1); if (key) GM_setValue(key, arr); else { config.fakeFriendRequests = arr; saveConfig(); } } } async function addItemToInventory(itemId, itemType = 'asset') { const typeParam = itemType === 'bundle' ? 'Bundle' : 'Asset'; const res = await fetch(`https://catalog.roblox.com/v1/catalog/items/${itemId}/details?itemType=${typeParam}`); if (!res.ok) return; const data = await res.json(); if (data.itemType === 'Bundle') { if (config.inventory.purchasedItems.some(i => i.itemId === data.id && i.itemCategory?.itemType === 2)) return; let outfitDetail = null; try { const bRes = await fetch(`https://avatar.roblox.com/v1/bundles/${itemId}/details`); if (bRes.ok) { const bData = await bRes.json(); const firstOutfit = bData.items?.find(i => i.type === 'UserOutfit'); if (firstOutfit?.id) { const oRes = await fetch(`https://avatar.roblox.com/v1/outfits/${firstOutfit.id}/details`); if (oRes.ok) { const oData = await oRes.json(); outfitDetail = { playerAvatarType: oData.playerAvatarType || 'R15', bodyColor3s: oData.bodyColors || oData.bodyColor3s || {}, scales: oData.scale || oData.scales || {}, assets: oData.assets || [] }; } } } } catch { } const bundleItem = { itemId: data.id, itemName: data.name, availabilityStatus: 'Available', acquisitionTime: new Date().toISOString(), bundledItems: data.bundledItems || [], itemCategory: { itemType: 2, itemSubType: data.bundleType } }; if (outfitDetail) bundleItem.outfitDetail = outfitDetail; config.inventory.purchasedItems.push(bundleItem); } else { if (config.inventory.purchasedItems.some(i => i.itemId === data.id && i.itemCategory?.itemType === 1)) return; config.inventory.purchasedItems.push({ itemId: data.id, itemName: data.name, availabilityStatus: 'Available', acquisitionTime: new Date().toISOString(), itemCategory: { itemType: 1, itemSubType: data.assetType } }); } saveInventory(); if (window.gui) refreshInventoryList(); } function removeItemFromInventory(itemId, itemType) { const i = config.inventory.purchasedItems.findIndex( item => item.itemId === itemId && item.itemCategory?.itemType === itemType ); if (i !== -1) { config.inventory.purchasedItems.splice(i, 1); saveInventory(); if (window.gui) refreshInventoryList(); } } function isCurrentlyRendering() { if (!isRenderingInProgress) return false; if (renderingStartTime && (Date.now() - renderingStartTime) > MAX_RENDER_WAIT_MS) { isRenderingInProgress = false; renderingStartTime = null; return false; } return true; } async function getCSRFToken() { const cached = GM_getValue('x-csrf-token', null); if (cached) return cached; try { const res = await fetch('https://auth.roblox.com/v1/logout', { method: 'POST', credentials: 'include' }); return res.headers.get('x-csrf-token') || null; } catch { return null; } } async function renderAvatarThumbnail() { isRenderingInProgress = true; renderingStartTime = Date.now(); if (!config.avatar.avatarData || !config.avatar.outfit) { isRenderingInProgress = false; return null; } const targetUserId = getTargetUserId(); if (!targetUserId) { isRenderingInProgress = false; return null; } if (config.avatar.customThumbnail3d && config.avatar.thumbnailGeneratedAt) { const hrs = (Date.now() - new Date(config.avatar.thumbnailGeneratedAt)) / 3_600_000; const outfitChanged = JSON.stringify(config.avatar.outfit) !== JSON.stringify(config.avatar.lastRenderedOutfit); if (!outfitChanged && hrs < 23) { isRenderingInProgress = false; return config.avatar.customThumbnail3d; } } try { let assetIds; if (config.avatar.outfit.assets?.length) assetIds = config.avatar.outfit.assets.map(a => a.id); else if (config.avatar.outfit.assetIds?.length) assetIds = config.avatar.outfit.assetIds; else if (Array.isArray(config.avatar.outfit)) assetIds = config.avatar.outfit; else throw new Error('Invalid outfit structure'); if (!assetIds.length) throw new Error('No asset IDs found'); const assets = assetIds.map(id => ({ id })); config.avatar.outfit.meta?.forEach((meta, i) => { if (assets[i]) assets[i].meta = meta; }); const COLOR_MAP = { headColor3: 'headColor', torsoColor3: 'torsoColor', rightArmColor3: 'rightArmColor', leftArmColor3: 'leftArmColor', rightLegColor3: 'rightLegColor', leftLegColor3: 'leftLegColor' }; const bc3s = config.avatar.avatarData.bodyColor3s; const bodyColors = {}; for (const [old, nw] of Object.entries(COLOR_MAP)) { if (bc3s[old]) bodyColors[nw] = bc3s[old]; } const requestData = { thumbnailConfig: { thumbnailId: parseInt(targetUserId), thumbnailType: '3d', size: '420x420' }, avatarDefinition: { assets, bodyColors, scales: config.avatar.avatarData.scales, playerAvatarType: { playerAvatarType: config.avatar.avatarData.playerAvatarType } } }; let csrfToken = await getCSRFToken(); if (!csrfToken) throw new Error('Failed to get CSRF token'); const doRender = token => fetch('https://avatar.roblox.com/v1/avatar/render', { method: 'POST', credentials: 'include', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': token }, body: JSON.stringify(requestData) }); let res = await doRender(csrfToken); if (!res.ok && res.status === 403) { const ref = await fetch('https://auth.roblox.com/v1/logout', { method: 'POST', credentials: 'include' }); const fresh = ref.headers.get('x-csrf-token'); if (!fresh) throw new Error('Failed to get fresh CSRF token'); res = await doRender(fresh); } let data = await res.json(); for (let attempts = 0; data.state === 'Pending' && attempts < 60; attempts++) { await new Promise(r => setTimeout(r, 500)); const poll = await fetch(`https://thumbnails.roblox.com/v1/users/avatar-3d?userId=${targetUserId}`, { credentials: 'include' }); if (poll.ok) data = await poll.json(); } if (data.state !== 'Completed' || !data.imageUrl) throw new Error(`Render failed: ${data.state}`); config.avatar.customThumbnail3d = data.imageUrl; config.avatar.thumbnailGeneratedAt = new Date().toISOString(); config.avatar.lastRenderedOutfit = structuredClone(config.avatar.outfit); saveAvatarData(); isRenderingInProgress = false; renderingStartTime = null; setTimeout(stealthToggleAvatarView, 120); return data.imageUrl; } catch { isRenderingInProgress = false; return null; } } function stealthToggleAvatarView() { const container = document.querySelector('.avatar-thumbnail'); const toggle = container?.querySelector('.toggle-three-dee'); if (!toggle || !container) return; const pe = container.style.pointerEvents, vis = container.style.visibility; container.style.pointerEvents = 'none'; container.style.visibility = 'hidden'; toggle.click(); setTimeout(() => { toggle.click(); setTimeout(() => { container.style.pointerEvents = pe; container.style.visibility = vis; }, 0); }, 10); } function applySpoofing() { const profile = getEffectiveProfile(); if (!config.enabled || !profile) return; const realId = unsafeWindow.Roblox?.CurrentUser?.userId?.toString() || null; if (realId) GM_setValue('original_user_id', realId); GM_setValue('spoofed_user_id', profile.id.toString()); GM_setValue('spoofed_user_name', profile.name); GM_setValue('spoofed_user_display_name', profile.displayName); function getSpoofData() { const lp = getEffectiveProfile() || profile; return { isAuthenticated: true, userId: lp.id, name: lp.name, displayName: lp.displayName, isPremiumUser: lp.isPremium, hasVerifiedBadge: lp.isVerified, isUnder13: false, is13orOver: true }; } function patchCurrentUser(currentUser) { if (!currentUser) return; const data = getSpoofData(); const descs = {}; for (const [key, val] of Object.entries(data)) { descs[key] = { get() { return val; }, set() {}, configurable: true, enumerable: true }; } try { Object.defineProperties(currentUser, descs); } catch { } } function hookCurrentUser(robloxObj) { if (!robloxObj) return; if (robloxObj.CurrentUser) patchCurrentUser(robloxObj.CurrentUser); try { let _cu = robloxObj.CurrentUser; Object.defineProperty(robloxObj, 'CurrentUser', { get() { return _cu; }, set(newCu) { _cu = newCu; if (config.enabled) patchCurrentUser(newCu); }, configurable: true }); } catch { } } let robloxObj = unsafeWindow.Roblox; hookCurrentUser(robloxObj); try { Object.defineProperty(unsafeWindow, 'Roblox', { get() { return robloxObj; }, set(v) { robloxObj = v; if (config.enabled) hookCurrentUser(v); }, configurable: true }); } catch { } let lastRobloxRef = robloxObj; let _rafPending = false; const scheduleCurrentUserPatch = () => { if (_rafPending) return; _rafPending = true; requestAnimationFrame(() => { _rafPending = false; if (!config.enabled) return; const current = unsafeWindow.Roblox; if (current?.CurrentUser) { patchCurrentUser(current.CurrentUser); if (current !== lastRobloxRef) { lastRobloxRef = current; hookCurrentUser(current); } } }); }; new MutationObserver(() => { if (config.enabled) scheduleCurrentUserPatch(); }) .observe(document.documentElement, { childList: true, subtree: true, attributes: false, characterData: false }); } const SKIP_TAGS = new Set(['SCRIPT','STYLE','NOSCRIPT','TEXTAREA','CODE','PRE']); const ATTR_SELECTOR = ['aria-label','title','placeholder','alt','data-tooltip'].map(a => `[${a}]`).join(','); const ATTR_NAMES = ['aria-label','title','placeholder','alt','data-tooltip']; function buildReplacementMap() { const fd = config.fetchedData; const p = config.profile; if (!fd) return []; const entries = new Map(); if (fd.displayName && p.displayName && fd.displayName !== p.displayName) entries.set(fd.displayName, p.displayName); if (fd.name && p.name && fd.name !== p.name) entries.set(fd.name, p.name); return [...entries.entries()].sort((a, b) => b[0].length - a[0].length); } function replaceInText(node, pairs) { if (!pairs.length) return; let text = node.nodeValue; let changed = false; for (const [from, to] of pairs) { if (text.includes(from)) { text = text.split(from).join(to); changed = true; } } if (changed) node.nodeValue = text; } function walkAndReplace(root, pairs) { if (!pairs.length) return; const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, { acceptNode(node) { const p = node.parentElement; if (!p || SKIP_TAGS.has(p.tagName)) return NodeFilter.FILTER_REJECT; if (p.tagName === 'INPUT' && (p.type === 'password' || p.type === 'hidden')) return NodeFilter.FILTER_REJECT; return NodeFilter.FILTER_ACCEPT; } }); let node; while ((node = walker.nextNode())) replaceInText(node, pairs); } function replaceInAttributes(root, pairs) { if (!pairs.length) return; const elements = root.querySelectorAll ? root.querySelectorAll(ATTR_SELECTOR) : []; for (const el of elements) { for (const attr of ATTR_NAMES) { const val = el.getAttribute(attr); if (!val) continue; let next = val; for (const [from, to] of pairs) { if (next.includes(from)) next = next.split(from).join(to); } if (next !== val) el.setAttribute(attr, next); } } } function startTextReplacement() { if (!config.enabled) return; let pairs = buildReplacementMap(); if (!pairs.length) return; const patchTitle = (p) => { let t = document.title; let changed = false; for (const [from, to] of p) { if (t.includes(from)) { t = t.split(from).join(to); changed = true; } } if (changed) document.title = t; }; walkAndReplace(document.body || document.documentElement, pairs); replaceInAttributes(document.body || document.documentElement, pairs); patchTitle(pairs); let _lastFetchedDataRef = config.fetchedData; let _attrRafPending = false; const textMO = new MutationObserver(mutations => { if (config.fetchedData !== _lastFetchedDataRef) { _lastFetchedDataRef = config.fetchedData; pairs = buildReplacementMap(); } if (!pairs.length) return; const attrNodes = []; for (const m of mutations) { if (m.type !== 'childList') continue; for (const node of m.addedNodes) { if (node.nodeType === Node.TEXT_NODE) { replaceInText(node, pairs); } else if (node.nodeType === Node.ELEMENT_NODE && !SKIP_TAGS.has(node.tagName)) { walkAndReplace(node, pairs); attrNodes.push(node); } } } if (attrNodes.length && !_attrRafPending) { _attrRafPending = true; requestAnimationFrame(() => { _attrRafPending = false; for (const n of attrNodes) replaceInAttributes(n, pairs); }); } patchTitle(pairs); }); textMO.observe(document.documentElement, { childList: true, subtree: true }); startTextReplacement._mo = textMO; startTextReplacement._sweep = () => { pairs = buildReplacementMap(); _lastFetchedDataRef = config.fetchedData; if (!pairs.length) return; walkAndReplace(document.body || document.documentElement, pairs); replaceInAttributes(document.body || document.documentElement, pairs); patchTitle(pairs); }; } function interceptMetaTag() { if (!config.enabled) return; function getFields() { const p = getEffectiveProfile(); if (!p) return null; return { userid: String(p.id), name: p.name, displayname: p.displayName, ispremiumuser: p.isPremium.toString(), hasverifiedbadge: p.isVerified.toString(), isunder13: 'false', created: p.created || '', }; } let _lockedMeta = null; let _gmWritePending = false; let _pendingFields = null; let _pendingOrig = null; function scheduleGMWrites(fields, orig) { _pendingFields = fields; _pendingOrig = orig; if (_gmWritePending) return; _gmWritePending = true; (window.requestIdleCallback || setTimeout)(() => { _gmWritePending = false; if (!_pendingFields) return; const f = _pendingFields; const o = _pendingOrig; _pendingFields = null; _pendingOrig = null; if (o && o !== f.userid) GM_setValue('original_user_id', o); GM_setValue('spoofed_user_id', f.userid); GM_setValue('spoofed_user_name', f.name); GM_setValue('spoofed_user_display_name', f.displayname); }, { timeout: 400 }); } function applyFields(meta, fields) { const descs = {}; for (const [key, val] of Object.entries(fields)) { descs[key] = { get() { return val; }, set() {}, configurable: true, enumerable: true }; } try { Object.defineProperties(meta.dataset, descs); } catch { for (const [key, val] of Object.entries(fields)) { try { meta.dataset[key] = val; } catch { } } } } function lockMeta(meta) { if (!meta) return; const fields = getFields(); if (!fields) return; const orig = meta.dataset.userid; scheduleGMWrites(fields, orig); applyFields(meta, fields); meta._spoofLocked = true; _lockedMeta = meta; } function refreshLockedMeta() { if (!_lockedMeta) return; const fields = getFields(); if (!fields) return; applyFields(_lockedMeta, fields); } const existingMeta = document.querySelector('meta[name="user-data"]'); if (existingMeta) lockMeta(existingMeta); let _refreshRaf = null; const scheduleRefresh = () => { if (_refreshRaf) return; _refreshRaf = requestAnimationFrame(() => { _refreshRaf = null; if (config.enabled) refreshLockedMeta(); }); }; interceptMetaTag._refresh = refreshLockedMeta; const _metaRefreshInterval = setInterval(() => { if (!config.enabled) { clearInterval(_metaRefreshInterval); return; } refreshLockedMeta(); }, 1000); const metaObserverRoot = document.head || document.documentElement; new MutationObserver(mutations => { for (const m of mutations) { if (m.type !== 'childList') continue; for (const node of m.addedNodes) { if (node.nodeType !== 1) continue; if (node.nodeName === 'META' && node.name === 'user-data') { lockMeta(node); return; } const nested = node.querySelector?.('meta[name="user-data"]'); if (nested) { lockMeta(nested); return; } } scheduleRefresh(); } }).observe(metaObserverRoot, { childList: true, subtree: true }); } function patchXHR(xhr, patch) { const orig = xhr.onreadystatechange; xhr.onreadystatechange = async function () { if (this.readyState === 4) await patch.call(this); if (orig) orig.apply(this, arguments); }; } function overrideXHRResponse(xhr, body, status = 200) { const s = JSON.stringify(body); Object.defineProperties(xhr, { status: { get: () => status, configurable: true }, statusText: { get: () => 'OK', configurable: true }, responseText: { get: () => s, configurable: true }, response: { get: () => s, configurable: true }, }); } function maskedEmail() { const username = (config.account.emailUsername || 'noreply').replace('@', ''); const domain = config.account.emailDomain || '@gmail.com'; return `${username[0]}${'*'.repeat(Math.max(0, username.length - 1))}${domain}`; } function spoofResponse(original, body) { return new Response(JSON.stringify(body), { status: original.status, statusText: original.statusText, headers: original.headers }); } const originalFetch = unsafeWindow.fetch; unsafeWindow.fetch = async function (url, options = {}) { const urlString = typeof url === 'string' ? url : url instanceof URL ? url.href : (url && typeof url === 'object') ? (url.url || url.href || String(url)) : String(url); const profile = getEffectiveProfile(); const currentUserId = getCurrentUserId(); if (urlString?.includes('presence.roblox.com/v1/presence/users')) { const res = await originalFetch(url, options); try { const data = await res.clone().json(); const uid = Number(currentUserId); data.userPresences?.forEach(p => { if (p.userId === uid) p.userPresenceType = 1; }); return spoofResponse(res, data); } catch { return res; } } if (urlString?.includes('users.roblox.com')) { if (urlString.includes('/v1/users/authenticated')) { const res = await originalFetch(url, options); if (!config.enabled || !profile) return res; const id = Number(GM_getValue('spoofed_user_id', null) || profile.id); const name = GM_getValue('spoofed_user_name', null) || profile.name; const dn = GM_getValue('spoofed_user_display_name', null) || profile.displayName; if (!id || !name) return res; return spoofResponse(res, { id, name, displayName: dn }); } if (urlString.includes('/v1/description')) { const res = await originalFetch(url, options); if (!config.enabled || !profile) return res; return spoofResponse(res, { description: config.profile.description || '' }); } } if (urlString?.includes('economy.roblox.com')) { if (config.robux.enabled && urlString.includes('/v1/users/') && urlString.includes('/currency')) { const res = await originalFetch(url, options); return spoofResponse(res, { robux: config.robux.amount }); } } if (urlString?.includes('apis.roblox.com')) { if (options?.method === 'POST' && urlString.includes('/oauth/v1/authorizations')) { const origId = GM_getValue('original_user_id', null); if (origId && options.body) { try { const body = JSON.parse(options.body); if (body.resourceInfos?.[0]?.owner) { body.resourceInfos[0].owner.id = origId; options = { ...options, body: JSON.stringify(body) }; } } catch { } } } } return originalFetch(url, options); }; const originalOpen = unsafeWindow.XMLHttpRequest.prototype.open; const originalSend = unsafeWindow.XMLHttpRequest.prototype.send; unsafeWindow.XMLHttpRequest.prototype.open = function (method, url, ...rest) { this._url = url; this._method = method; return originalOpen.call(this, method, url, ...rest); }; unsafeWindow.XMLHttpRequest.prototype.send = function (body) { const url = this._url; const method = this._method; const xhr = this; if (url?.includes('apis.roblox.com')) { if (method === 'POST' && url.includes('account-switcher/v1/switch')) { try { const { switched_to_user_id } = JSON.parse(body); if (switched_to_user_id) { setTimeout(async () => { try { await switchToUser(switched_to_user_id.toString()); } catch { } }, 100); } } catch { } patchXHR(xhr, function () { overrideXHRResponse(this, { success: true }); }); } if (url.includes('account-switcher/v1/getLoggedInUsersMetadata')) { patchXHR(xhr, function () { if (this.status !== 200) return; const data = JSON.parse(this.responseText); const profile = getEffectiveProfile(); data.active_user_id = getCurrentUserId(); data.logged_in_users_metadata = [ { user_id: String(profile.id), display_name: profile.displayName, username: profile.name }, ...getUsersList().map(u => ({ user_id: u.id, display_name: u.displayName, username: u.name })) ]; overrideXHRResponse(this, data); }); } if (url.includes('credit-balance/v1/get-credit-balance-for-navigation')) { if (config.usd.enabled) patchXHR(xhr, function () { overrideXHRResponse(this, { creditDisplayConfig: 'showCreditAndRobux', creditBalance: config.usd.amount, currencyCode: config.usd.currencyCode || 'USD' }); }); } if (url.includes('credit-balance/v1/get-conversion-metadata')) { if (config.usd.enabled) patchXHR(xhr, function () { overrideXHRResponse(this, { creditBalance: config.usd.amount, currencyCode: config.usd.currencyCode || 'USD', robuxConversionAmount: 0, isConvertAllFlowEnabled: false }); }); } if (url.includes('payments-gateway/v1/gift-card/redeem')) { let redemptionCode = null; try { const parsed = JSON.parse(body); redemptionCode = (parsed.pinCode || parsed.redemptionCode || parsed.code || '').toString().trim().toUpperCase(); } catch { } patchXHR(xhr, function () { const cardIndex = config.usd.giftCards.findIndex( c => c.code.toString().trim().toUpperCase() === redemptionCode ); if (cardIndex === -1) { overrideXHRResponse(this, { errors: [{ code: 1, message: 'Invalid gift card code.' }], redemptionResult: null }, 200); return; } const card = config.usd.giftCards[cardIndex]; const cardValue = parseFloat(card.value) || 0; const newBalance = Math.round(((parseFloat(config.usd.amount) || 0) + cardValue) * 100) / 100; config.usd.giftCards.splice(cardIndex, 1); config.usd.amount = newBalance; saveConfig(); const _uid = config.spoofUserId || getCurrentUserId(); if (_uid) savePerUserData(_uid, 'usd', { enabled: config.usd.enabled, amount: config.usd.amount }); overrideXHRResponse(this, { errors: null, redemptionResult: { balanceAmountInLocalCurrency: newBalance, currencyCode: 'USD', creatorName: '', error: '0', grantedRobux: '0', itemId: 0, itemName: '', itemType: '', itemTypeDisplayName: '', redeemedCredit: cardValue, redeemedCreditInLocalCurrency: cardValue, successMsg: 'You have successfully redeemed your gift card!', successSubText: `$${cardValue.toFixed(2)} USD credit added to your account.`, grantCredit: cardValue, exchangeRate: 0 } }, 200); }); } if (config.marketplace.freePurchases && url.includes('marketplace-sales/v1/item/')) { const isLimitedBuy = url.includes('/purchase-resale'); if (isLimitedBuy && method === 'POST') { let parsedBody = null; try { parsedBody = JSON.parse(body); } catch { } if (parsedBody?.collectibleItemId) { const { collectibleItemId, collectibleItemInstanceId, collectibleProductId, expectedPrice } = parsedBody; const assetId = window.location.href.match(/[/]catalog[/](\d+)[/]/)?.[1] ?? null; xhr._limitedMetaPromise = assetId ? (async () => { try { const metaRes = await originalFetch(`https://catalog.roblox.com/v1/catalog/items/${assetId}/details?itemType=Asset`); if (!metaRes.ok) return null; const item = await metaRes.json(); if (!item?.name) return null; return { assetId, itemName: item.name, assetType: item.assetType ?? null, recentAveragePrice: item.lowestPrice ?? item.price ?? expectedPrice ?? null, assetStock: item.unitsAvailableForConsumption ?? null }; } catch { return null; } })() : Promise.resolve(null); xhr._cheapestReseller = (async () => { try { const r = await originalFetch( `https://apis.roblox.com/marketplace-sales/v1/item/${collectibleItemId}/resellers?cursor=&limit=100`, { credentials: 'include' } ); if (!r.ok) return null; const data = await r.json(); if (!data.data?.length) return null; const ownedInstanceIds = new Set( config.inventory.limitedItems .filter(i => i.collectibleItemId === collectibleItemId) .map(i => i.collectibleItemInstanceId) ); const otherListings = data.data.filter(l => !ownedInstanceIds.has(l.collectibleItemInstanceId)); if (!otherListings.length) return null; return otherListings.reduce((a, b) => a.price < b.price ? a : b); } catch { return null; } })(); const origSendThis = originalSend.bind(xhr); const _body = body; setTimeout(async () => { const cheapest = await xhr._cheapestReseller; let finalBody = _body; if (cheapest) { try { const parsed = JSON.parse(_body); parsed.collectibleItemInstanceId = cheapest.collectibleItemInstanceId; parsed.collectibleProductId = cheapest.collectibleProductId; parsed.expectedPrice = cheapest.price; parsed.expectedSellerId = cheapest.seller.sellerId; parsed.expectedSellerType = 'User'; const originalUserId = GM_getValue('original_user_id', null); if (originalUserId) parsed.expectedPurchaserId = originalUserId; finalBody = JSON.stringify(parsed); } catch { } } origSendThis(finalBody); }, 500 + Math.floor(Math.random() * 2000)); patchXHR(xhr, async function () { overrideXHRResponse(this, { purchaseResult: 'Purchase transaction success', purchased: true, pending: false, errorMessage: null }, 200); const cheapest = await (xhr._cheapestReseller ?? Promise.resolve(null)); const usedInstanceId = cheapest?.collectibleItemInstanceId ?? collectibleItemInstanceId; const usedProductId = cheapest?.collectibleProductId ?? collectibleProductId; const usedPrice = cheapest?.price ?? expectedPrice; if (config.inventory.limitedItems.some(i => i.collectibleItemInstanceId === usedInstanceId)) return; const meta = await (xhr._limitedMetaPromise ?? Promise.resolve(null)); if (!meta) return; config.inventory.limitedItems.push({ collectibleItemId, collectibleItemInstanceId: usedInstanceId, collectibleProductId: usedProductId, expectedPrice: usedPrice, assetId: meta.assetId, itemName: meta.itemName, assetType: meta.assetType, recentAveragePrice: meta.recentAveragePrice, assetStock: meta.assetStock, serialNumber: null }); if (config.robux.enabled && usedPrice) { config.robux.amount = Math.max(0, config.robux.amount - usedPrice); const uid = config.spoofUserId || _cachedUid; if (uid) savePerUserData(uid, 'robux', config.robux); } addResaleDataPoint(collectibleItemId, usedPrice); addPurchaseTransaction({ itemName: meta.itemName, assetId: meta.assetId, price: usedPrice, seller: cheapest?.seller ?? null }); saveLimitedItems(); }); return; } } if (!isLimitedBuy && url.includes('/purchase-item')) { patchXHR(xhr, function () { overrideXHRResponse(this, { purchaseResult: 'Purchase transaction success', purchased: true, pending: false, errorMessage: null }); const catalogMatch = window.location.href.match(/[/]catalog[/](\d+)[/]/); const bundleMatch = window.location.href.match(/[/]bundles[/](\d+)[/]/); if (catalogMatch) addItemToInventory(catalogMatch[1], 'asset'); else if (bundleMatch) addItemToInventory(bundleMatch[1], 'bundle'); }); } } if (url.includes('marketplace-sales/v1/item/') && url.includes('/resellable-instances')) { const collectibleItemId = url.match(/marketplace-sales\/v1\/item\/([^/]+)\/resellable-instances/)?.[1]; if (collectibleItemId) { patchXHR(xhr, function () { const fakes = config.inventory.limitedItems.filter(i => i.collectibleItemId === collectibleItemId); if (!fakes.length) return; const fakeInstances = fakes.map(fake => ({ collectibleInstanceId: fake.collectibleItemInstanceId, collectibleItemId: fake.collectibleItemId, collectibleProductId: fake.collectibleProductId, serialNumber: fake.serialNumber ?? null, isHeld: false, saleState: 'OffSale', price: fake.expectedPrice ?? 0 })); let existingInstances = []; if (this.status === 200) { try { existingInstances = JSON.parse(this.responseText).itemInstances ?? []; } catch { } } overrideXHRResponse(this, { itemInstances: [...fakeInstances, ...existingInstances], previousPageCursor: null, nextPageCursor: null }); }); } } if (url.includes('marketplace-sales/v1/item/') && url.includes('/resellers')) { const collectibleItemId = url.match(/marketplace-sales\/v1\/item\/([^/]+)\/resellers/)?.[1]; if (collectibleItemId) { patchXHR(xhr, function () { if (this.status !== 200) return; const data = JSON.parse(this.responseText); if (!data.data?.length) return; const fakeInstanceIds = new Set( config.inventory.limitedItems .filter(i => i.collectibleItemId === collectibleItemId) .map(i => i.collectibleItemInstanceId) ); if (!fakeInstanceIds.size) return; data.data = data.data.filter(entry => !fakeInstanceIds.has(entry.collectibleItemInstanceId)); overrideXHRResponse(this, data); }); } } if (url.includes('marketplace-sales/v1/item/') && url.includes('/resale-data')) { const collectibleItemId = url.match(/marketplace-sales\/v1\/item\/([^/]+)\/resale-data/)?.[1]; if (collectibleItemId) { patchXHR(xhr, function () { if (this.status !== 200) return; const fakePoints = config.resaleData[collectibleItemId] ?? []; if (!fakePoints.length) return; const data = JSON.parse(this.responseText); data.priceDataPoints = [...fakePoints, ...(data.priceDataPoints ?? [])]; if (data.priceDataPoints.length) { const sum = data.priceDataPoints.reduce((acc, p) => acc + p.value, 0); data.recentAveragePrice = Math.round(sum / data.priceDataPoints.length); } overrideXHRResponse(this, data); }); } } } if (url?.includes('avatar.roblox.com')) { if (method === 'POST' && url.includes('/v2/avatar/set-wearing-assets')) { config.avatar.outfit = JSON.parse(body); saveAvatarData(); const currentUserId = getCurrentUserId(); setTimeout(async () => { try { const r = await fetch(`https://avatar.roblox.com/v2/avatar/users/${currentUserId}/avatar`, { credentials: 'include' }); if (r.ok) { const d = await r.json(); if (d.bodyColor3s && d.scales && d.playerAvatarType) { config.avatar.avatarData = { bodyColor3s: d.bodyColor3s, scales: d.scales, playerAvatarType: d.playerAvatarType }; saveAvatarData(); } } await renderAvatarThumbnail(); } catch { } }, 1500); patchXHR(xhr, function () { overrideXHRResponse(this, { invalidAssets: [], invalidAssetIds: [], success: true }); }); } if (url.includes('/v1/avatar-inventory')) { const currentUserId = getCurrentUserId(); patchXHR(xhr, async function () { if (this.status !== 200) return; if (!config.inventory.purchasedItems.length && !config.inventory.limitedItems.length && !config.inventory.useRealInventory) return; const data = JSON.parse(this.responseText); data.avatarInventoryItems ??= []; const urlObj = new URL(url, 'https://avatar.roblox.com'); let filterSubType = null, filterType = null; for (const [k, v] of urlObj.searchParams) { if (k.includes('ItemSubType')) filterSubType = parseInt(v); if (k.includes('ItemType')) filterType = v === 'Asset' || v === '1' ? 1 : v === 'Bundle' || v === '2' ? 2 : null; } let itemsToAdd = []; const fromLocalStorage = () => { for (const item of config.inventory.purchasedItems) { const isBundle = item.itemCategory?.itemType === 2; if (isBundle) { if (filterType === null || filterType === 2) { if (filterSubType === null || item.itemCategory?.itemSubType === filterSubType) itemsToAdd.push(item); } if ((filterType === null || filterType === 1) && item.bundledItems) { for (const bi of item.bundledItems) { if (bi.type !== 'Asset') continue; if (filterSubType !== null && bi.assetType !== filterSubType) continue; itemsToAdd.push({ itemId: bi.id, itemName: bi.name, availabilityStatus: 'Available', acquisitionTime: item.acquisitionTime || new Date().toISOString(), itemCategory: { itemType: 1, itemSubType: bi.assetType } }); } } } else { if ((filterSubType === null || item.itemCategory?.itemSubType === filterSubType) && (filterType === null || item.itemCategory?.itemType === filterType)) { itemsToAdd.push(item); } } } for (const item of config.inventory.limitedItems) { if (filterType !== null && filterType !== 1) continue; if (filterSubType !== null && item.assetType !== filterSubType) continue; itemsToAdd.push({ itemId: parseInt(item.assetId), itemName: item.itemName, availabilityStatus: 'Available', acquisitionTime: new Date().toISOString(), itemCategory: { itemType: 1, itemSubType: item.assetType ?? null } }); } }; if (config.inventory.useRealInventory && filterSubType !== null) { try { const r = await fetch(`https://inventory.roblox.com/v2/users/${currentUserId}/inventory/${filterSubType}?cursor=&limit=100&sortOrder=Desc`, { credentials: 'include' }); if (!r.ok) throw new Error(`HTTP ${r.status}`); const inv = await r.json(); if (inv.data) itemsToAdd = inv.data.map(i => ({ itemId: i.assetId, itemName: i.assetName, availabilityStatus: 'Available', acquisitionTime: i.created, itemCategory: { itemType: 1, itemSubType: filterSubType } })); } catch { fromLocalStorage(); } } else { fromLocalStorage(); } data.avatarInventoryItems = [...itemsToAdd, ...data.avatarInventoryItems]; overrideXHRResponse(this, data); }); } if (url.includes('/v2/avatar/avatar')) { const currentUserId = getCurrentUserId(); patchXHR(xhr, async function () { if (this.status !== 200) return; const r = await fetch(`https://avatar.roblox.com/v2/avatar/users/${currentUserId}/avatar`, { credentials: 'include' }); if (!r.ok) return; const spoofed = await r.json(); const data = JSON.parse(this.responseText); if (data.bodyColor3s && data.scales && data.playerAvatarType) { config.avatar.avatarData = { bodyColor3s: spoofed.bodyColor3s, scales: spoofed.scales, playerAvatarType: spoofed.playerAvatarType }; saveAvatarData(); } if (!config.avatar.outfit) { config.avatar.outfit = { assets: spoofed.assets }; saveAvatarData(); } if (config.avatar.outfit?.assets) data.assets = config.avatar.outfit.assets; overrideXHRResponse(this, data); }); } } if (url?.includes('thumbnails.roblox.com/v1/users/avatar-3d')) { const reqUserId = url.match(/userId=(\d+)/)?.[1]; const currentUserId = getCurrentUserId(); if (reqUserId && currentUserId && reqUserId === currentUserId && config.avatar.customThumbnail3d) { patchXHR(xhr, async function () { if (this.readyState !== 4) return; const startWait = Date.now(); while (isRenderingInProgress && (Date.now() - startWait) < MAX_RENDER_WAIT_MS) await new Promise(r => setTimeout(r, 500)); const base = config.avatar.customThumbnail3d; const bust = base + (base.includes('?') ? '&' : '?') + 'spooferBust=' + Date.now(); overrideXHRResponse(this, { targetId: parseInt(reqUserId), state: 'Completed', imageUrl: bust, version: 'TN3' }); }); } } if (url?.includes('friends.roblox.com')) { const currentUserId = getCurrentUserId(); if (url.includes('/v1/user/friend-requests/count')) { patchXHR(xhr, function () { if (!config.enabled) return; const fake = getFakeFriendRequests(); if (!fake.length) return; let base = 0; try { base = JSON.parse(this.responseText)?.count || 0; } catch { } overrideXHRResponse(this, { count: base + fake.length }); }); } // ── FRIEND REQUESTS LIST ───────────────────────────────────────── if (url.includes('/v1/my/friends/requests')) { patchXHR(xhr, function () { if (!config.enabled) return; const fake = getFakeFriendRequests(); if (!fake.length) return; let realData = { previousPageCursor: null, nextPageCursor: null, data: [] }; try { realData = JSON.parse(this.responseText); } catch { } const fakeEntries = fake.map(r => ({ friendRequest: { sentAt: r.sentAt, senderId: parseInt(r.id), sourceUniverseId: null, originSourceType: 'PlayerSearch', contactName: null, senderNickname: '' }, mutualFriendsList: [], id: parseInt(r.id) })); const fakeIds = new Set(fake.map(r => parseInt(r.id))); const filteredReal = Array.isArray(realData.data) ? realData.data.filter(entry => !fakeIds.has(entry.id)) : []; realData.data = [...fakeEntries, ...filteredReal]; overrideXHRResponse(this, realData); }); } // ───────────────────────────────────────────────────────────────── if (url.includes(`/v1/users/${currentUserId}`) && url.includes('userSort=1')) { patchXHR(xhr, async function () { if (this.status !== 403) return; const r = await fetch(`https://friends.roblox.com/v1/users/${getCurrentUserId()}/friends/find?limit=18&cursor=&userSort=`, { credentials: 'include' }); if (r.ok) overrideXHRResponse(this, await r.json()); }); } if (url.includes('friends/online')) { patchXHR(xhr, function () { if (this.status === 400) overrideXHRResponse(this, { data: [] }); }); } } if (url?.includes('trades.roblox.com')) { if (url.includes('/tradableitems')) { const currentUserId = getCurrentUserId(); const urlUserId = url.match(/\/users\/(\d+)\/tradableitems/)?.[1]; if (urlUserId && (urlUserId === currentUserId || urlUserId === config.spoofUserId)) { patchXHR(xhr, function () { if (this.status !== 200 || !config.inventory.limitedItems.length) return; const data = JSON.parse(this.responseText); const acc = {}; for (const limited of config.inventory.limitedItems) { const cid = limited.collectibleItemId; if (!acc[cid]) { acc[cid] = { collectibleItemId: cid, itemTarget: { itemType: 'Asset', targetId: limited.assetId }, itemName: limited.itemName, originalPrice: null, recentAveragePrice: limited.recentAveragePrice, assetStock: limited.assetStock, instances: [] }; } acc[cid].instances.push({ collectibleItemInstanceId: limited.collectibleItemInstanceId, itemTarget: { itemType: 'Asset', targetId: limited.assetId }, itemName: limited.itemName, serialNumber: limited.serialNumber ?? null, originalPrice: null, recentAveragePrice: limited.recentAveragePrice, assetStock: limited.assetStock, isOnHold: false }); } data.items = [...Object.values(acc), ...(data.items ?? [])]; data.userId = parseInt(currentUserId); overrideXHRResponse(this, data); }); } } if (url.includes('/can-trade-with')) { patchXHR(xhr, function () { overrideXHRResponse(this, { canTrade: true, status: 'CanTrade' }, 200); }); } } if (url?.includes('economy.roblox.com')) { if (config.robux.enabled && url.includes('/v1/users/') && url.includes('/currency')) { patchXHR(xhr, function () { overrideXHRResponse(this, { robux: config.robux.amount }); }); } } if (url?.includes('transaction-records/v1/users/')) { if (url.includes('/transaction-types')) { patchXHR(xhr, function () { const t = config.transactions; const uid = config.spoofUserId || getCurrentUserId(); const hasFakePurchases = uid ? GM_getValue(`roblox_spoofer_user_${uid}_purchaseTransactions`, []).length > 0 : false; const hasFakeRobuxPurchases = uid ? GM_getValue(`roblox_spoofer_user_${uid}_robuxPurchases`, config.robuxPurchases).length > 0 : config.robuxPurchases.length > 0; overrideXHRResponse(this, { HasPurchase: t.purchasesTotal > 0 || hasFakePurchases, HasSale: t.salesTotal > 0, HasAffiliatePayout: t.affiliatePayoutTotal > 0, HasAffiliateSale: t.affiliateSalesTotal > 0, HasGroupPayout: t.groupPayoutsTotal > 0, HasCurrencyPurchase: t.currencyPurchasesTotal > 0 || hasFakeRobuxPurchases, HasTradeRobux: t.tradeSystemEarningsTotal > 0 || t.tradeSystemCostsTotal > 0, HasPremiumStipend: t.premiumStipendsTotal > 0, HasEngagementPayout: t.premiumPayoutsTotal > 0, HasGroupEngagementPayout: t.groupPremiumPayoutsTotal > 0, HasAdSpend: t.adSpendTotal > 0, HasDevEx: t.developerExchangeTotal > 0, HasPendingRobux: t.pendingRobuxTotal > 0, HasIndividualToGroup: t.individualToGroupTotal > 0, HasCSAdjustment: t.csAdjustmentTotal > 0, HasAdsRevsharePayout: t.adsRevsharePayoutsTotal > 0, HasGroupAdsRevsharePayout: t.groupAdsRevsharePayoutsTotal > 0, HasSubscriptionsRevsharePayout: t.subscriptionsRevshareTotal > 0, HasGroupSubscriptionsRevsharePayout: t.groupSubscriptionsRevshareTotal > 0, HasPublishingAdvanceRebates: t.publishingAdvanceRebatesTotal > 0, HasLicensingPayment: t.licensingPaymentTotal > 0 }); }); } if (url.includes('/transaction-totals')) { patchXHR(xhr, function () { overrideXHRResponse(this, config.transactions); }); } if (url.includes('/transactions') && url.includes('transactionType=Purchase') && url.includes('PaidAndLimited')) { patchXHR(xhr, function () { const uid = config.spoofUserId || getCurrentUserId(); const userTxns = uid ? GM_getValue(`roblox_spoofer_user_${uid}_purchaseTransactions`, []) : []; overrideXHRResponse(this, { previousPageCursor: null, nextPageCursor: null, data: userTxns }, 200); }); } if (url.includes('/transactions') && url.includes('transactionType=CurrencyPurchase')) { patchXHR(xhr, function () { const uid = config.spoofUserId || getCurrentUserId(); const purchases = uid ? GM_getValue(`roblox_spoofer_user_${uid}_robuxPurchases`, config.robuxPurchases) : config.robuxPurchases; overrideXHRResponse(this, { previousPageCursor: null, nextPageCursor: null, data: purchases }, 200); }); } } if (url?.includes('catalog.roblox.com')) { if (url.includes('/v1/catalog/items/') && url.includes('/details')) { patchXHR(xhr, async function () { if (this.status !== 200) return; const data = JSON.parse(this.responseText); const itemId = data.id; let modified = false; if (itemId === 201 && config.robux.buyableHeadless) { data.isPurchasable = true; data.isOffSale = false; delete data.priceStatus; modified = true; } const ownedLimiteds = data.collectibleItemId ? config.inventory.limitedItems.filter(i => i.collectibleItemId === data.collectibleItemId) : []; const owned = config.inventory.purchasedItems.some(i => i.itemId === itemId) || ownedLimiteds.length > 0; if (owned) { data.owned = true; if (data.bundledItems?.length) { data.bundledItems = data.bundledItems.map(bi => ({ ...bi, owned: config.inventory.purchasedItems.some(i => i.itemId === bi.id) || config.inventory.purchasedItems.some(i => i.itemCategory?.itemType === 2 && i.bundledItems?.some(b => b.id === bi.id)) })); } modified = true; } if (ownedLimiteds.length > 0 && data.collectibleItemId) { try { const r = await originalFetch( `https://apis.roblox.com/marketplace-sales/v1/item/${data.collectibleItemId}/resellers?cursor=&limit=100`, { credentials: 'include' } ); if (r.ok) { const resellData = await r.json(); const ownedInstanceIds = new Set(ownedLimiteds.map(i => i.collectibleItemInstanceId)); const otherListings = (resellData.data ?? []).filter(l => !ownedInstanceIds.has(l.collectibleItemInstanceId)); const cheapest = otherListings.length ? otherListings.reduce((a, b) => a.price < b.price ? a : b) : null; data.lowestPrice = cheapest?.price ?? data.lowestPrice; data.lowestResalePrice = cheapest?.price ?? data.lowestResalePrice; data.expectedSellerId = cheapest?.seller?.sellerId ?? data.expectedSellerId; data.hasResellers = otherListings.length > 0; data.isPurchasable = otherListings.length > 0; data.isOffSale = otherListings.length === 0; modified = true; } } catch { } } if (modified) overrideXHRResponse(this, data); }); } } if (!config.enabled) return originalSend.call(this, body); if (url?.includes('users.roblox.com')) { if (url.includes('/v1/description')) { patchXHR(xhr, function () { if (this.status !== 200) return; overrideXHRResponse(this, { description: config.profile.description || '' }); }); } if (url.includes('/v1/users')) { patchXHR(xhr, function () { if (this.status !== 200) return; const data = JSON.parse(this.responseText); if (!Array.isArray(data.data)) return; const spoofedUsers = getUsersList(); const realId = getCurrentUserId(); const spoofId = config.spoofUserId; let modified = false; let realIdx = -1, spoofIdx = -1; data.data.forEach((u, i) => { if (realId && u.id.toString() === realId) realIdx = i; if (spoofId && u.id.toString() === spoofId) spoofIdx = i; }); if (realIdx !== -1 && spoofIdx !== -1 && realIdx !== spoofIdx) { [data.data[realIdx], data.data[spoofIdx]] = [data.data[spoofIdx], data.data[realIdx]]; modified = true; } if (spoofId && realIdx !== -1 && config.fetchedData) { const entry = data.data[realIdx]; entry.id = parseInt(spoofId); entry.name = config.profile.name || config.fetchedData.name; entry.displayName = config.profile.displayName || config.fetchedData.displayName; entry.hasVerifiedBadge = config.profile.isVerified; modified = true; } const existingIds = new Set(data.data.map(u => u.id.toString())); for (const u of spoofedUsers) { if (!existingIds.has(u.id)) { data.data.push({ hasVerifiedBadge: u.hasVerifiedBadge, id: parseInt(u.id), name: u.name, displayName: u.displayName }); modified = true; } } if (modified) overrideXHRResponse(this, data); }); } } if (url?.includes('/my/settings/json')) { patchXHR(xhr, function () { if (this.status !== 200) return; const data = JSON.parse(this.responseText); const profile = getEffectiveProfile(); const tid = getTargetUserId(); if (!config.enabled || !tid || !profile) return; config.account.isPremium = config.profile.isPremium; const email = maskedEmail(); Object.assign(data, { Name: profile.name, DisplayName: profile.displayName, UserId: parseInt(tid), IsAdmin: profile.isRobloxAdmin, IsPremium: config.account.isPremium, CanTrade: config.account.canTrade, IsEmailVerified: config.account.emailVerified, UserEmailVerified: config.account.emailVerified, IsEmailOnFile: config.account.isEmailOnFile, IsPhoneFeatureEnabled: config.account.phoneFeatureEnabled, AccountAgeInDays: config.account.accountAge, CanHideInventory: config.account.canHideInventory, UserAbove13: config.account.userAbove13, HasValidPasswordSet: config.account.hasValidPasswordSet, ChangeUsernameEnabled: config.account.changeUsernameEnabled, RobuxRemainingForUsernameChange: config.account.robuxRemainingForUsernameChange, UserEmail: email, UserEmailMasked: true, PreviousUserNames: '' }); if (data.MyAccountSecurityModel) { Object.assign(data.MyAccountSecurityModel, { IsEmailSet: config.account.isEmailOnFile, IsEmailVerified: config.account.emailVerified, IsTwoStepEnabled: config.account.twoStepEnabled }); if (data.MyAccountSecurityModel.TwoStepVerificationViewModel) { Object.assign(data.MyAccountSecurityModel.TwoStepVerificationViewModel, { UserId: parseInt(tid), IsEnabled: config.account.twoStepEnabled }); } } overrideXHRResponse(this, data); }); } if (url?.includes('accountsettings.roblox.com')) { if (url.includes('/v1/emails')) { patchXHR(xhr, function () { if (this.status !== 200 || !config.enabled) return; const data = JSON.parse(this.responseText); const email = maskedEmail(); data.verifiedEmail = config.account.emailVerified ? email : null; data.pendingEmail = config.account.emailVerified ? null : email; overrideXHRResponse(this, data); }); } if (url.includes('/v1/account/settings/account-country')) { patchXHR(xhr, function () { if (this.status !== 200 || !config.enabled) return; const data = JSON.parse(this.responseText); const cn = config.account.countryName || 'United States'; data.value = { countryName: cn, subdivisionIso: null, localizedSubdivision: null, localizedName: cn, countryId: 226 }; overrideXHRResponse(this, data); }); } } if (url?.includes('apis.roblox.com')) { if (url.includes('profile-platform-api/v1/profiles/get')) { const currentUserId = getCurrentUserId(); patchXHR(xhr, async function () { if (this.status !== 200) return; const data = JSON.parse(this.responseText); const profile = getEffectiveProfile(); const tid = getTargetUserId(); if (tid && data.profileId === tid) { if (data.components?.Actions) { data.components.Actions.buttons = [{ type: 'EditProfile' }, { type: 'EditAvatar' }, { type: 'QrCode' }]; data.components.Actions.contextual = ['ViewInventory', 'ViewFavorites']; } const hdr = data.components?.UserProfileHeader; if (hdr) { hdr.isPremium = profile.isPremium; hdr.isVerified = profile.isVerified; hdr.isRobloxAdmin = profile.isRobloxAdmin; hdr.names.displayName = profile.displayName; hdr.names.primaryName = profile.displayName; hdr.names.username = profile.name; if (profile.friendsCount > 0) { hdr.counts.friendsCount = profile.friendsCount; hdr.counts.isFriendsCountEnabled = true; } if (profile.followersCount > 0) { hdr.counts.followersCount = profile.followersCount; hdr.counts.isFollowersCountEnabled = true; } if (profile.followingsCount > 0) { hdr.counts.followingsCount = profile.followingsCount; hdr.counts.isFollowingsCountEnabled = true; } if (hdr.contextualInformation) hdr.contextualInformation.context = 'Presence'; } if (data.components?.About) { data.components.About.description = profile.description; data.components.About.joinDateTime = profile.created; } if (data.components?.Statistics) data.components.Statistics.userJoinedDate = profile.created; } else { try { const r = await fetch(`https://friends.roblox.com/v1/users/${currentUserId}/friends?limit=100`, { credentials: 'include' }); if (r.ok) { const { data: friends } = await r.json(); if (friends.some(f => f.id === Number(data.profileId))) { const contextual = ['EditAlias', 'FollowUser', 'Unfriend']; if (data.components?.UserProfileHeader?.isPremium) contextual.push('TradeItems'); contextual.push('ViewInventory', 'ViewFavorites', 'Block', 'Report'); data.components.Actions.contextual = contextual; data.components.Actions.buttons = [{ type: 'Chat' }]; } else { const isFakeRequester = getFakeFriendRequests().some(r => r.id === data.profileId?.toString()); if (isFakeRequester) { data.components.Actions.buttons = [{ type: 'AcceptFriendRequest' }]; } } } } catch { } } overrideXHRResponse(this, data); }); } } if (url?.includes('premiumfeatures.roblox.com/v1/users/') && url?.includes('/subscriptions')) { if (config.profile.isPremium && config.premium?.createdAt) { patchXHR(xhr, function () { const product = PREMIUM_PRODUCTS[config.premium.subscriptionType] || PREMIUM_PRODUCTS.RobloxPremium2200; const created = new Date(config.premium.createdAt); const renewal = new Date(created); renewal.setMonth(renewal.getMonth() + 1); overrideXHRResponse(this, { subscriptionProductModel: { premiumFeatureId: product.premiumFeatureId, subscriptionTypeName: config.premium.subscriptionType, robuxStipendAmount: product.robuxStipendAmount, isLifetime: true, expiration: renewal.toISOString(), renewal: renewal.toISOString(), renewedSince: config.premium.renewedSince, created: config.premium.createdAt, purchasePlatform: 'isDesktop', subscriptionName: product.subscriptionName } }, 200); }); } } return originalSend.call(this, body); }; const PREMIUM_PRODUCTS = { RobloxPremium450: { premiumFeatureId: 505, robuxStipendAmount: 450, subscriptionName: 'Roblox Premium 450' }, RobloxPremium1000: { premiumFeatureId: 506, robuxStipendAmount: 1000, subscriptionName: 'Roblox Premium 1000' }, RobloxPremium2200: { premiumFeatureId: 507, robuxStipendAmount: 2200, subscriptionName: 'Roblox Premium 2200' }, }; const savePremium = () => { saveConfig(); const uid = config.spoofUserId || getCurrentUserId(); if (uid) savePerUserData(uid, 'premium', config.premium); }; let gui = null; let currentTab = 'main'; let inventoryFolder = null; let usersFolder = null; function refreshInventoryList() { if (!inventoryFolder) return; inventoryFolder.children.slice().forEach(c => { if (c._title?.startsWith('Item:') || c._title?.startsWith('Bundle:') || c._title?.startsWith('Limited:')) c.destroy(); }); config.inventory.purchasedItems.forEach(item => { const isBundle = item.itemCategory?.itemType === 2; const folder = inventoryFolder.addFolder(`${isBundle ? 'Bundle' : 'Item'}: ${item.itemName || item.itemId}`); folder.close(); if (isBundle && item.bundledItems?.length) { const sub = folder.addFolder(`Contents (${item.bundledItems.length})`); sub.close(); item.bundledItems.slice(0, 5).forEach((bi, idx) => sub.add({ name: `${idx + 1}. ${bi.name || bi.id}` }, 'name').name('').disable()); if (item.bundledItems.length > 5) sub.add({ more: `... and ${item.bundledItems.length - 5} more` }, 'more').name('').disable(); } folder.add({ remove: () => removeItemFromInventory(item.itemId, item.itemCategory?.itemType || 1) }, 'remove').name('Remove'); folder.add({ view: () => window.open(`https://www.roblox.com/${isBundle ? 'bundles' : 'catalog'}/${item.itemId}`, '_blank') }, 'view').name('View on Roblox'); }); config.inventory.limitedItems.forEach(item => { const folder = inventoryFolder.addFolder(`Limited: ${item.itemName || item.collectibleItemId}`); folder.close(); folder.add({ price: item.expectedPrice ?? 0 }, 'price').name('Bought For').disable(); folder.add({ rap: item.recentAveragePrice ?? 0 }, 'rap').name('RAP').disable(); folder.add({ id: item.collectibleItemInstanceId }, 'id').name('Instance ID').disable(); folder.add({ remove: () => { const idx = config.inventory.limitedItems.findIndex(i => i.collectibleItemInstanceId === item.collectibleItemInstanceId); if (idx !== -1) { config.inventory.limitedItems.splice(idx, 1); saveLimitedItems(); switchTab('inventory'); } }}, 'remove').name('Remove'); folder.add({ view: () => window.open(`https://www.roblox.com/catalog/${item.assetId}/`, '_blank') }, 'view').name('View on Roblox'); }); } function refreshUsersList() { if (!usersFolder) return; usersFolder.children.slice().forEach(c => { if (c._title?.startsWith('User:')) c.destroy(); }); getUsersList().forEach(user => { const f = usersFolder.addFolder(`User: ${user.displayName} (@${user.name})`); f.close(); f.add({ id: user.id }, 'id').name('ID').disable(); f.add({ verified: user.hasVerifiedBadge ? 'Yes' : 'No' }, 'verified').name('Verified').disable(); f.add({ remove: () => removeSpoofedUser(user.id) }, 'remove').name('Remove'); f.add({ view: () => window.open(`https://www.roblox.com/users/${user.id}/profile`, '_blank') }, 'view').name('View Profile'); }); } function saveFolderStates(container, prefix = '') { const states = {}; for (const f of (container.folders || [])) { const key = prefix + f._title; states[key] = !f._closed; Object.assign(states, saveFolderStates(f, key + '/')); } return states; } function restoreFolderStates(container, states, prefix = '') { for (const f of (container.folders || [])) { const key = prefix + f._title; if (states[key] === true) f.open(); else if (states[key] === false) f.close(); restoreFolderStates(f, states, key + '/'); } } let _pendingFolderStates = null; function switchTab(tabName) { currentTab = tabName; GM_setValue('roblox_spoofer_current_tab', tabName); if (gui) { _pendingFolderStates = saveFolderStates(gui); gui.destroy(); createUI(); } } let _bgElement = null; function applyBackground() { if (_bgElement) { _bgElement.remove(); _bgElement = null; } const existing = document.getElementById('spoofer-bg'); if (existing) existing.remove(); if (!config.uiSettings.backgroundEnabled || !config.uiSettings.backgroundUrl) return; const _doApply = () => { if (_bgElement) { _bgElement.remove(); _bgElement = null; } document.getElementById('spoofer-bg')?.remove(); const el = document.createElement('div'); el.id = 'spoofer-bg'; const opacity = config.uiSettings.backgroundOpacity ?? 0.18; Object.assign(el.style, { position: 'fixed', top: '0', left: '0', width: '100%', height: '100%', zIndex: '2147483646', pointerEvents: 'none', backgroundImage: `url("${config.uiSettings.backgroundUrl.replace(/"/g, '%22')}")`, backgroundSize: 'cover', backgroundPosition: 'center', backgroundRepeat: 'no-repeat', opacity: String(opacity), transform: 'translateZ(0)', willChange: 'opacity', }); const target = document.body || document.documentElement; target.appendChild(el); _bgElement = el; }; if (document.body) { _doApply(); } else { document.addEventListener('DOMContentLoaded', _doApply, { once: true }); } } const EXPORT_SKIP_KEYS = new Set([ 'roblox_spoofer_position', 'roblox_spoofer_ui_hidden', 'roblox_spoofer_current_tab', 'x-csrf-token' ]); function exportData() { const dump = {}; for (const k of GM_listValues()) { if (EXPORT_SKIP_KEYS.has(k)) continue; dump[k] = GM_getValue(k, null); } return btoa(unescape(encodeURIComponent(JSON.stringify(dump)))); } function importData(b64) { let dump; try { dump = JSON.parse(decodeURIComponent(escape(atob(b64.trim())))); } catch { throw new Error('Invalid import string'); } for (const k of GM_listValues()) { if (!EXPORT_SKIP_KEYS.has(k)) GM_deleteValue(k); } for (const [k, v] of Object.entries(dump)) { if (!EXPORT_SKIP_KEYS.has(k)) GM_setValue(k, v); } const fresh = GM_getValue('roblox_spoofer_config', null); if (fresh) { Object.assign(config, fresh); for (const key of ['profile','robux','premium','usd','transactions','marketplace','inventory','avatar','users','account']) { config[key] = { ...defaultConfig[key], ...(config[key] || {}) }; } if (!Array.isArray(config.purchaseTransactions)) config.purchaseTransactions = []; if (!Array.isArray(config.robuxPurchases)) config.robuxPurchases = []; if (!Array.isArray(config.fakeFriendRequests)) config.fakeFriendRequests = []; if (!config.uiSettings || typeof config.uiSettings !== 'object') config.uiSettings = defaultConfig.uiSettings; config.uiSettings = { ...defaultConfig.uiSettings, ...config.uiSettings }; } } function refreshBackgroundStyle() { saveConfig(); applyBackground(); } let _drag = { active: false, ix: 0, iy: 0, ox: 0, oy: 0 }; function _initDragListeners() { document.addEventListener('mousemove', e => { if (!_drag.active) return; e.preventDefault(); _drag.ox = e.clientX - _drag.ix; _drag.oy = e.clientY - _drag.iy; if (gui) Object.assign(gui.domElement.style, { position: 'fixed', left: _drag.ox + 'px', top: _drag.oy + 'px', right: 'auto' }); }); document.addEventListener('mouseup', () => { if (_drag.active) { _drag.active = false; GM_setValue('roblox_spoofer_position', { x: _drag.ox, y: _drag.oy }); } }); } _initDragListeners(); function createUI() { if (typeof lil === 'undefined') { setTimeout(createUI, 100); return; } const savedTab = GM_getValue('roblox_spoofer_current_tab', null); if (savedTab) currentTab = savedTab; const TAB_MIGRATION = { robux: 'currency', trans: 'currency', account: 'identity' }; if (TAB_MIGRATION[currentTab]) currentTab = TAB_MIGRATION[currentTab]; try { gui = new lil.GUI({ title: 'roblox larp [silenthill10]', width: 420 }); } catch { setTimeout(createUI, 100); return; } gui.domElement.style.zIndex = '2147483647'; if (GM_getValue('roblox_spoofer_ui_hidden', null) === true) gui.hide(); const titleBar = gui.domElement.querySelector('.title'); titleBar.style.cursor = 'move'; const savedPos = GM_getValue('roblox_spoofer_position', null); if (savedPos) { Object.assign(gui.domElement.style, { position: 'fixed', left: savedPos.x + 'px', top: savedPos.y + 'px', right: 'auto' }); _drag.ox = savedPos.x; _drag.oy = savedPos.y; } titleBar.addEventListener('mousedown', e => { if (!titleBar.contains(e.target)) return; _drag.ix = e.clientX - _drag.ox; _drag.iy = e.clientY - _drag.oy; _drag.active = true; }); document.getElementById('spoofer-style')?.remove(); const style = document.createElement('style'); style.id = 'spoofer-style'; style.textContent = ` .spoofer-tabs { display:flex; flex-wrap:wrap; gap:4px; padding:8px; background:#111113; border-bottom:1px solid #2a2a2e; } .spoofer-tab { flex:1 1 calc(25% - 4px); min-width:80px; padding:6px 6px; background:#1c1c1f; border:1px solid #2e2e33; color:#888; cursor:pointer; font-size:9px; font-family:'JetBrains Mono',monospace; font-weight:500; letter-spacing:.04em; border-radius:6px; transition:background .18s,color .18s,border-color .18s; display:flex; flex-direction:row; align-items:center; justify-content:center; gap:5px; white-space:nowrap; line-height:1; } .spoofer-tab svg { display:block; width:12px; height:12px; flex-shrink:0; stroke:currentColor; fill:none; stroke-width:1.8; stroke-linecap:round; stroke-linejoin:round; } .spoofer-tab span { overflow:hidden; text-overflow:ellipsis; } .spoofer-tab:hover { background:#242428; border-color:#3e3e45; color:#aaa; } .spoofer-tab.active { background:#2a2a2f; border-color:#555; color:#ddd; font-weight:700; } .lil-gui .controller.boolean input[type=checkbox] { accent-color:#999 !important; } .lil-gui .controller.function .name { font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Arial,sans-serif; } `; document.head.appendChild(style); const lucide = { zap: '<polyline points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>', user: '<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>', coins: '<circle cx="8" cy="8" r="6"/><path d="M18.09 10.37A6 6 0 1 1 10.34 18"/><path d="M7 6h1v4"/><path d="m16.71 13.88.7.71-2.82 2.82"/>', package: '<path d="m16.5 9.4-9-5.19"/><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.29 7 12 12 20.71 7"/><line x1="12" y1="22" x2="12" y2="12"/>', sparkles: '<path d="m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3z"/><path d="M5 3v4"/><path d="M19 17v4"/><path d="M3 5h4"/><path d="M17 19h4"/>', shield: '<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>', users: '<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>', settings: '<circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>', }; const makeSVG = p => `<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">${p}</svg>`; const TABS = [ { id: 'main', label: 'Main', icon: lucide.zap }, { id: 'profile', label: 'Profile', icon: lucide.user }, { id: 'currency', label: 'Currency', icon: lucide.coins }, { id: 'inventory', label: 'Inventory', icon: lucide.package }, { id: 'avatar', label: 'Avatar', icon: lucide.sparkles }, { id: 'identity', label: 'Identity', icon: lucide.shield }, { id: 'users', label: 'Users', icon: lucide.users }, { id: 'settings', label: 'Settings', icon: lucide.settings }, ]; const tabContainer = document.createElement('div'); tabContainer.className = 'spoofer-tabs'; TABS.forEach(t => { const btn = document.createElement('button'); btn.className = `spoofer-tab${currentTab === t.id ? ' active' : ''}`; btn.innerHTML = makeSVG(t.icon) + `<span>${t.label}</span>`; btn.onclick = () => switchTab(t.id); tabContainer.appendChild(btn); }); gui.domElement.querySelector('.title').after(tabContainer); const saveAccount = () => { saveConfig(); const uid = config.spoofUserId || getCurrentUserId(); if (uid) savePerUserData(uid, 'account', config.account); }; if (currentTab === 'main') { gui.add(config, 'enabled').name('Enable Spoofing').onChange(saveConfig); gui.add(config, 'spoofUserId').name('Spoof User ID').onChange(saveConfig); gui.add({ fetch: async () => { const uid = config.spoofUserId || getCurrentUserId(); if (!uid) { alert('No user ID found.'); return; } await fetchUserData(uid); loadUserConfig(uid); switchTab('main'); }}, 'fetch').name('Fetch & Load User Data'); gui.add({ useCurrent: () => { config.spoofUserId = ''; saveConfig(); const cid = getCurrentUserId(); if (cid) loadUserConfig(cid); switchTab('main'); }}, 'useCurrent').name('Use Current User'); const saveFolder = gui.addFolder('Save / Reset'); saveFolder.close(); saveFolder.add({ save: () => { saveCurrentUserConfig(); alert('Per-user data saved!'); }}, 'save').name('Save Current User Data'); } else if (currentTab === 'profile') { const badges = gui.addFolder('Badges'); badges.close(); badges.add(config.profile, 'isPremium').name('Premium Badge').onChange(saveConfig); badges.add(config.profile, 'isVerified').name('Verified Badge').onChange(saveConfig); badges.add(config.profile, 'isRobloxAdmin').name('Admin Badge').onChange(saveConfig); const names = gui.addFolder('Identity'); names.close(); names.add(config.profile, 'name').name('Username').onChange(saveConfig); names.add(config.profile, 'displayName').name('Display Name').onChange(saveConfig); names.add(config.profile, 'description').name('Description').onChange(saveConfig); names.add(config.profile, 'joinDate').name('Join Date').onChange(saveConfig); const social = gui.addFolder('Social Counts'); social.close(); social.add(config.profile, 'friendsCount', 0, 999999999, 1).name('Friends').onChange(saveConfig); social.add(config.profile, 'followersCount', 0, 999999999, 1).name('Followers').onChange(saveConfig); social.add(config.profile, 'followingsCount', 0, 999999999, 1).name('Following').onChange(saveConfig); const existingFR = getFakeFriendRequests(); const frFolder = gui.addFolder(`Friend Requests (${existingFR.length})`); frFolder.close(); const frParams = { userId: '' }; const frAddFolder = frFolder.addFolder('Add Request'); frAddFolder.close(); frAddFolder.add(frParams, 'userId').name('User ID'); frAddFolder.add({ add: async () => { const uid = frParams.userId.trim(); if (!uid) return; try { await addFakeFriendRequest(uid); frParams.userId = ''; switchTab('profile'); } catch (e) { alert(`Failed: ${e.message}`); } }}, 'add').name('Add'); if (existingFR.length) { frFolder.add({ clearAll: () => { if (!confirm('Clear all fake friend requests?')) return; const uid = config.spoofUserId || _cachedUid; const key = uid ? `roblox_spoofer_user_${uid}_fakeFriendRequests` : null; if (key) GM_setValue(key, []); else { config.fakeFriendRequests = []; saveConfig(); } switchTab('profile'); }}, 'clearAll').name('Clear All'); existingFR.forEach((r, i) => { const f = frFolder.addFolder(`${r.displayName} (@${r.name})`); f.close(); f.add({ id: r.id }, 'id').name('User ID').disable(); f.add({ sent: new Date(r.sentAt).toLocaleDateString() }, 'sent').name('Sent').disable(); f.add({ remove: () => { removeFakeFriendRequest(r.id); switchTab('profile'); }}, 'remove').name('Remove'); f.add({ view: () => window.open(`https://www.roblox.com/users/${r.id}/profile`, '_blank') }, 'view').name('View Profile'); }); } } else if (currentTab === 'currency') { const saveRobux = () => { saveConfig(); const uid = config.spoofUserId || getCurrentUserId(); if (uid) savePerUserData(uid, 'robux', config.robux); }; const saveUsd = () => { saveConfig(); const uid = config.spoofUserId || getCurrentUserId(); if (uid) savePerUserData(uid, 'usd', { enabled: config.usd.enabled, amount: config.usd.amount }); }; const robux = gui.addFolder('Robux'); robux.close(); robux.add(config.robux, 'enabled').name('Enable Robux Spoof').onChange(saveRobux); robux.add(config.robux, 'amount', 0, 999999999, 1).name('Robux Amount').onChange(saveRobux); robux.add(config.robux, 'buyableHeadless').name('Buyable Headless').onChange(saveRobux); robux.add(config.marketplace, 'freePurchases').name('Free Purchases').onChange(saveConfig); const usdFolder = gui.addFolder('Credit Balance'); usdFolder.close(); usdFolder.add(config.usd, 'enabled').name('Enable Spoof').onChange(saveUsd); usdFolder.add(config.usd, 'amount', 0, 999999, 0.01).name('Amount').onChange(saveUsd); usdFolder.add(config.usd, 'currencyCode', ['USD', 'EUR', 'GBP', 'CAD', 'AUD', 'JPY', 'BRL', 'MXN', 'INR', 'KRW']).name('Currency').onChange(saveUsd); const gcFolder = usdFolder.addFolder(`Gift Cards (${config.usd.giftCards.length})`); gcFolder.close(); const gcParams = { code: '', value: 10.00 }; const gcAddFolder = gcFolder.addFolder('Add Gift Card'); gcAddFolder.close(); const generateCode = () => { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; const prefix = Math.random() < 0.5 ? 'RI' : 'RB'; const bytes = crypto.getRandomValues(new Uint8Array(16)); return prefix + Array.from(bytes, b => chars[b % chars.length]).join(''); }; const codeController = gcAddFolder.add(gcParams, 'code').name('Code'); gcAddFolder.add(gcParams, 'value', 0.01, 500, 0.01).name('Value ($)'); gcAddFolder.add({ generate: () => { gcParams.code = generateCode(); codeController.updateDisplay(); }}, 'generate').name('Generate Random Code'); gcAddFolder.add({ add: () => { const code = gcParams.code.trim().toUpperCase(); if (!code) return; if (config.usd.giftCards.some(c => c.code.toUpperCase() === code)) { alert('A gift card with that code already exists.'); return; } config.usd.giftCards.push({ code, value: Math.round(gcParams.value * 100) / 100 }); saveConfig(); gcParams.code = ''; switchTab('currency'); } }, 'add').name('Add Card'); config.usd.giftCards.forEach((card, idx) => { const cf = gcFolder.addFolder(`${card.code} — $${parseFloat(card.value).toFixed(2)}`); cf.close(); cf.add({ code: card.code }, 'code').name('Code').disable(); cf.add({ value: `$${parseFloat(card.value).toFixed(2)}` }, 'value').name('Value').disable(); cf.add({ remove: () => { config.usd.giftCards.splice(idx, 1); saveConfig(); switchTab('currency'); }}, 'remove').name('Remove'); }); const premFolder = gui.addFolder('Premium Subscription'); premFolder.close(); premFolder.add(config.premium, 'subscriptionType', ['RobloxPremium450', 'RobloxPremium1000', 'RobloxPremium2200']).name('Plan').onChange(savePremium); premFolder.add({ status: config.premium.createdAt ? `Active since ${new Date(config.premium.createdAt).toLocaleDateString()}` : 'Not started' }, 'status').name('Status').disable(); premFolder.add({ start: () => { const now = new Date(); const monthAgo = new Date(now); monthAgo.setMonth(monthAgo.getMonth() - 1); config.premium.createdAt = now.toISOString(); config.premium.renewedSince = monthAgo.toISOString(); savePremium(); switchTab('currency'); } }, 'start').name(config.premium.createdAt ? 'Restart Subscription' : 'Start Subscription'); if (config.premium.createdAt) { premFolder.add({ clear: () => { config.premium.createdAt = null; config.premium.renewedSince = null; savePremium(); switchTab('currency'); } }, 'clear').name('Clear Subscription'); } const txLabel = k => k.replace(/Total$/, '').replace(/([A-Z])/g, ' $1').trim(); const totals = gui.addFolder('Totals'); totals.close(); const inc = totals.addFolder('Income'); inc.close(); ['salesTotal','affiliateSalesTotal','groupPayoutsTotal','premiumStipendsTotal','premiumPayoutsTotal', 'developerExchangeTotal','adsRevsharePayoutsTotal','groupAdsRevsharePayoutsTotal', 'subscriptionsRevshareTotal','affiliatePayoutTotal','licensingPaymentTotal','publishingAdvanceRebatesTotal' ].forEach(k => inc.add(config.transactions, k, 0, 999999999, 1).name(txLabel(k)).onChange(saveConfig)); const exp = totals.addFolder('Expenses'); exp.close(); ['purchasesTotal','adSpendTotal','currencyPurchasesTotal','tradeSystemCostsTotal', 'subscriptionsRevshareOutgoingTotal','licensingPaymentClawbackOutgoingTotal' ].forEach(k => exp.add(config.transactions, k, 0, 999999999, 1).name(txLabel(k)).onChange(saveConfig)); const flow = totals.addFolder('Flow & Misc'); flow.close(); ['pendingRobuxTotal','incomingRobuxTotal','outgoingRobuxTotal','tradeSystemEarningsTotal', 'individualToGroupTotal','csAdjustmentTotal','groupPremiumPayoutsTotal', 'groupSubscriptionsRevshareTotal','groupSubscriptionsRevshareOutgoingTotal' ].forEach(k => flow.add(config.transactions, k, 0, 999999999, 1).name(txLabel(k)).onChange(saveConfig)); const rbxBuyFolder = gui.addFolder('Robux Purchase History'); rbxBuyFolder.close(); const rbxParams = { amount: 800, date: new Date().toISOString().slice(0, 16) }; const rbxAddFolder = rbxBuyFolder.addFolder('Add Purchase'); rbxAddFolder.close(); rbxAddFolder.add(rbxParams, 'amount', 1, 99999, 1).name('Robux Amount'); rbxAddFolder.add(rbxParams, 'date').name('Date (ISO, local)'); rbxAddFolder.add({ add: () => { const amt = Math.round(rbxParams.amount); if (!amt) return; let isoDate; try { isoDate = new Date(rbxParams.date).toISOString(); } catch { isoDate = new Date().toISOString(); } addRobuxPurchase({ amount: amt, date: isoDate }); switchTab('currency'); } }, 'add').name('Add Entry'); const existingRbxPurchases = getRobuxPurchases(); if (existingRbxPurchases.length) { rbxBuyFolder.add({ clearAll: () => { if (!confirm('Clear all robux purchase history?')) return; const uid = config.spoofUserId || _cachedUid; const key = uid ? `roblox_spoofer_user_${uid}_robuxPurchases` : null; if (key) GM_setValue(key, []); else config.robuxPurchases = []; switchTab('currency'); }}, 'clearAll').name('Clear All'); existingRbxPurchases.slice(0, 20).forEach((p, i) => { const d = new Date(p.created).toLocaleDateString(); const f = rbxBuyFolder.addFolder(`+${p.currency.amount} Robux — ${d}`); f.close(); f.add({ amount: p.currency.amount }, 'amount').name('Amount').disable(); f.add({ date: p.created }, 'date').name('Date').disable(); f.add({ hash: p.idHash }, 'hash').name('ID Hash').disable(); f.add({ remove: () => { const uid = config.spoofUserId || _cachedUid; const key = uid ? `roblox_spoofer_user_${uid}_robuxPurchases` : null; const arr = key ? GM_getValue(key, []) : config.robuxPurchases; arr.splice(i, 1); if (key) GM_setValue(key, arr); else saveConfig(); switchTab('currency'); }}, 'remove').name('Remove'); }); if (existingRbxPurchases.length > 20) { rbxBuyFolder.add({ more: `... and ${existingRbxPurchases.length - 20} more` }, 'more').name('').disable(); } } } else if (currentTab === 'inventory') { const addF = gui.addFolder('Add Item'); addF.close(); const invP = { itemId: '', itemType: 'asset' }; addF.add(invP, 'itemId').name('Item/Bundle ID'); addF.add(invP, 'itemType', ['asset', 'bundle']).name('Type'); addF.add({ add: () => { const id = invP.itemId.trim(); if (!id) return; addItemToInventory(id, invP.itemType); invP.itemId = ''; switchTab('inventory'); }}, 'add').name('Add Item'); gui.add({ clearAll: () => { if (!confirm('Clear all inventory?')) return; config.inventory.purchasedItems = []; config.inventory.limitedItems = []; saveInventory(); switchTab('inventory'); }}, 'clearAll').name('Clear All Items'); gui.add(config.inventory, 'useRealInventory').name('Use Real Inventory').onChange(saveConfig); const ac = config.inventory.purchasedItems.filter(i => i.itemCategory?.itemType === 1).length; const bc = config.inventory.purchasedItems.filter(i => i.itemCategory?.itemType === 2).length; const lc = config.inventory.limitedItems.length; inventoryFolder = gui.addFolder(`Items (${ac} assets, ${bc} bundles, ${lc} limiteds)`); inventoryFolder.close(); refreshInventoryList(); } else if (currentTab === 'avatar') { const status = gui.addFolder('Status'); status.close(); status.add({ outfit: config.avatar.outfit ? '✓ Captured' : '✗ Not captured' }, 'outfit').name('Outfit Status').disable(); status.add({ avatarData: config.avatar.avatarData ? '✓ Captured' : '✗ Not captured' }, 'avatarData').name('Avatar Data').disable(); status.add({ thumbnail: config.avatar.customThumbnail3d ? '✓ Generated' : '✗ Not generated' }, 'thumbnail').name('Thumbnail').disable(); gui.add({ capture: async () => { const r = await fetch(`https://avatar.roblox.com/v2/avatar/users/${getTargetUserId()}/avatar`, { credentials: 'include' }); if (!r.ok) throw new Error(`HTTP ${r.status}`); const d = await r.json(); if (d.bodyColor3s && d.scales && d.playerAvatarType) { config.avatar.avatarData = { bodyColor3s: d.bodyColor3s, scales: d.scales, playerAvatarType: d.playerAvatarType }; saveAvatarData(); switchTab('avatar'); } }}, 'capture').name('Force Capture Avatar Data'); gui.add({ render: async () => { if (!config.avatar.outfit || !config.avatar.avatarData) return; if (await renderAvatarThumbnail()) switchTab('avatar'); }}, 'render').name('Render Thumbnail'); gui.add({ clear: () => { if (!confirm('Clear all avatar data?')) return; config.avatar = { outfit: null, avatarData: null, customThumbnail3d: null, thumbnailGeneratedAt: null }; saveAvatarData(); switchTab('avatar'); }}, 'clear').name('Clear Data'); if (config.avatar.outfit) { const od = gui.addFolder('Outfit Data (Debug)'); od.close(); const ids = config.avatar.outfit.assetIds || config.avatar.outfit.assets?.map(a => a.id) || (Array.isArray(config.avatar.outfit) ? config.avatar.outfit : []); od.add({ count: ids.length }, 'count').name('Asset Count').disable(); if (ids.length) od.add({ assets: ids.slice(0, 5).join(', ') + (ids.length > 5 ? '...' : '') }, 'assets').name('Assets (first 5)').disable(); od.add({ view: () => alert('Outfit data logged to console (F12)') }, 'view').name('View in Console'); } if (config.avatar.avatarData) { const ad = gui.addFolder('Avatar Data (Debug)'); ad.close(); if (config.avatar.avatarData.playerAvatarType) ad.add({ type: config.avatar.avatarData.playerAvatarType }, 'type').name('Avatar Type').disable(); if (config.avatar.avatarData.scales) ad.add({ height: config.avatar.avatarData.scales.height || 0 }, 'height').name('Height Scale').disable(); ad.add({ view: () => alert('Avatar data logged to console (F12)') }, 'view').name('View in Console'); } } else if (currentTab === 'identity') { const email = gui.addFolder('Email'); email.close(); email.add(config.account, 'emailUsername').name('Email Username').onChange(v => { config.account.emailUsername = v.replace('@', ''); saveAccount(); }); email.add(config.account, 'emailDomain').name('Email Domain').onChange(saveAccount); email.add(config.account, 'emailVerified').name('Email Verified').onChange(saveAccount); email.add(config.account, 'isEmailOnFile').name('Email On File').onChange(saveAccount); const loc = gui.addFolder('Location'); loc.close(); loc.add(config.account, 'countryName').name('Country Name').onChange(saveAccount); const sec = gui.addFolder('Security'); sec.close(); sec.add(config.account, 'twoStepEnabled').name('2-Step Auth').onChange(saveAccount); sec.add(config.account, 'phoneFeatureEnabled').name('Phone Feature').onChange(saveAccount); sec.add(config.account, 'hasValidPasswordSet').name('Valid Password').onChange(saveAccount); const perms = gui.addFolder('Permissions & Status'); perms.close(); perms.add(config.account, 'canTrade').name('Can Trade').onChange(saveAccount); perms.add(config.account, 'isPremium').name('Premium').onChange(saveAccount); perms.add(config.account, 'canHideInventory').name('Can Hide Inventory').onChange(saveAccount); perms.add(config.account, 'userAbove13').name('User Above 13').onChange(saveAccount); perms.add(config.account, 'changeUsernameEnabled').name('Can Change Username').onChange(saveAccount); perms.add(config.account, 'robuxRemainingForUsernameChange', 0, 10000, 1).name('Robux for Username').onChange(saveAccount); perms.add(config.account, 'accountAge', 0, 10000, 1).name('Account Age (days)').onChange(saveAccount); } else if (currentTab === 'users') { const addUF = gui.addFolder('Add User'); addUF.close(); const uP = { userId: '' }; addUF.add(uP, 'userId').name('User ID'); addUF.add({ add: async () => { const uid = uP.userId.trim(); if (!uid) return; await addSpoofedUser(uid); uP.userId = ''; switchTab('users'); }}, 'add').name('Add User'); gui.add({ clearAll: () => { if (!confirm('Clear all spoofed users?')) return; saveUsersList([]); saveConfig(); switchTab('users'); }}, 'clearAll').name('Clear All'); const storedIds = getAllStoredUserIds(); if (storedIds.length) { const sf = gui.addFolder(`Per-User Storage (${storedIds.length} users)`); sf.close(); const usersList = getUsersList(); storedIds.forEach(uid => { const label = usersList.find(u => u.id === uid)?.displayName || uid; const uf = sf.addFolder(`UID: ${label} (${uid})`); uf.close(); uf.add({ del: () => { if (confirm(`Delete data for UID ${uid}?`)) { deletePerUserData(uid); switchTab('users'); } }}, 'del').name('Delete Stored Data'); }); } usersFolder = gui.addFolder(`Spoofed Users (${getUsersList().length})`); usersFolder.close(); refreshUsersList(); } else if (currentTab === 'settings') { const bgFolder = gui.addFolder('Background'); bgFolder.close(); bgFolder.add(config.uiSettings, 'backgroundEnabled').name('Enable Background').onChange(refreshBackgroundStyle); bgFolder.add(config.uiSettings, 'backgroundUrl').name('Image URL').onChange(refreshBackgroundStyle); bgFolder.add(config.uiSettings, 'backgroundOpacity', 0.01, 1, 0.01).name('Opacity').onChange(refreshBackgroundStyle); bgFolder.add({ preview: () => { applyBackground(); }}, 'preview').name('Preview'); const exportFolder = gui.addFolder('Export / Import'); exportFolder.close(); const exportParams = { data: '' }; exportFolder.add({ doExport: () => { exportParams.data = exportData(); exportController.updateDisplay(); }}, 'doExport').name('Export Data'); const exportController = exportFolder.add(exportParams, 'data').name('Base64 String'); exportFolder.add({ doCopy: () => { if (!exportParams.data) { exportParams.data = exportData(); exportController.updateDisplay(); } navigator.clipboard.writeText(exportParams.data).catch(() => {}); }}, 'doCopy').name('Copy to Clipboard'); const importParams = { data: '' }; const importFolder = exportFolder.addFolder('Import'); importFolder.close(); importFolder.add(importParams, 'data').name('Paste Base64'); importFolder.add({ doImport: () => { if (!importParams.data.trim()) return; try { importData(importParams.data); importParams.data = ''; alert('Import successful! Reloading UI...'); applyBackground(); switchTab('main'); } catch (e) { alert(`Import failed: ${e.message}`); } }}, 'doImport').name('Import'); const dangerFolder = gui.addFolder('Danger Zone'); dangerFolder.close(); dangerFolder.add({ wipeAll: () => { if (!confirm('Wipe ALL spoofer data? This cannot be undone.')) return; for (const k of GM_listValues()) GM_deleteValue(k); location.reload(); }}, 'wipeAll').name('Wipe All Data & Reload'); } if (_pendingFolderStates) { restoreFolderStates(gui, _pendingFolderStates); _pendingFolderStates = null; } window.gui = gui; } document.addEventListener('keydown', e => { if (e.ctrlKey || e.altKey || e.shiftKey) return; const tag = document.activeElement?.tagName; if (tag === 'INPUT' || tag === 'TEXTAREA' || !gui) return; if (e.key === 'h') { e.preventDefault(); if (gui._hidden) { gui.show(); GM_setValue('roblox_spoofer_ui_hidden', false); } else { gui.hide(); GM_setValue('roblox_spoofer_ui_hidden', true); } } if (e.key === 'r') { e.preventDefault(); GM_deleteValue('roblox_spoofer_position'); for (const prop of ['position', 'left', 'top', 'right']) { gui.domElement.style.removeProperty(prop); } } }); window.addEventListener('spooferReset', () => { for (const k of GM_listValues()) GM_deleteValue(k); console.log('[spoofer] all data wiped — reload the page'); }); const _resetHelper = document.createElement('script'); _resetHelper.textContent = `function spooferReset() { dispatchEvent(new CustomEvent('spooferReset')); console.log('[spoofer] reset triggered'); }`; document.documentElement.appendChild(_resetHelper); _resetHelper.remove(); async function init() { if (window.location.hostname.includes('rolimons.com')) { const spoofedId = GM_getValue('spoofed_user_id', null); const spoofedName = GM_getValue('spoofed_user_name', null); const isLogged = document.cookie.split(';').some(c => c.trim().startsWith('_Roli')); let _name = null, _id = null; Object.defineProperty(unsafeWindow, 'jwt_player_name', { get() { return _name; }, set(v) { _name = (isLogged && spoofedName) ? spoofedName : v; }, configurable: true }); Object.defineProperty(unsafeWindow, 'jwt_player_id', { get() { return _id; }, set(v) { _id = (isLogged && spoofedId) ? Number(spoofedId) : v; }, configurable: true }); applyBackground(); return; } if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', createUI); else createUI(); const removeChat = () => document.getElementById('chat-container')?.remove(); removeChat(); new MutationObserver(mutations => { let checkChat = false; for (const m of mutations) { if (m.type !== 'childList') continue; for (const node of m.removedNodes) { if (node === _cachedMeta) { _cachedMeta = null; break; } } for (const node of m.addedNodes) { if (node.nodeType === 1) { if (node.nodeName === 'META' || node.nodeName === 'HEAD') _cachedMeta = null; if (node.id === 'chat-container' || node.querySelector?.('#chat-container')) checkChat = true; } } } if (checkChat) removeChat(); }).observe(document.documentElement, { childList: true, subtree: true }); if (config.enabled && config.spoofUserId) { _cachedUid = config.spoofUserId; loadUserConfig(config.spoofUserId); if (!config.fetchedData) await fetchUserData(config.spoofUserId); } else { const tryLoadReal = () => { const id = getCurrentUserId(); if (id) { _cachedUid = id; loadUserConfig(id); } }; if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', tryLoadReal); else tryLoadReal(); } if (config.enabled) { interceptMetaTag(); applySpoofing(); startTextReplacement(); } applyBackground(); } init(); })();