Initial commit: POE2 automated trade bot

Monitors pathofexile.com/trade2 for new listings, travels to seller
hideouts, buys items from public stash tabs, and stores them.

Includes persistent C# OCR daemon for fast screen capture + Windows
native OCR, web dashboard for managing trade links and settings,
and full game automation via Win32 SendInput.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Boki 2026-02-10 14:03:47 -05:00
commit 41d174195e
28 changed files with 6449 additions and 0 deletions

View file

@ -0,0 +1,69 @@
import { logger } from '../util/logger.js';
import { sleep, randomDelay } from '../util/sleep.js';
import type { TradeExecutor } from './TradeExecutor.js';
import type { TradeInfo, Config } from '../types.js';
export class TradeQueue {
private queue: TradeInfo[] = [];
private processing = false;
constructor(
private executor: TradeExecutor,
private config: Config,
) {}
enqueue(trade: TradeInfo): void {
// De-duplicate: skip if same item ID already queued
const existingIds = new Set(this.queue.flatMap((t) => t.itemIds));
const newIds = trade.itemIds.filter((id) => !existingIds.has(id));
if (newIds.length === 0) {
logger.info({ itemIds: trade.itemIds }, 'Skipping duplicate trade');
return;
}
const dedupedTrade = { ...trade, itemIds: newIds };
this.queue.push(dedupedTrade);
logger.info(
{ itemIds: newIds, queueLength: this.queue.length },
'Trade enqueued',
);
this.processNext();
}
get length(): number {
return this.queue.length;
}
get isProcessing(): boolean {
return this.processing;
}
private async processNext(): Promise<void> {
if (this.processing || this.queue.length === 0) return;
this.processing = true;
const trade = this.queue.shift()!;
try {
logger.info(
{ searchId: trade.searchId, itemIds: trade.itemIds },
'Processing trade',
);
const success = await this.executor.executeTrade(trade);
if (success) {
logger.info({ itemIds: trade.itemIds }, 'Trade completed successfully');
} else {
logger.warn({ itemIds: trade.itemIds }, 'Trade failed');
}
} catch (err) {
logger.error({ err, itemIds: trade.itemIds }, 'Trade execution error');
}
this.processing = false;
// Delay between trades
await randomDelay(this.config.betweenTradesDelayMs, this.config.betweenTradesDelayMs + 3000);
this.processNext();
}
}