'use strict'; const { sleep } = require('../../utils'); const { CJbot } = require('../../model/minecraft'); // Owner players who can run admin commands const owners = ['wmantly', 'useless666', 'tux4242']; // Team players who can use basic storage features const _team = [...owners, 'pi_chef', 'Ethan', 'Vince_NL']; // Dynamic team list that includes all bot usernames Object.defineProperty(Array.prototype, '_includesWithBots', { value: undefined, writable: true }); const team = new Proxy(_team, { get(target, prop) { if (prop === 'includes') { return (name) => { if (target.includes(name)) return true; for (const bot of Object.values(CJbot.bots)) { if (bot.bot && bot.bot.entity && bot.bot.entity.username === name) return true; } return false; }; } return target[prop]; } }); const botSlots = [0, 1, 2, 3, 9, 10, 11, 12, 18, 19, 20, 21]; const customerSlots = [5, 6, 7, 8, 14, 15, 16, 17, 23, 24, 25, 26]; module.exports = { 'scan': { desc: 'Force chest area scan', allowed: owners, ignoreLock: true, async function(from) { console.log(`Storage command 'scan' from ${from}`); const storage = this.plunginsLoaded['Storage']; if (!storage) return this.whisper(from, 'Storage plugin not loaded'); await storage.handleCommand(from, 'scan'); } }, 'status': { desc: 'Show storage stats', allowed: team, async function(from) { console.log(`Storage command 'status' from ${from}`); const storage = this.plunginsLoaded['Storage']; if (!storage) return this.whisper(from, 'Storage plugin not loaded'); await storage.handleCommand(from, 'status'); } }, 'withdraw': { desc: 'Withdraw items from storage (use "3s" for 3 shulkers)', allowed: team, async function(from, itemName, countStr) { console.log(`Storage command 'withdraw' from ${from}: ${itemName} x${countStr}`); const storage = this.plunginsLoaded['Storage']; if (!storage) return this.whisper(from, 'Storage plugin not loaded'); // Parse count — "3s" means 3 shulkers, "10" means 10 items const str = (countStr || '1').toString().trim(); if (str.endsWith('s') || str.endsWith('S')) { const shulkerCount = parseInt(str) || 1; await storage.handleCommand(from, 'withdraw-shulkers', itemName, shulkerCount); } else { const count = parseInt(str) || 1; await storage.handleCommand(from, 'withdraw', itemName, count); } } }, 'find': { desc: 'Search for an item', allowed: team, async function(from, itemName) { console.log(`Storage command 'find' from ${from}: ${itemName}`); const storage = this.plunginsLoaded['Storage']; if (!storage) return this.whisper(from, 'Storage plugin not loaded'); await storage.handleCommand(from, 'find', itemName); } }, 'chests': { desc: 'List tracked chests', allowed: owners, ignoreLock: true, async function(from) { console.log(`Storage command 'chests' from ${from}`); const storage = this.plunginsLoaded['Storage']; if (!storage) return this.whisper(from, 'Storage plugin not loaded'); await storage.handleCommand(from, 'chests'); } }, 'organize': { desc: 'Force full re-sort', allowed: owners, ignoreLock: true, async function(from) { console.log(`Storage command 'organize' from ${from}`); const storage = this.plunginsLoaded['Storage']; if (!storage) return this.whisper(from, 'Storage plugin not loaded'); await storage.handleCommand(from, 'organize'); } }, 'consolidate': { desc: 'Merge partially filled shulkers', allowed: owners, ignoreLock: true, async function(from) { console.log(`Storage command 'consolidate' from ${from}`); const storage = this.plunginsLoaded['Storage']; if (!storage) return this.whisper(from, 'Storage plugin not loaded'); await storage.handleCommand(from, 'consolidate'); } }, 'addplayer': { desc: 'Add player to storage', allowed: owners, ignoreLock: true, async function(from, name, role = 'team') { console.log(`Storage command 'addplayer' from ${from}: ${name} as ${role}`); const storage = this.plunginsLoaded['Storage']; if (!storage) return this.whisper(from, 'Storage plugin not loaded'); await storage.handleCommand(from, 'addplayer', name, role); } }, 'removeplayer': { desc: 'Remove player from storage', allowed: owners, ignoreLock: true, async function(from, name) { console.log(`Storage command 'removeplayer' from ${from}: ${name}`); const storage = this.plunginsLoaded['Storage']; if (!storage) return this.whisper(from, 'Storage plugin not loaded'); await storage.handleCommand(from, 'removeplayer', name); } }, 'players': { desc: 'List authorized players', allowed: owners, ignoreLock: true, async function(from) { console.log(`Storage command 'players' from ${from}`); const storage = this.plunginsLoaded['Storage']; if (!storage) return this.whisper(from, 'Storage plugin not loaded'); await storage.handleCommand(from, 'players'); } }, '.trade': { desc: 'Handle trade deposits/withdrawals for storage', allowed: team, ignoreLock: true, async function(from) { const storage = this.plunginsLoaded['Storage']; if (!storage) return; storage._busy = true; try { const pending = storage.pendingWithdrawals.get(from); await this.say('/trade accept'); let window = await this.once('windowOpen'); // If there's a pending withdrawal, place items in bot's trade slots if (pending) { console.log(`Storage trade: Withdrawal pickup for ${from} — ${pending.count}x ${pending.itemName} (mode: ${pending.mode})`); let placed = 0; for (const slotNum of botSlots) { if (placed >= 12) break; // Find matching item in bot inventory portion of trade window for (let i = window.inventoryStart; i < window.inventoryEnd; i++) { const item = window.slots[i]; if (!item) continue; if (pending.mode === 'shulkers') { if (!item.name.includes('shulker_box')) continue; } else { if (item.name !== pending.itemName) continue; } try { await this.bot.moveSlotItem(i, slotNum); await sleep(200); placed++; break; } catch (error) { console.log(`Storage trade: Could not move item to slot ${slotNum}: ${error.message}`); } } } console.log(`Storage trade: Placed ${placed} stack(s) in trade window`); } // Poll for customer confirmation (lime_dye at slot 53) let timeoutCheck = setTimeout(() => { this.bot.closeWindow(window); this.whisper(from, 'Trade timed out.'); }, 120000); let confirmationCheck = setInterval(async () => { try { const indicator = window.slots[53]; if (indicator && indicator.name === 'lime_dye') { this.bot.moveSlotItem(37, 37); } } catch (e) { // window may have closed } }, 500); // Wait for trade to complete await this.once('windowClose'); clearInterval(confirmationCheck); if (timeoutCheck._destroyed) { storage._busy = false; return; } clearTimeout(timeoutCheck); if (pending) { // Withdrawal complete — clear pending if (pending.timeoutId) clearTimeout(pending.timeoutId); storage.pendingWithdrawals.delete(from); this.whisper(from, `Withdrawal complete! Enjoy your ${pending.itemName}.`); } else { // Deposit — collect items from bot inventory and sort into storage await sleep(500); const hotbarNames = new Set((storage.config.hotbarItems || []).map(h => h.name)); const itemsReceived = []; for (const item of this.bot.inventory.items()) { if (hotbarNames.has(item.name)) continue; itemsReceived.push({ name: item.name, id: item.type, count: item.count, nbt: item.nbt }); } if (itemsReceived.length > 0) { this.whisper(from, `Received ${itemsReceived.length} item type(s). Sorting into storage.`); await storage.handleTrade(from, itemsReceived); } else { this.whisper(from, 'No items received.'); } } } finally { storage._busy = false; } } }, };