cleanup
This commit is contained in:
parent
3d7a8aafdf
commit
c1892230b7
13 changed files with 1111 additions and 993 deletions
269
src/index.ts
269
src/index.ts
|
|
@ -1,19 +1,9 @@
|
|||
import { Command } from 'commander';
|
||||
import { loadConfig } from './config.js';
|
||||
import { TradeMonitor } from './trade/TradeMonitor.js';
|
||||
import { GameController } from './game/GameController.js';
|
||||
import { ScreenReader } from './game/ScreenReader.js';
|
||||
import { ClientLogWatcher } from './log/ClientLogWatcher.js';
|
||||
import { TradeExecutor } from './executor/TradeExecutor.js';
|
||||
import { ScrapExecutor } from './executor/ScrapExecutor.js';
|
||||
import { TradeQueue } from './executor/TradeQueue.js';
|
||||
import { InventoryManager } from './inventory/InventoryManager.js';
|
||||
import { BotController } from './dashboard/BotController.js';
|
||||
import { DashboardServer } from './dashboard/DashboardServer.js';
|
||||
import { ConfigStore } from './dashboard/ConfigStore.js';
|
||||
import { Bot } from './bot/Bot.js';
|
||||
import { Server } from './server/Server.js';
|
||||
import { ConfigStore } from './bot/ConfigStore.js';
|
||||
import { logger } from './util/logger.js';
|
||||
import type { TradeLink } from './dashboard/BotController.js';
|
||||
import type { Page } from 'playwright';
|
||||
|
||||
const program = new Command();
|
||||
|
||||
|
|
@ -25,15 +15,12 @@ program
|
|||
.option('-p, --port <number>', 'Dashboard port')
|
||||
.option('-c, --config <path>', 'Path to config.json', 'config.json')
|
||||
.action(async (options) => {
|
||||
// Load persisted config
|
||||
const store = new ConfigStore(options.config);
|
||||
const saved = store.settings;
|
||||
|
||||
// CLI/env overrides persisted values
|
||||
const envConfig = loadConfig(options.url);
|
||||
if (options.logPath) envConfig.poe2LogPath = options.logPath;
|
||||
|
||||
// Merge: CLI args > .env > config.json defaults
|
||||
const config = {
|
||||
...envConfig,
|
||||
poe2LogPath: options.logPath || saved.poe2LogPath,
|
||||
|
|
@ -47,250 +34,15 @@ program
|
|||
|
||||
const port = parseInt(options.port, 10) || saved.dashboardPort;
|
||||
|
||||
// Collect all URLs: CLI args + saved links (deduped)
|
||||
const allUrls = new Set<string>([
|
||||
...config.tradeUrls,
|
||||
...saved.links.map((l) => l.url),
|
||||
]);
|
||||
const bot = new Bot(store, config);
|
||||
const server = new Server(bot, port);
|
||||
await server.start();
|
||||
await bot.start(config.tradeUrls, port);
|
||||
|
||||
// Initialize bot controller with config store
|
||||
const bot = new BotController(store);
|
||||
|
||||
// 1. Start dashboard
|
||||
const dashboard = new DashboardServer(bot, port);
|
||||
await dashboard.start();
|
||||
|
||||
// 2. Create game components
|
||||
const screenReader = new ScreenReader();
|
||||
const gameController = new GameController(config);
|
||||
dashboard.setDebugDeps({ screenReader, gameController });
|
||||
|
||||
// 3. Start logWatcher BEFORE /hideout so we can wait for area transition
|
||||
const logWatcher = new ClientLogWatcher(config.poe2LogPath);
|
||||
await logWatcher.start();
|
||||
dashboard.broadcastLog('info', 'Watching Client.txt for game events');
|
||||
|
||||
// 4. Start tradeMonitor
|
||||
const tradeMonitor = new TradeMonitor(config);
|
||||
await tradeMonitor.start(`http://localhost:${port}`);
|
||||
dashboard.broadcastLog('info', 'Browser launched');
|
||||
|
||||
// 5. Create InventoryManager
|
||||
const inventoryManager = new InventoryManager(gameController, screenReader, logWatcher, config);
|
||||
|
||||
// 6. /hideout + waitForAreaTransition
|
||||
dashboard.broadcastLog('info', 'Sending /hideout command...');
|
||||
await gameController.focusGame();
|
||||
const arrivedHome = await inventoryManager.waitForAreaTransition(
|
||||
config.travelTimeoutMs,
|
||||
() => gameController.goToHideout(),
|
||||
);
|
||||
if (arrivedHome) {
|
||||
inventoryManager.setLocation(true);
|
||||
logWatcher.currentArea = 'Hideout';
|
||||
} else {
|
||||
// Assume we're already in hideout if timeout (e.g. already there)
|
||||
inventoryManager.setLocation(true);
|
||||
logWatcher.currentArea = 'Hideout';
|
||||
logger.warn('Timed out waiting for hideout transition on startup (may already be in hideout)');
|
||||
}
|
||||
bot.state = 'IN_HIDEOUT';
|
||||
dashboard.broadcastStatus();
|
||||
dashboard.broadcastLog('info', 'In hideout, ready to trade');
|
||||
|
||||
// 7. Clear leftover inventory items to stash
|
||||
dashboard.broadcastLog('info', 'Checking inventory for leftover items...');
|
||||
await inventoryManager.clearToStash();
|
||||
dashboard.broadcastLog('info', 'Inventory cleared');
|
||||
|
||||
// 8. Create executors with shared InventoryManager
|
||||
const executor = new TradeExecutor(
|
||||
gameController,
|
||||
screenReader,
|
||||
tradeMonitor,
|
||||
inventoryManager,
|
||||
config,
|
||||
);
|
||||
|
||||
// 9. Create tradeQueue
|
||||
const tradeQueue = new TradeQueue(executor, config);
|
||||
|
||||
// Track running scrap executors per link ID
|
||||
const scrapExecutors = new Map<string, ScrapExecutor>();
|
||||
|
||||
// 10. Activate a link based on its mode
|
||||
const activateLink = async (link: TradeLink) => {
|
||||
try {
|
||||
if (link.mode === 'scrap') {
|
||||
// Start scrap loop for this link
|
||||
const scrapExec = new ScrapExecutor(
|
||||
gameController,
|
||||
screenReader,
|
||||
tradeMonitor,
|
||||
inventoryManager,
|
||||
config,
|
||||
);
|
||||
scrapExecutors.set(link.id, scrapExec);
|
||||
dashboard.broadcastLog('info', `Scrap loop started: ${link.name || link.label}`);
|
||||
dashboard.broadcastStatus();
|
||||
// Run in background (don't await — it's an infinite loop)
|
||||
scrapExec.runScrapLoop(link.url, link.postAction).catch((err) => {
|
||||
logger.error({ err, linkId: link.id }, 'Scrap loop error');
|
||||
dashboard.broadcastLog('error', `Scrap loop failed: ${link.name || link.label}`);
|
||||
scrapExecutors.delete(link.id);
|
||||
});
|
||||
} else {
|
||||
// Live search mode
|
||||
await tradeMonitor.addSearch(link.url);
|
||||
dashboard.broadcastLog('info', `Monitoring: ${link.name || link.label}`);
|
||||
dashboard.broadcastStatus();
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error({ err, url: link.url }, 'Failed to activate link');
|
||||
dashboard.broadcastLog('error', `Failed to activate: ${link.name || link.label}`);
|
||||
}
|
||||
};
|
||||
|
||||
// Deactivate a link based on its mode
|
||||
const deactivateLink = async (id: string) => {
|
||||
// Stop scrap executor if running
|
||||
const scrapExec = scrapExecutors.get(id);
|
||||
if (scrapExec) {
|
||||
await scrapExec.stop();
|
||||
scrapExecutors.delete(id);
|
||||
}
|
||||
// Pause live search if active
|
||||
await tradeMonitor.pauseSearch(id);
|
||||
};
|
||||
|
||||
// 11. Load all saved + CLI links (only activate ones marked active)
|
||||
for (const url of allUrls) {
|
||||
const link = bot.addLink(url);
|
||||
if (link.active) {
|
||||
await activateLink(link);
|
||||
} else {
|
||||
dashboard.broadcastLog('info', `Loaded (inactive): ${link.name || link.label}`);
|
||||
}
|
||||
}
|
||||
|
||||
dashboard.broadcastLog('info', `Loaded ${allUrls.size} trade link(s) from config`);
|
||||
|
||||
// When dashboard adds a link, activate it
|
||||
bot.on('link-added', async (link: TradeLink) => {
|
||||
if (link.active) {
|
||||
await activateLink(link);
|
||||
}
|
||||
});
|
||||
|
||||
// When dashboard removes a link, deactivate it
|
||||
bot.on('link-removed', async (id: string) => {
|
||||
await deactivateLink(id);
|
||||
dashboard.broadcastLog('info', `Removed search: ${id}`);
|
||||
dashboard.broadcastStatus();
|
||||
});
|
||||
|
||||
// When dashboard toggles a link active/inactive
|
||||
bot.on('link-toggled', async (data: { id: string; active: boolean; link: TradeLink }) => {
|
||||
if (data.active) {
|
||||
await activateLink(data.link);
|
||||
dashboard.broadcastLog('info', `Activated: ${data.link.name || data.id}`);
|
||||
} else {
|
||||
await deactivateLink(data.id);
|
||||
dashboard.broadcastLog('info', `Deactivated: ${data.link.name || data.id}`);
|
||||
}
|
||||
});
|
||||
|
||||
// When link mode changes, restart with new mode if active
|
||||
bot.on('link-mode-changed', async (data: { id: string; mode: string; link: TradeLink }) => {
|
||||
if (data.link.active) {
|
||||
await deactivateLink(data.id);
|
||||
await activateLink(data.link);
|
||||
dashboard.broadcastLog('info', `Mode changed to ${data.mode}: ${data.link.name || data.id}`);
|
||||
}
|
||||
});
|
||||
|
||||
// When link postAction changes, restart executor if active
|
||||
bot.on('link-postaction-changed', async (data: { id: string; postAction: string; link: TradeLink }) => {
|
||||
if (data.link.active) {
|
||||
await deactivateLink(data.id);
|
||||
await activateLink(data.link);
|
||||
dashboard.broadcastLog('info', `Post-action changed to ${data.postAction}: ${data.link.name || data.id}`);
|
||||
}
|
||||
});
|
||||
|
||||
// Wire up events: when new listings appear, queue them for trading
|
||||
tradeMonitor.on('new-listings', (data: { searchId: string; itemIds: string[]; page: Page }) => {
|
||||
if (bot.isPaused) {
|
||||
dashboard.broadcastLog('warn', `New listings (${data.itemIds.length}) skipped - bot paused`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this specific link is active
|
||||
if (!bot.isLinkActive(data.searchId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info(
|
||||
{ searchId: data.searchId, itemCount: data.itemIds.length },
|
||||
'New listings received, queuing trade...',
|
||||
);
|
||||
dashboard.broadcastLog('info', `New listings: ${data.itemIds.length} items from ${data.searchId}`);
|
||||
|
||||
tradeQueue.enqueue({
|
||||
searchId: data.searchId,
|
||||
itemIds: data.itemIds,
|
||||
whisperText: '',
|
||||
timestamp: Date.now(),
|
||||
tradeUrl: '',
|
||||
page: data.page,
|
||||
});
|
||||
});
|
||||
|
||||
// Forward executor state changes to dashboard
|
||||
const stateInterval = setInterval(() => {
|
||||
// Feed inventory state from shared InventoryManager
|
||||
bot.setInventory(inventoryManager.getInventoryState());
|
||||
|
||||
// Check live trade executor state
|
||||
const execState = executor.getState();
|
||||
if (execState !== 'IDLE') {
|
||||
if (bot.state !== execState) {
|
||||
bot.state = execState;
|
||||
dashboard.broadcastStatus();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check scrap executor states
|
||||
for (const [, scrapExec] of scrapExecutors) {
|
||||
const scrapState = scrapExec.getState();
|
||||
if (scrapState !== 'IDLE') {
|
||||
if (bot.state !== scrapState) {
|
||||
bot.state = scrapState;
|
||||
dashboard.broadcastStatus();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// All idle
|
||||
if (bot.state !== 'IDLE') {
|
||||
bot.state = 'IDLE';
|
||||
dashboard.broadcastStatus();
|
||||
}
|
||||
}, 500);
|
||||
|
||||
// Graceful shutdown
|
||||
const shutdown = async () => {
|
||||
logger.info('Shutting down...');
|
||||
clearInterval(stateInterval);
|
||||
for (const [, scrapExec] of scrapExecutors) {
|
||||
await scrapExec.stop();
|
||||
}
|
||||
await screenReader.dispose();
|
||||
await dashboard.stop();
|
||||
await tradeMonitor.stop();
|
||||
await logWatcher.stop();
|
||||
await bot.stop();
|
||||
await server.stop();
|
||||
process.exit(0);
|
||||
};
|
||||
|
||||
|
|
@ -298,9 +50,6 @@ program
|
|||
process.on('SIGTERM', shutdown);
|
||||
|
||||
logger.info(`Dashboard: http://localhost:${port}`);
|
||||
logger.info(
|
||||
`Monitoring ${allUrls.size} trade search(es). Press Ctrl+C to stop.`,
|
||||
);
|
||||
});
|
||||
|
||||
program.parse();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue