added easyOCR

This commit is contained in:
Boki 2026-02-12 01:04:19 -05:00
parent 37d6678577
commit 9f208b0606
27 changed files with 1780 additions and 112 deletions

View file

@ -5,11 +5,13 @@ 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 { BotController } from './dashboard/BotController.js';
import { DashboardServer } from './dashboard/DashboardServer.js';
import { ConfigStore } from './dashboard/ConfigStore.js';
import { logger } from './util/logger.js';
import type { TradeLink } from './dashboard/BotController.js';
import type { Page } from 'playwright';
const program = new Command();
@ -73,6 +75,7 @@ program
const logWatcher = new ClientLogWatcher(config.poe2LogPath);
await logWatcher.start();
logWatcher.currentArea = 'Hideout'; // We just sent /hideout on startup
dashboard.broadcastLog('info', 'Watching Client.txt for game events');
const tradeMonitor = new TradeMonitor(config);
@ -89,23 +92,59 @@ program
const tradeQueue = new TradeQueue(executor, config);
// Helper to add a trade search
const activateLink = async (url: string) => {
// Track running scrap executors per link ID
const scrapExecutors = new Map<string, ScrapExecutor>();
// Activate a link based on its mode
const activateLink = async (link: TradeLink) => {
try {
await tradeMonitor.addSearch(url);
dashboard.broadcastLog('info', `Monitoring: ${url}`);
dashboard.broadcastStatus();
if (link.mode === 'scrap') {
// Start scrap loop for this link
const scrapExec = new ScrapExecutor(
gameController,
screenReader,
logWatcher,
tradeMonitor,
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).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 }, 'Failed to add trade search');
dashboard.broadcastLog('error', `Failed to add: ${url}`);
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);
};
// 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(url);
await activateLink(link);
} else {
dashboard.broadcastLog('info', `Loaded (inactive): ${link.name || link.label}`);
}
@ -113,31 +152,40 @@ program
dashboard.broadcastLog('info', `Loaded ${allUrls.size} trade link(s) from config`);
// When dashboard adds a link, activate it in the trade monitor
bot.on('link-added', async (link) => {
// When dashboard adds a link, activate it
bot.on('link-added', async (link: TradeLink) => {
if (link.active) {
await activateLink(link.url);
await activateLink(link);
}
});
// When dashboard removes a link, deactivate it
bot.on('link-removed', async (id: string) => {
await tradeMonitor.removeSearch(id);
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: { url: string; name: string } }) => {
bot.on('link-toggled', async (data: { id: string; active: boolean; link: TradeLink }) => {
if (data.active) {
await activateLink(data.link.url);
await activateLink(data.link);
dashboard.broadcastLog('info', `Activated: ${data.link.name || data.id}`);
} else {
await tradeMonitor.pauseSearch(data.id);
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}`);
}
});
// 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) {
@ -168,9 +216,43 @@ program
// Forward executor state changes to dashboard
const stateInterval = setInterval(() => {
// Feed inventory state from active scrap executors
let inventorySet = false;
for (const [, scrapExec] of scrapExecutors) {
const inv = scrapExec.getInventoryState();
if (inv) {
bot.setInventory(inv);
inventorySet = true;
break;
}
}
if (!inventorySet) bot.setInventory(undefined);
// Check live trade executor state
const execState = executor.getState();
if (bot.state !== execState) {
bot.state = execState;
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);
@ -179,6 +261,9 @@ program
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();