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

107
src/game/GameController.ts Normal file
View file

@ -0,0 +1,107 @@
import { WindowManager } from './WindowManager.js';
import { InputSender, VK } from './InputSender.js';
import { sleep, randomDelay } from '../util/sleep.js';
import { writeClipboard } from '../util/clipboard.js';
import { logger } from '../util/logger.js';
import type { Config } from '../types.js';
export class GameController {
private windowManager: WindowManager;
private inputSender: InputSender;
constructor(config: Config) {
this.windowManager = new WindowManager(config.poe2WindowTitle);
this.inputSender = new InputSender();
}
async focusGame(): Promise<boolean> {
const result = this.windowManager.focusWindow();
if (result) {
await sleep(300); // Wait for window to actually focus
}
return result;
}
isGameFocused(): boolean {
return this.windowManager.isGameFocused();
}
getWindowRect() {
return this.windowManager.getWindowRect();
}
async sendChat(message: string): Promise<void> {
logger.info({ message }, 'Sending chat message');
// Open chat
await this.inputSender.pressKey(VK.RETURN);
await randomDelay(100, 200);
// Clear any existing text
await this.inputSender.selectAll();
await sleep(50);
await this.inputSender.pressKey(VK.DELETE);
await sleep(50);
// Type the message
await this.inputSender.typeText(message);
await randomDelay(50, 100);
// Send
await this.inputSender.pressKey(VK.RETURN);
await sleep(100);
}
async sendChatViaPaste(message: string): Promise<void> {
logger.info({ message }, 'Sending chat message via paste');
// Copy message to clipboard
writeClipboard(message);
await sleep(50);
// Open chat
await this.inputSender.pressKey(VK.RETURN);
await randomDelay(100, 200);
// Clear any existing text
await this.inputSender.selectAll();
await sleep(50);
await this.inputSender.pressKey(VK.DELETE);
await sleep(50);
// Paste
await this.inputSender.paste();
await randomDelay(100, 200);
// Send
await this.inputSender.pressKey(VK.RETURN);
await sleep(100);
}
async goToHideout(): Promise<void> {
logger.info('Sending /hideout command');
await this.sendChat('/hideout');
}
async ctrlRightClickAt(x: number, y: number): Promise<void> {
await this.inputSender.ctrlRightClick(x, y);
}
async leftClickAt(x: number, y: number): Promise<void> {
await this.inputSender.leftClick(x, y);
}
async rightClickAt(x: number, y: number): Promise<void> {
await this.inputSender.rightClick(x, y);
}
async pressEscape(): Promise<void> {
await this.inputSender.pressKey(VK.ESCAPE);
}
async openInventory(): Promise<void> {
logger.info('Opening inventory');
await this.inputSender.pressKey(VK.I);
await sleep(300);
}
}