Socket interceptor + Vue state reader + auto-play bot + HUD overlay
// ==UserScript==
// @name richup.io Advanced Cheat Suite
// @namespace http://tampermonkey.net/
// @version 2.0.0
// @description Socket interceptor + Vue state reader + auto-play bot + HUD overlay
// @author Fox
// @match *://www.richup.io/*
// @match *://richup.io/*
// @grant GM_addStyle
// @run-at document-start
// ==/UserScript==
(function () {
'use strict';
// ─────────────────────────────────────────────
// CONFIG
// ─────────────────────────────────────────────
const CFG = {
botEnabled: false,
hudEnabled: true,
autoBuy: true, // buy every unowned property landed on
autoBuildHotels: true, // build houses → hotels on complete sets
autoAcceptTrade: false, // auto-accept ALL incoming trades (risky)
rollDelay: 1200, // ms between bot actions
logPackets: true, // dump socket events to console
};
// ─────────────────────────────────────────────
// STYLES — floating HUD
// ─────────────────────────────────────────────
GM_addStyle(`
#ru-hud {
position: fixed;
top: 10px;
right: 10px;
z-index: 999999;
background: rgba(10,10,20,0.92);
color: #e0e0e0;
font: 12px/1.5 'Consolas', monospace;
padding: 10px 14px;
border-radius: 8px;
border: 1px solid #444;
min-width: 260px;
max-height: 80vh;
overflow-y: auto;
box-shadow: 0 4px 24px #000a;
user-select: none;
}
#ru-hud h3 {
margin: 0 0 6px;
color: #7ecfff;
font-size: 13px;
letter-spacing: 1px;
}
#ru-hud .ru-player {
border-bottom: 1px solid #333;
padding: 4px 0;
}
#ru-hud .ru-player:last-child { border: none; }
#ru-hud .ru-name { color: #ffd700; font-weight: bold; }
#ru-hud .ru-money { color: #4cff91; }
#ru-hud .ru-pos { color: #aaa; font-size: 11px; }
#ru-hud .ru-props { color: #c8a0ff; font-size: 11px; }
#ru-hud .ru-status { color: #ff6b6b; font-size: 11px; }
#ru-hud .ru-controls {
margin-top: 8px;
display: flex;
gap: 6px;
flex-wrap: wrap;
}
#ru-hud button {
background: #1a2a3a;
color: #7ecfff;
border: 1px solid #7ecfff44;
border-radius: 4px;
padding: 2px 8px;
cursor: pointer;
font: 11px monospace;
}
#ru-hud button:hover { background: #2a4a6a; }
#ru-hud button.active { background: #0a4a2a; color: #4cff91; border-color: #4cff91; }
`);
// ─────────────────────────────────────────────
// SOCKET.IO INTERCEPTOR
// Hooks into the io() constructor before the
// game's own code calls it, wrapping emit/on
// so we can sniff every packet in both dirs.
// ─────────────────────────────────────────────
const _interceptedSocket = { ref: null };
const _socketListeners = {}; // event → [callbacks]
function hookSocketIO() {
// Poll until socket.io client lib is loaded
const poll = setInterval(() => {
if (typeof window.io !== 'function') return;
clearInterval(poll);
const origIO = window.io;
window.io = function (...args) {
const sock = origIO.apply(this, args);
_interceptedSocket.ref = sock;
patchSocket(sock);
return sock;
};
// copy static props (managers etc.)
Object.assign(window.io, origIO);
console.log('[RU-CHEAT] socket.io hooked');
}, 50);
}
function patchSocket(sock) {
// ── Outgoing: wrap emit ──
const origEmit = sock.emit.bind(sock);
sock.emit = function (event, ...args) {
if (CFG.logPackets)
console.log(`[RU→SRV] ${event}`, ...args);
return origEmit(event, ...args);
};
// ── Incoming: wrap onevent (socket.io v4 internal) ──
const origOnevent = sock.onevent.bind(sock);
sock.onevent = function (packet) {
const [event, ...data] = packet.data || [];
if (CFG.logPackets)
console.log(`[SRV→RU] ${event}`, ...data);
// dispatch to our own listeners
(_socketListeners[event] || []).forEach(cb => {
try { cb(...data); } catch(e) {}
});
return origOnevent(packet);
};
}
// Register a listener for an incoming socket event
function onSock(event, cb) {
if (!_socketListeners[event]) _socketListeners[event] = [];
_socketListeners[event].push(cb);
}
// Emit to server through the hooked socket
function sockEmit(event, ...args) {
if (_interceptedSocket.ref)
_interceptedSocket.ref.emit(event, ...args);
}
// ─────────────────────────────────────────────
// VUE STATE READER
// richup.io uses Vue 3. __vue_app__ is on #app.
// We walk the component tree to find the root
// game store / component state.
// ─────────────────────────────────────────────
function getVueRoot() {
const el = document.querySelector('#app') ||
document.querySelector('[data-v-app]');
if (!el || !el.__vue_app__) return null;
return el.__vue_app__;
}
// Walk component tree, collect all component instances
function collectComponents(vnode, out = []) {
if (!vnode) return out;
if (vnode.component) {
out.push(vnode.component);
collectComponents(vnode.component.subTree, out);
}
if (vnode.shapeFlag & 16) { // ARRAY_CHILDREN
vnode.children?.forEach(c => collectComponents(c, out));
}
return out;
}
// Find a component whose data/props contain a key
function findComponentWith(key) {
const app = getVueRoot();
if (!app) return null;
const comps = collectComponents(app._instance?.subTree);
return comps.find(c =>
c.data?.[key] !== undefined ||
c.props?.[key] !== undefined ||
c.setupState?.[key] !== undefined
);
}
// Get the full game state object — tries common key names
function getGameState() {
for (const key of ['game', 'gameState', 'state', 'room', 'gameData']) {
const comp = findComponentWith(key);
if (comp) {
return comp.data?.[key] ??
comp.setupState?.[key] ??
comp.props?.[key];
}
}
// Fallback: scan all components for a 'players' array
const app = getVueRoot();
if (!app) return null;
const comps = collectComponents(app._instance?.subTree);
for (const c of comps) {
const src = c.setupState ?? c.data ?? {};
if (Array.isArray(src.players) && src.players.length > 0)
return src;
}
return null;
}
// ─────────────────────────────────────────────
// DOM HELPERS — click the right buttons
// ─────────────────────────────────────────────
function clickButton(selector) {
const btn = document.querySelector(selector);
if (btn && !btn.disabled) { btn.click(); return true; }
return false;
}
// Finds a visible, enabled button whose text matches a regex
function clickButtonByText(regex) {
const btns = [...document.querySelectorAll('button')];
const btn = btns.find(b =>
regex.test(b.textContent.trim()) &&
!b.disabled &&
b.offsetParent !== null
);
if (btn) { btn.click(); return true; }
return false;
}
// ─────────────────────────────────────────────
// AUTO-PLAY BOT
// ─────────────────────────────────────────────
let botTimer = null;
function botTick() {
if (!CFG.botEnabled) return;
// Priority order:
// 1. Roll dice if it's our turn and dice haven't been rolled
// 2. Buy property if prompt is visible
// 3. Build houses/hotels
// 4. Handle card draw (OK/Close)
// 5. End turn
// Roll dice
if (clickButtonByText(/roll|throw|dice/i)) return;
// Buy property
if (CFG.autoBuy && clickButtonByText(/buy|purchase/i)) return;
// Build — look for 'Build' or 'House' or 'Hotel' buttons
if (CFG.autoBuildHotels && clickButtonByText(/build|house|hotel/i)) return;
// Dismiss card / OK / Close
if (clickButtonByText(/^ok$|^close$|^got it$|^continue$/i)) return;
// Mortgage decision — never mortgage, click 'No' if asked
if (clickButtonByText(/^no$|^cancel$/i)) return;
// End turn
if (clickButtonByText(/end turn|next turn|finish/i)) return;
}
function startBot() {
if (botTimer) clearInterval(botTimer);
botTimer = setInterval(botTick, CFG.rollDelay);
}
function stopBot() {
clearInterval(botTimer);
botTimer = null;
}
// ─────────────────────────────────────────────
// TRADE SNIPER
// Listens for incoming trade offer packets and
// auto-accepts if the offer is in our favor.
// ─────────────────────────────────────────────
onSock('trade:offer', (offer) => {
console.log('[RU-CHEAT] Trade offer received:', offer);
if (!CFG.autoAcceptTrade) return;
// Simple heuristic: accept if we receive more money than we give
const weGet = (offer.toMoney || 0);
const weGive = (offer.fromMoney || 0);
if (weGet >= weGive) {
console.log('[RU-CHEAT] Auto-accepting trade (favorable)');
sockEmit('trade:accept', { tradeId: offer.id });
}
});
// Also try common event names
['tradeOffer', 'trade_offer', 'offer'].forEach(ev => {
onSock(ev, (data) => {
console.log(`[RU-CHEAT] Trade event '${ev}':`, data);
});
});
// ─────────────────────────────────────────────
// HUD OVERLAY
// ─────────────────────────────────────────────
let hudEl = null;
function createHUD() {
hudEl = document.createElement('div');
hudEl.id = 'ru-hud';
hudEl.innerHTML = `
<h3>RU-CHEAT v2.0</h3>
<div id="ru-hud-players">Waiting for game state...</div>
<div class="ru-controls">
<button id="ru-btn-bot">BOT: OFF</button>
<button id="ru-btn-buy">AUTO-BUY: ON</button>
<button id="ru-btn-build">AUTO-BUILD: ON</button>
<button id="ru-btn-trade">AUTO-TRADE: OFF</button>
<button id="ru-btn-dump">DUMP STATE</button>
</div>
`;
document.body.appendChild(hudEl);
document.getElementById('ru-btn-bot').onclick = () => {
CFG.botEnabled = !CFG.botEnabled;
CFG.botEnabled ? startBot() : stopBot();
updateHUDControls();
};
document.getElementById('ru-btn-buy').onclick = () => {
CFG.autoBuy = !CFG.autoBuy;
updateHUDControls();
};
document.getElementById('ru-btn-build').onclick = () => {
CFG.autoBuildHotels = !CFG.autoBuildHotels;
updateHUDControls();
};
document.getElementById('ru-btn-trade').onclick = () => {
CFG.autoAcceptTrade = !CFG.autoAcceptTrade;
updateHUDControls();
};
document.getElementById('ru-btn-dump').onclick = () => {
const state = getGameState();
console.log('[RU-CHEAT] Full game state dump:', state);
alert('Game state dumped to console (F12)');
};
}
function updateHUDControls() {
const botBtn = document.getElementById('ru-btn-bot');
const buyBtn = document.getElementById('ru-btn-buy');
const buildBtn = document.getElementById('ru-btn-build');
const tradeBtn = document.getElementById('ru-btn-trade');
if (!botBtn) return;
botBtn.textContent = `BOT: ${CFG.botEnabled ? 'ON' : 'OFF'}`;
botBtn.className = CFG.botEnabled ? 'active' : '';
buyBtn.textContent = `AUTO-BUY: ${CFG.autoBuy ? 'ON' : 'OFF'}`;
buyBtn.className = CFG.autoBuy ? 'active' : '';
buildBtn.textContent = `AUTO-BUILD: ${CFG.autoBuildHotels ? 'ON' : 'OFF'}`;
buildBtn.className = CFG.autoBuildHotels ? 'active' : '';
tradeBtn.textContent = `AUTO-TRADE: ${CFG.autoAcceptTrade ? 'ON' : 'OFF'}`;
tradeBtn.className = CFG.autoAcceptTrade ? 'active' : '';
}
function updateHUDPlayers() {
const el = document.getElementById('ru-hud-players');
if (!el) return;
const state = getGameState();
if (!state || !state.players) {
el.textContent = 'Game state not found yet...';
return;
}
el.innerHTML = state.players.map(p => {
const props = (p.properties || p.ownedProperties || []);
const propNames = props.map(pr => pr.name || pr.id || pr).join(', ') || 'none';
const bankrupt = p.bankrupt || p.isBankrupt ? '<span class="ru-status"> [BANKRUPT]</span>' : '';
const isTurn = (state.currentPlayer === p.id || state.currentPlayerId === p.id)
? ' ◀ TURN' : '';
return `
<div class="ru-player">
<span class="ru-name">${p.name || p.username || 'Player'}</span>${bankrupt}
<span style="color:#aaa">${isTurn}</span><br>
<span class="ru-money">$${(p.money ?? p.balance ?? '?').toLocaleString()}</span>
<span class="ru-pos"> · Pos: ${p.position ?? p.pos ?? '?'}</span><br>
<span class="ru-props">Props: ${propNames}</span>
</div>
`;
}).join('');
}
// ─────────────────────────────────────────────
// HOTKEYS
// ─────────────────────────────────────────────
document.addEventListener('keydown', (e) => {
if (e.key === 'F2') {
e.preventDefault();
CFG.botEnabled = !CFG.botEnabled;
CFG.botEnabled ? startBot() : stopBot();
updateHUDControls();
console.log(`[RU-CHEAT] Bot ${CFG.botEnabled ? 'ENABLED' : 'DISABLED'}`);
}
if (e.key === 'F3') {
e.preventDefault();
console.log('[RU-CHEAT] State dump:', getGameState());
}
if (e.key === 'F4') {
e.preventDefault();
CFG.hudEnabled = !CFG.hudEnabled;
if (hudEl) hudEl.style.display = CFG.hudEnabled ? '' : 'none';
}
});
// ─────────────────────────────────────────────
// INIT
// ─────────────────────────────────────────────
hookSocketIO(); // must run before page JS loads (run-at: document-start)
// Wait for DOM to be ready, then build HUD and start polling
window.addEventListener('DOMContentLoaded', () => {
createHUD();
// Refresh HUD every second
setInterval(() => {
if (CFG.hudEnabled) updateHUDPlayers();
updateHUDControls();
}, 1000);
});
console.log('[RU-CHEAT] Loaded. F2=bot, F3=dump state, F4=toggle HUD');
})();