oh fuck i forgot
This commit is contained in:
475
extension/injected_bot.js
Normal file
475
extension/injected_bot.js
Normal file
@@ -0,0 +1,475 @@
|
||||
(function () {
|
||||
console.log("%c Magic Garden Bot Extension Loaded ", "background: #222; color: #bada55; font-size: 20px");
|
||||
|
||||
// --- LOGIC: WebSocket Hook ---
|
||||
window.gameSocket = null;
|
||||
const OriginalSend = WebSocket.prototype.send;
|
||||
|
||||
WebSocket.prototype.send = function (data) {
|
||||
if (!window.gameSocket || window.gameSocket.readyState >= 2) {
|
||||
window.gameSocket = this;
|
||||
updateStatus(true);
|
||||
|
||||
// Extract Player ID from URL
|
||||
try {
|
||||
const urlObj = new URL(this.url);
|
||||
let pid = urlObj.searchParams.get('playerId');
|
||||
// Remove quotes content if wrapped in " " (e.g. from %22p_...%22)
|
||||
if (pid) pid = pid.replace(/^"|"$/g, '');
|
||||
gameState.playerId = pid;
|
||||
console.log("[MagicBot] Captured Player ID:", gameState.playerId);
|
||||
} catch (e) {
|
||||
console.error("[MagicBot] Failed to parse Player ID:", e);
|
||||
}
|
||||
|
||||
this.addEventListener('close', () => {
|
||||
updateStatus(false);
|
||||
});
|
||||
|
||||
// Capture Incoming
|
||||
this.addEventListener('message', (e) => {
|
||||
logToOverlay('RX', e.data);
|
||||
try {
|
||||
const msg = JSON.parse(e.data);
|
||||
if (msg.type === 'Welcome') {
|
||||
handleWelcome(msg);
|
||||
}
|
||||
} catch (err) { }
|
||||
});
|
||||
}
|
||||
|
||||
// Capture Outgoing
|
||||
logToOverlay('TX', data);
|
||||
|
||||
return OriginalSend.apply(this, arguments);
|
||||
};
|
||||
|
||||
// --- STATE MANAGEMENT ---
|
||||
let gameState = {
|
||||
coins: 0,
|
||||
garden: {},
|
||||
inventory: {},
|
||||
playerId: null
|
||||
};
|
||||
|
||||
function handleWelcome(msg) {
|
||||
console.log("Parsing Welcome Message...");
|
||||
const root = msg.fullState.data;
|
||||
|
||||
console.log("Looking for slot for PlayerID:", gameState.playerId);
|
||||
|
||||
// Find our user slot
|
||||
const userSlot = root.userSlots.find(s => s.playerId === gameState.playerId);
|
||||
|
||||
if (userSlot) {
|
||||
console.log("Found User Slot!", userSlot);
|
||||
gameState.coins = userSlot.data.coinsCount;
|
||||
gameState.garden = userSlot.data.garden.tileObjects;
|
||||
gameState.inventory = userSlot.data.inventory;
|
||||
|
||||
updateVisuals();
|
||||
} else {
|
||||
console.error("Could not find user slot for ID:", gameState.playerId);
|
||||
// Debug: Log all available IDs
|
||||
console.log("Available Slots:", root.userSlots.map(s => s.playerId));
|
||||
}
|
||||
}
|
||||
|
||||
function updateVisuals() {
|
||||
// Update Wallet
|
||||
const walletContent = document.getElementById('window-wallet-content');
|
||||
if (walletContent) {
|
||||
walletContent.innerHTML = `
|
||||
<div style="font-size: 24px; color: #ffd700; text-align: center;">CA$H: ${gameState.coins.toLocaleString()}</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// Update Garden
|
||||
const gardenContent = document.getElementById('window-garden-content');
|
||||
if (gardenContent) {
|
||||
let html = '<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 5px;">';
|
||||
const now = Date.now();
|
||||
|
||||
Object.entries(gameState.garden).forEach(([slotId, tile]) => {
|
||||
const crop = tile.slots[0]; // Assuming single slot for now
|
||||
if (!crop) return;
|
||||
|
||||
const isReady = now >= tile.maturedAt;
|
||||
const timeLeft = Math.max(0, Math.ceil((tile.maturedAt - now) / 1000));
|
||||
const color = isReady ? '#66bb6a' : '#ffa726';
|
||||
|
||||
html += `
|
||||
<div style="background: rgba(255,255,255,0.05); padding: 5px; border-radius: 4px; border: 1px solid ${color}; font-size: 10px; text-align: center;">
|
||||
<div style="font-weight: bold; color: ${color};">${tile.species}</div>
|
||||
<div>${slotId}</div>
|
||||
<div>${isReady ? 'READY' : timeLeft + 's'}</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
html += '</div>';
|
||||
gardenContent.innerHTML = html;
|
||||
}
|
||||
}
|
||||
|
||||
// --- UI HELPER: Floating Windows ---
|
||||
function createFloatingWindow(id, title, x, y, width, height) {
|
||||
if (document.getElementById(id)) return; // Already exists
|
||||
|
||||
const win = document.createElement('div');
|
||||
win.id = id;
|
||||
win.style.cssText = `
|
||||
position: fixed;
|
||||
top: ${y}px;
|
||||
left: ${x}px;
|
||||
width: ${width}px;
|
||||
height: ${height}px;
|
||||
background: rgba(10, 10, 20, 0.9);
|
||||
border: 1px solid #444;
|
||||
border-radius: 8px;
|
||||
z-index: 9999995;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: 0 44px 15px rgba(0,0,0,0.5);
|
||||
font-family: sans-serif;
|
||||
color: #eee;
|
||||
backdrop-filter: blur(5px);
|
||||
resize: both;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
win.innerHTML = `
|
||||
<div id="${id}-header" style="padding: 8px; background: rgba(255,255,255,0.05); border-bottom: 1px solid #444; cursor: move; font-weight: bold; font-size: 12px; display: flex; justify-content: space-between;">
|
||||
<span>${title}</span>
|
||||
<span style="cursor: pointer; color: #ff5252;" onclick="document.getElementById('${id}').remove()">✕</span>
|
||||
</div>
|
||||
<div id="${id}-content" style="flex: 1; overflow-y: auto; padding: 10px;"></div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(win);
|
||||
|
||||
// Drag Logic
|
||||
const header = win.querySelector(`#${id}-header`);
|
||||
let isDragging = false, offX = 0, offY = 0;
|
||||
|
||||
header.addEventListener('mousedown', (e) => {
|
||||
isDragging = true;
|
||||
offX = e.clientX - win.offsetLeft;
|
||||
offY = e.clientY - win.offsetTop;
|
||||
});
|
||||
window.addEventListener('mousemove', (e) => {
|
||||
if (isDragging) {
|
||||
win.style.left = (e.clientX - offX) + 'px';
|
||||
win.style.top = (e.clientY - offY) + 'px';
|
||||
}
|
||||
});
|
||||
window.addEventListener('mouseup', () => isDragging = false);
|
||||
}
|
||||
|
||||
// --- LOGIC: Commands ---
|
||||
function sendMsg(msg) {
|
||||
if (!window.gameSocket) {
|
||||
alert("Socket not connected! Wait for game to load.");
|
||||
return;
|
||||
}
|
||||
window.gameSocket.send(JSON.stringify(msg));
|
||||
}
|
||||
|
||||
async function harvestLoop(start, end, count, delay) {
|
||||
let sent = 0;
|
||||
for (let slot = start; slot <= end; slot++) {
|
||||
for (let i = 0; i < count; i++) {
|
||||
sendMsg({
|
||||
type: 'HarvestCrop',
|
||||
slot: slot,
|
||||
slotsIndex: i,
|
||||
scopePath: ["Room", "Quinoa"]
|
||||
});
|
||||
sent++;
|
||||
await new Promise(r => setTimeout(r, delay));
|
||||
}
|
||||
}
|
||||
console.log(`Harvested ${sent} items.`);
|
||||
}
|
||||
|
||||
// --- UI: Sidebar ---
|
||||
const sidebar = document.createElement('div');
|
||||
sidebar.id = 'magic-bot-sidebar';
|
||||
sidebar.style.cssText = `
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
width: 280px;
|
||||
background: rgba(20, 20, 25, 0.95);
|
||||
color: #eee;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
z-index: 9999999;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
box-shadow: 0 4px 15px rgba(0,0,0,0.6);
|
||||
border: 1px solid #444;
|
||||
backdrop-filter: blur(5px);
|
||||
`;
|
||||
|
||||
sidebar.innerHTML = `
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
|
||||
<h3 style="margin: 0; color: #bada55; font-size: 18px;">🌱 Magic Bot</h3>
|
||||
<div id="status-indicator" style="font-size: 12px; font-weight: bold; color: #ff5252;">● Disconnected</div>
|
||||
</div>
|
||||
|
||||
<!-- Teleport -->
|
||||
<div style="background: rgba(255,255,255,0.05); padding: 10px; border-radius: 8px; margin-bottom: 15px;">
|
||||
<div id="head-tp" style="display: flex; justify-content: space-between; cursor: pointer; margin-bottom: 5px; user-select: none;">
|
||||
<label style="font-size: 12px; color: #aaa; cursor: pointer;">Teleport</label>
|
||||
<span id="arrow-tp" style="font-size: 10px; color: #aaa;">▼</span>
|
||||
</div>
|
||||
<div id="section-tp">
|
||||
<div style="display: flex; gap: 8px;">
|
||||
<input type="number" id="tp-x" placeholder="X" value="15" style="width: 100%; padding: 6px; background: #333; border: 1px solid #555; color: white; border-radius: 4px;">
|
||||
<input type="number" id="tp-y" placeholder="Y" value="15" style="width: 100%; padding: 6px; background: #333; border: 1px solid #555; color: white; border-radius: 4px;">
|
||||
<button id="btn-tp" style="cursor: pointer; background: #448aff; color: white; border: none; padding: 0 15px; border-radius: 4px; font-weight: bold;">Go</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Harvest -->
|
||||
<div style="background: rgba(255,255,255,0.05); padding: 10px; border-radius: 8px; margin-bottom: 15px;">
|
||||
<div id="head-hv" style="display: flex; justify-content: space-between; cursor: pointer; margin-bottom: 5px; user-select: none;">
|
||||
<label style="font-size: 12px; color: #aaa; cursor: pointer;">Harvest</label>
|
||||
<span id="arrow-hv" style="font-size: 10px; color: #aaa;">▼</span>
|
||||
</div>
|
||||
<div id="section-hv">
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 8px;">
|
||||
<div>
|
||||
<span style="font-size: 10px; color: #888;">Start Slot</span>
|
||||
<input type="number" id="hv-start" value="140" style="width: 100%; padding: 6px; background: #333; border: 1px solid #555; color: white; border-radius: 4px;">
|
||||
</div>
|
||||
<div>
|
||||
<span style="font-size: 10px; color: #888;">End Slot</span>
|
||||
<input type="number" id="hv-end" value="160" style="width: 100%; padding: 6px; background: #333; border: 1px solid #555; color: white; border-radius: 4px;">
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 10px;">
|
||||
<div>
|
||||
<span style="font-size: 10px; color: #888;">Iter. (Count)</span>
|
||||
<input type="number" id="hv-count" value="1" style="width: 100%; padding: 6px; background: #333; border: 1px solid #555; color: white; border-radius: 4px;">
|
||||
</div>
|
||||
<div>
|
||||
<span style="font-size: 10px; color: #888;">Delay (ms)</span>
|
||||
<input type="number" id="hv-delay" value="20" style="width: 100%; padding: 6px; background: #333; border: 1px solid #555; color: white; border-radius: 4px;">
|
||||
</div>
|
||||
</div>
|
||||
<button id="btn-harvest" style="width: 100%; cursor: pointer; background: #66bb6a; color: white; border: none; padding: 8px; border-radius: 4px; font-weight: bold; transition: background 0.2s;">Run Harvest</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button id="btn-sell" style="width: 100%; cursor: pointer; background: #ffa726; color: white; border: none; padding: 10px; border-radius: 8px; font-weight: bold; box-shadow: 0 2px 5px rgba(0,0,0,0.2);">Sell All Crops</button>
|
||||
|
||||
<div style="border-top: 1px solid #444; margin: 10px 0;"></div>
|
||||
|
||||
<div style="display: flex; gap: 5px;">
|
||||
<button id="btn-logs" style="flex: 1; cursor: pointer; background: #5c6bc0; color: white; border: none; padding: 8px; border-radius: 4px; font-weight: bold;">Logs</button>
|
||||
<button id="btn-state" style="flex: 1; cursor: pointer; background: #ab47bc; color: white; border: none; padding: 8px; border-radius: 4px; font-weight: bold;">State</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(sidebar);
|
||||
|
||||
document.getElementById('btn-state').onclick = () => {
|
||||
createFloatingWindow('window-wallet', '💰 Wallet', 20, 400, 200, 80);
|
||||
createFloatingWindow('window-garden', '🌻 Garden', 240, 400, 300, 300);
|
||||
updateVisuals(); // Refresh if data exists
|
||||
};
|
||||
|
||||
// --- LOG OVERLAY ---
|
||||
const logOverlay = document.createElement('div');
|
||||
logOverlay.id = 'magic-bot-logs';
|
||||
logOverlay.style.cssText = `
|
||||
display: none;
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
left: 20px;
|
||||
width: 600px;
|
||||
height: 300px;
|
||||
background: rgba(10, 10, 15, 0.95);
|
||||
border: 1px solid #444;
|
||||
border-radius: 8px;
|
||||
z-index: 9999999;
|
||||
font-family: 'Consolas', 'Monaco', monospace;
|
||||
font-size: 11px;
|
||||
color: #eee;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: 0 4px 15px rgba(0,0,0,0.6);
|
||||
resize: both;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
logOverlay.innerHTML = `
|
||||
<div style="padding: 5px 10px; background: #222; border-bottom: 1px solid #444; display: flex; justify-content: space-between; align-items: center; cursor: move;" id="log-header">
|
||||
<span style="font-weight: bold; color: #aaa;">Network Logs</span>
|
||||
<div>
|
||||
<button id="btn-clear-logs" style="background: none; border: none; color: #888; cursor: pointer; margin-right: 10px;">Clear</button>
|
||||
<button id="btn-close-logs" style="background: none; border: none; color: #ff5252; cursor: pointer;">X</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="log-content" style="flex: 1; overflow-y: auto; padding: 5px; word-break: break-all;"></div>
|
||||
`;
|
||||
document.body.appendChild(logOverlay);
|
||||
logOverlay.style.display = 'none'; // Ensure hidden initially
|
||||
|
||||
// Make Draggable
|
||||
let isDragging = false;
|
||||
let dragOffsetX = 0;
|
||||
let dragOffsetY = 0;
|
||||
const header = logOverlay.querySelector('#log-header');
|
||||
|
||||
header.addEventListener('mousedown', (e) => {
|
||||
isDragging = true;
|
||||
dragOffsetX = e.clientX - logOverlay.offsetLeft;
|
||||
dragOffsetY = e.clientY - logOverlay.offsetTop;
|
||||
});
|
||||
|
||||
window.addEventListener('mousemove', (e) => {
|
||||
if (isDragging) {
|
||||
logOverlay.style.left = (e.clientX - dragOffsetX) + 'px';
|
||||
logOverlay.style.top = (e.clientY - dragOffsetY) + 'px';
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('mouseup', () => isDragging = false);
|
||||
|
||||
// Stop propagation for logs too
|
||||
logOverlay.addEventListener('keydown', (e) => e.stopPropagation());
|
||||
logOverlay.addEventListener('keyup', (e) => e.stopPropagation());
|
||||
logOverlay.addEventListener('keypress', (e) => e.stopPropagation());
|
||||
|
||||
function logToOverlay(type, data) {
|
||||
const container = document.getElementById('log-content');
|
||||
if (!container) return;
|
||||
|
||||
// Check if we are near the bottom BEFORE adding new content (within 50px)
|
||||
const isAtBottom = (container.scrollHeight - container.scrollTop - container.clientHeight) < 50;
|
||||
|
||||
const line = document.createElement('div');
|
||||
line.style.borderBottom = '1px solid #222';
|
||||
line.style.padding = '2px 0';
|
||||
|
||||
const timestamp = new Date().toLocaleTimeString();
|
||||
let color = type === 'TX' ? '#66bb6a' : '#448aff';
|
||||
let content = data;
|
||||
|
||||
// Pretty print JSON
|
||||
try {
|
||||
if (typeof data === 'string' && (data.startsWith('{') || data.startsWith('['))) {
|
||||
content = JSON.stringify(JSON.parse(data)); // Minify
|
||||
}
|
||||
} catch (e) { }
|
||||
|
||||
line.innerHTML = `<span style="color: #666;">[${timestamp}]</span> <span style="color: ${color}; font-weight: bold;">${type}</span> <span style="color: #ccc;">${content}</span>`;
|
||||
|
||||
// Click to copy
|
||||
line.style.cursor = 'pointer';
|
||||
line.title = 'Click to copy raw message';
|
||||
line.addEventListener('click', () => {
|
||||
navigator.clipboard.writeText(data).then(() => {
|
||||
// Visual feedback
|
||||
line.style.background = 'rgba(255, 255, 255, 0.1)';
|
||||
setTimeout(() => line.style.background = 'transparent', 200);
|
||||
console.log('Copied to clipboard:', data);
|
||||
}).catch(err => console.error('Failed to copy matches:', err));
|
||||
});
|
||||
|
||||
container.appendChild(line);
|
||||
|
||||
// Auto scroll ONLY if we were already at the bottom
|
||||
if (isAtBottom) {
|
||||
container.scrollTop = container.scrollHeight;
|
||||
}
|
||||
|
||||
// Limit history (500 lines)
|
||||
if (container.children.length > 500) {
|
||||
container.removeChild(container.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
// Stop keyboard events from reaching the game (Fixes specific keys not working)
|
||||
sidebar.addEventListener('keydown', (e) => e.stopPropagation());
|
||||
sidebar.addEventListener('keyup', (e) => e.stopPropagation());
|
||||
sidebar.addEventListener('keypress', (e) => e.stopPropagation());
|
||||
|
||||
// --- COLLAPSE LOGIC ---
|
||||
const setupCollapse = (headId, bodyId, arrowId) => {
|
||||
document.getElementById(headId).onclick = () => {
|
||||
const el = document.getElementById(bodyId);
|
||||
const arrow = document.getElementById(arrowId);
|
||||
if (el.style.display === 'none') {
|
||||
el.style.display = 'block';
|
||||
arrow.textContent = '▼';
|
||||
} else {
|
||||
el.style.display = 'none';
|
||||
arrow.textContent = '▶';
|
||||
}
|
||||
};
|
||||
};
|
||||
setupCollapse('head-tp', 'section-tp', 'arrow-tp');
|
||||
setupCollapse('head-hv', 'section-hv', 'arrow-hv');
|
||||
|
||||
document.getElementById('btn-logs').onclick = () => {
|
||||
const el = document.getElementById('magic-bot-logs');
|
||||
el.style.display = el.style.display === 'none' ? 'flex' : 'none';
|
||||
// Reset position if off-screen (optional safety)
|
||||
if (el.style.display === 'flex') {
|
||||
el.style.top = 'auto';
|
||||
el.style.bottom = '20px';
|
||||
el.style.left = '20px';
|
||||
}
|
||||
};
|
||||
|
||||
document.getElementById('btn-close-logs').onclick = () => {
|
||||
document.getElementById('magic-bot-logs').style.display = 'none';
|
||||
};
|
||||
|
||||
document.getElementById('btn-clear-logs').onclick = () => {
|
||||
document.getElementById('log-content').innerHTML = '';
|
||||
};
|
||||
|
||||
// --- UI LOGIC ---
|
||||
function updateStatus(connected) {
|
||||
const el = document.getElementById('status-indicator');
|
||||
if (connected) {
|
||||
el.innerHTML = '● Connected';
|
||||
el.style.color = '#66bb6a'; // Green
|
||||
} else {
|
||||
el.innerHTML = '● Disconnected';
|
||||
el.style.color = '#ff5252'; // Red
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('btn-tp').onclick = () => {
|
||||
const x = parseInt(document.getElementById('tp-x').value);
|
||||
const y = parseInt(document.getElementById('tp-y').value);
|
||||
|
||||
sendMsg({
|
||||
type: 'Teleport',
|
||||
position: { x, y },
|
||||
scopePath: ["Room", "Quinoa"]
|
||||
});
|
||||
};
|
||||
|
||||
document.getElementById('btn-harvest').onclick = () => {
|
||||
const start = parseInt(document.getElementById('hv-start').value);
|
||||
const end = parseInt(document.getElementById('hv-end').value);
|
||||
const count = parseInt(document.getElementById('hv-count').value);
|
||||
const delay = parseInt(document.getElementById('hv-delay').value);
|
||||
|
||||
harvestLoop(start, end, count, delay);
|
||||
};
|
||||
|
||||
document.getElementById('btn-sell').onclick = () => {
|
||||
sendMsg({
|
||||
type: 'SellAllCrops',
|
||||
scopePath: ["Room", "Quinoa"]
|
||||
});
|
||||
};
|
||||
|
||||
})();
|
||||
Reference in New Issue
Block a user