// ==UserScript==
// @name @akoge
// @namespace thelarpplug 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/lil-gui@0.19.2/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 [@suxitok]', 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: '',
user: '',
coins: '',
package: '',
sparkles: '',
shield: '',
users: '',
settings: '',
};
const makeSVG = p => ``;
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) + `${t.label}`;
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();
})();