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

90
src/game/WindowManager.ts Normal file
View file

@ -0,0 +1,90 @@
import koffi from 'koffi';
import { logger } from '../util/logger.js';
// Win32 types
const HWND = 'int';
const BOOL = 'bool';
const RECT = koffi.struct('RECT', {
left: 'long',
top: 'long',
right: 'long',
bottom: 'long',
});
// Load user32.dll
const user32 = koffi.load('user32.dll');
const FindWindowW = user32.func('FindWindowW', HWND, ['str16', 'str16']);
const SetForegroundWindow = user32.func('SetForegroundWindow', BOOL, [HWND]);
const ShowWindow = user32.func('ShowWindow', BOOL, [HWND, 'int']);
const BringWindowToTop = user32.func('BringWindowToTop', BOOL, [HWND]);
const GetForegroundWindow = user32.func('GetForegroundWindow', HWND, []);
const GetWindowRect = user32.func('GetWindowRect', BOOL, [HWND, koffi.out(koffi.pointer(RECT))]);
const IsWindow = user32.func('IsWindow', BOOL, [HWND]);
const keybd_event = user32.func('keybd_event', 'void', ['uint8', 'uint8', 'uint32', 'uint']);
const MapVirtualKeyW = user32.func('MapVirtualKeyW', 'uint32', ['uint32', 'uint32']);
// Constants
const SW_RESTORE = 9;
const VK_MENU = 0x12; // Alt key
const KEYEVENTF_KEYUP = 0x0002;
export class WindowManager {
private hwnd: number = 0;
constructor(private windowTitle: string) {}
findWindow(): number {
this.hwnd = FindWindowW(null as unknown as string, this.windowTitle);
if (this.hwnd === 0) {
logger.warn({ title: this.windowTitle }, 'Window not found');
} else {
logger.info({ title: this.windowTitle, hwnd: this.hwnd }, 'Window found');
}
return this.hwnd;
}
focusWindow(): boolean {
if (!this.hwnd || !IsWindow(this.hwnd)) {
this.findWindow();
}
if (!this.hwnd) return false;
// Restore if minimized
ShowWindow(this.hwnd, SW_RESTORE);
// Alt-key trick to bypass SetForegroundWindow restriction
const altScan = MapVirtualKeyW(VK_MENU, 0);
keybd_event(VK_MENU, altScan, 0, 0);
keybd_event(VK_MENU, altScan, KEYEVENTF_KEYUP, 0);
BringWindowToTop(this.hwnd);
const result = SetForegroundWindow(this.hwnd);
if (!result) {
logger.warn('SetForegroundWindow failed');
}
return result;
}
getWindowRect(): { left: number; top: number; right: number; bottom: number } | null {
if (!this.hwnd || !IsWindow(this.hwnd)) {
this.findWindow();
}
if (!this.hwnd) return null;
const rect = { left: 0, top: 0, right: 0, bottom: 0 };
const success = GetWindowRect(this.hwnd, rect);
if (!success) return null;
return rect;
}
isGameFocused(): boolean {
const fg = GetForegroundWindow();
return fg === this.hwnd && this.hwnd !== 0;
}
getHwnd(): number {
return this.hwnd;
}
}