-
Notifications
You must be signed in to change notification settings - Fork 0
Trade Bridge KO
Trade Bridge를 사용하면 브라우저 확장 프로그램이 모든 트레이드 사이트 작업(라이브 검색, fetch, whisper)을 수행하고, POEFixer는 게임 내 구매만 담당합니다. 덕분에 트레이드 사이트 트래픽이 실제 브라우저 세션 안에서 처리되며 — 쿠키 복사도, cf_clearance 골칫거리도 없습니다 — 이는 향후 "POEFixer를 다른 PC에서 실행" 모드의 토대가 됩니다.
무료 공식 확장 프로그램을 사용하거나, 아래 문서화된 프로토콜에 맞춰 직접 만들 수 있습니다.
Trade Bridge는 POEFixer 라이선스에 Trade 기능이 있어야 합니다. 서버에서 Trade가 유료로 설정되어 있으면 유효한 키가 필요하고, 무료라면 누구나 사용할 수 있습니다. 권한이 없으면 브리지는
entitlement_required로 응답하고 구매를 거부합니다.
Browser extension POEFixer
live-search WebSocket ─┐
fetch item details │ ws://127.0.0.1:PORT in-game buying:
whisper (teleport) ├─────────────────────► wait teleport → click → verify
(Force teleport, │ buy_request → auto-stash → /hideout
In-demand retry, │ ◄───────────────────── → report result
Rate limiting) ─┘ buy_result
- 확장 프로그램은 트레이드 사이트와 관련된 모든 것을 담당합니다.
- POEFixer는 게임 클라이언트 내의 모든 것을 담당하며, 확장 프로그램이 연결하는 로컬 WebSocket 서버(Trade Bridge)를 제공합니다.
- 이 모드에서는 POEFixer 내장
tls-client가 사용되지 않으며, 트레이드 사이트 관련 컨트롤(Connections, Trade Cookies, Force teleport, In-demand, Rate limiting)은 확장 프로그램으로 이동하고 POEFixer에서는 숨겨지거나 비활성화됩니다.
확장 프로그램은 POEFixer 안에 포함되어 제공됩니다. POEFixer 폴더에서 Resources\extension\을 엽니다:
<POEFixer folder>\Resources\extension\
├─ poefixer-extension-chrome.zip ← Chrome / Edge
├─ poefixer-extension-firefox.zip ← Firefox
└─ chrome\ ← unpacked copy used by the "Assisted install" button
완전히 조용한 원클릭 설치는 불가능합니다 — 브라우저는 설계상 프로그램에 의한 확장 프로그램 설치를 차단합니다. 사용하는 브라우저용 파일의 압축을 풀어 한 번 로드하면 됩니다. POEFixer의 Assisted install… 버튼(Configuration → Trade)이 Chrome/Edge 부분을 대신 처리합니다: 압축이 풀린
chrome\폴더 경로를 클립보드에 복사하고 이 가이드를 열어 주므로 압축 해제 단계를 건너뛸 수 있습니다.
-
Resources\extension\poefixer-extension-chrome.zip를 아무 폴더에나 압축 해제합니다(manifest.json이 그 폴더 최상단에 오도록). 또는 POEFixer에서 **Assisted install…**을 클릭하고 복사된 경로를 사용한 뒤 — 4단계로 건너뜁니다. -
chrome://extensions를 엽니다(Edge:edge://extensions). - Developer mode를 켭니다(오른쪽 위).
- Load unpacked를 클릭하고 그 폴더를 선택합니다(또는 Assisted install 경로를 붙여넣습니다).
- 확장 프로그램 아이콘이 도구 모음에 나타나며, 재시작 후에도 로드된 상태로 유지됩니다.
POEFixer 업데이트 후에는 포함된 확장 프로그램이 갱신됩니다 —
chrome://extensions를 열고 확장 프로그램 카드에서 Reload를 클릭하여 새 버전을 적용하세요(또는 다시 압축을 풀고 Load unpacked).
릴리스 버전 Firefox는 Mozilla가 서명한 확장 프로그램만 설치하므로, 임시 로드를 사용하세요:
-
Resources\extension\poefixer-extension-firefox.zip를 아무 폴더에나 압축 해제합니다. -
about:debugging#/runtime/this-firefox를 엽니다. - **Load Temporary Add-on…**을 클릭하고 압축이 풀린 폴더의
manifest.json을 선택합니다. - Firefox를 재시작하기 전까지 로드된 상태로 유지됩니다(재시작 후 반복).
-
영구 설치: Mozilla/AMO가 서명한
.xpi를 Firefox에서 열고 권한 요청을 확인하여 설치합니다.
- POEFixer에서: Configuration → Trade → Trade data source → Browser extension, Port(기본값
47362)를 확인하고 Start bridge를 클릭합니다. - 확장 프로그램 팝업에서 같은 Port를 설정하면 — Host: connected로 표시되어야 하고, POEFixer의 상태가 녹색으로 바뀌면서 확장 프로그램의 이름과 실제 버전을 표시합니다. 예: "Extension connected (PoeFixerExt/1.1.2)" (버전은 확장 프로그램의 manifest에서 가져옵니다 — 오래된 확장 프로그램은 여기에 표시됩니다).
- 해당 브라우저에서 트레이드 사이트에 로그인한 다음, Trade Links를 추가하고 Play를 누릅니다(§3 참조).
모든 것은 POEFixer에서 제어합니다 — 확장 프로그램 팝업은 연결 Port만 설정합니다.
- POEFixer에서: Configuration → Trade → Data source = Browser extension, Port를 설정하고 Start bridge를 클릭합니다.
- 확장 프로그램 팝업에서 Host: connected와 같은 Port를 확인합니다.
- 같은 브라우저에서 트레이드 사이트에 로그인합니다.
- POEFixer의 Trade Links(Connections 탭)에서 trade2 검색 URL을 추가하고 Play를 누릅니다 — 검색이 확장 프로그램으로 전달되어 거기에서 실행됩니다. Stop은 이를 제거합니다.
- POEFixer에서 동작을 설정합니다(Configuration → Trade): Force teleport, Item is in demand → teleport anyway, Rate limiting — POEFixer가 이를 확장 프로그램으로 전달하고, 확장 프로그램은 그대로 따릅니다.
- 매물이 일치하면 확장 프로그램이 whisper를 보내고 POEFixer가 게임 내에서 구매합니다. 모든 확장 프로그램 활동은 POEFixer의 Logs에 표시됩니다(카테고리 Trade).
신뢰할 수 있는 라이브 피드(자동): 활성 검색마다 확장 프로그램이 트레이드 페이지로 고정된 백그라운드 탭을 열고 그 페이지 안에서 라이브 WebSocket을 연결합니다(올바른 Origin + 사용자의 세션 쿠키 — 사이트가 하는 것과 똑같이). 트레이드 사이트에 로그인 상태를 유지하기만 하면 됩니다. Live Search를 수동으로 열 필요가 없습니다. Trade Link는 라이브 WS가 올라올 때까지 **Connecting…**을 표시하다가 Live로 바뀝니다. 그 고정 탭을 닫으면 검색이 중지됩니다.
Manual Buy (검색을 지금 한꺼번에 구매): 라이브 매물을 기다리는 대신, 아무 Trade Link에서 장바구니(Shopping Cart) 아이콘을 누르면 즉시 대량 구매합니다. 확장 프로그램은 해당 검색에 대해 가장 저렴한 일치 매물(설정한 item count까지, 해당 링크의 currency filters를 준수)을 가져와 판매자별로 그룹화하고, 각각에 대해 POEFixer를 통해 whisper + 구매를 수행합니다 — 동일한 spend limits와 auto-stash를 적용하며, 마지막에 단 한 번 은신처로 복귀합니다. Manual Buy와 라이브 검색(Play)은 상호 배타적입니다: Manual Buy를 시작하기 전에 라이브 검색을 중지하고, 그 반대도 마찬가지입니다. 장바구니 버튼은 확장 프로그램이 연결된 동안에만 활성화됩니다.
이 프로토콜은 WebSocket 위에서 동작하는 평범한 JSON입니다. 누구나 구현할 수 있습니다. POEFixer가 서버를 호스팅하며, 클라이언트는 ws://127.0.0.1:<port>에 연결합니다(기본값 47362, Settings에서 설정 가능).
호스트는 라이선스에 Trade 권한이 있을 때만 구매를 처리합니다. 그렇지 않으면
{"type":"error","code":"entitlement_required"}를 받습니다. (인증 세부 사항은 의도적으로 문서화하지 않습니다.)
먼저 hello를 보냅니다. 호스트는 welcome으로 응답합니다.
-
version은 확장 프로그램의 manifest 버전입니다(semver 문자열, 예:"1.1.0"). 확장 프로그램이 POEFixer가 기대하는 버전보다 오래되었으면, POEFixer가 불일치를 감지하여 라이브 검색을 억제하고, 최신 확장 프로그램을 다시 설치할 때까지 구매를 비활성화합니다. POEFixer는 Settings → Trade에 경고를 표시합니다. 오래된 상태에서 구매를 시도하면{"type":"error","code":"outdated_extension"}을 받습니다. -
allowed:false→ 권한 없음("키를 구매하라"로 취급); 호스트는 구매를entitlement_required로 거부합니다. -
token은 사용자가 POEFixer에서 페어링 토큰을 활성화한 경우에만 필요합니다(기본값은 꺼짐).
welcome 이후(그리고 사용자가 POEFixer에서 무언가를 변경할 때마다) 호스트는 config 메시지를 보냅니다. 프로그램이 단일 제어 지점이므로, 확장 프로그램은 정확히 이 검색들을 모니터링하고 이 behavior를 따라야 합니다:
// host → client
{ "type":"config",
"behavior":{ "forceTeleport":false, "inDemandRetry":true, "rateLimit":true },
"searches":[ { "realm":"poe2", "league":"Standard", "searchId":"…", "note":"…" } ] }호스트는 주기적으로 status를 보냅니다:
{ "type":"status", "ready":true, "inGame":true, "attached":true, "busy":false }마지막 status.ready === true일 때만 whisper/teleport를 해야 합니다. ready = entitled && attached && in-game && !busy. 준비되지 않은 상태에서 whisper하면, 거래를 주도하는 구매자 없이 캐릭터만 텔레포트됩니다.
whisper가 성공하면(캐릭터가 텔레포트 중) buy_request를 보냅니다:
// client → host
{ "type":"buy_request", "id":"r-42", "seq":7,
"group":{
"searchId":"…", "groupId":"g-9", "isLastGroup":true,
"items":[ {
"itemId":"...", "stashX":3, "stashY":1, "w":1, "h":1,
"currency":"chaos", "amount":50,
"seller":"AccountName", "stashName":"~price 50 chaos",
"name":"...", "typeLine":"...", "baseType":"...", "rarity":"Rare",
"iconUrl":"https://...", "ilvl":82, "corrupted":false, "identified":true,
"explicitMods":[ "..." ], "implicitMods":[ "..." ],
"league":"Standard", "realm":"poe2",
"hideoutToken":"...", "indexedTime":"..."
} ] } }호스트는 이를 ack하고, 선택적으로 진행 상황을 내보낸 뒤, 단일 결과를 보냅니다:
{ "type":"ack", "ackSeq":7 }
{ "type":"buy_progress", "id":"r-42", "phase":"teleporting" }
{ "type":"buy_result", "id":"r-42", "seq":11,
"items":[ { "itemId":"...", "ok":true, "reason":"verified" } ],
"summary":{ "bought":1, "failed":0 } }-
group.searchId는 매물이 나온 검색입니다. 호스트는 이를 사용해 구매를 올바른 Trade Link에 연결합니다(생략하면 첫 번째 활성 검색으로 폴백). 여러 검색을 올바르게 처리하려면 함께 보내세요. -
stashX/stashY는 판매자 상점 내 아이템의 좌표이고,w/h는 크기입니다 — 이들이 게임 내 클릭을 결정합니다. - 가격은 평면 형태(위처럼
currency/amount)로 보내거나 중첩 형태("price":{"currency","amount"})로 보낼 수 있습니다 — 호스트는 둘 다 받습니다. -
hideoutToken은 로깅용으로만 전달됩니다(whisper에는 이미 사용했습니다). - 각
buy_result는 반드시 ack해야 합니다:{"type":"ack","ackSeq":<buy_result.seq>}를 보내세요.
whisper 단계 결과(POEFixer의 로그를 완전하게 유지):
{ "type":"trade_event", "seq":8, "stage":"whisper", "outcome":"in_demand", "itemId":"...", "detail":"" }
// outcome ∈ in_demand | whisper_failed | teleport_failed | fetch_error
trade_event는 전체item객체(buy_request아이템과 동일한 필드)를 함께 담을 수도 있습니다.teleport_failed= 강제 재-whisper 후에도 판매자의 매물이 여전히 in demand였던 경우입니다.item과 함께 전송되면 POEFixer는 이를 Trade Logs에 Teleport Fail로 기록합니다.fetch_error/whisper_failed/in_demand는 정보성이며 일반 Logs(카테고리 Trade)로 갑니다.
활동을 POEFixer의 로그로 전달하세요(권장 — 모든 확장 프로그램 로그는 프로그램에 표시되어야 합니다):
{ "type":"log", "level":"info", "message":"…" } // shown in POEFixer Logs (category Trade)각 검색의 라이브 WS 상태를 보고하여 POEFixer의 Trade Link가 Connecting…/Live를 표시하도록 하세요:
{ "type":"search_status", "searchId":"…", "state":"connected" } // state ∈ connected | closed | error사용자가 Trade Link에서 Manual Buy(장바구니 아이콘)를 클릭하면, 호스트가 manual_buy를 보냅니다. 확장 프로그램은 해당 검색의 일회성 대량 구매를 수행하고 manual_buy_status로 라이프사이클/진행 상황을 보고합니다. 판매자별 구매는 일반적인 buy_request 흐름(§4.3)을 isLastGroup:false로 사용하며, 호스트는 state:"done"을 받으면 마지막 /hideout을 한 번 실행합니다.
// host → client: start a one-shot bulk buy of one saved search
{ "type":"manual_buy", "id":"mb-…", "seq":12,
"search":{ "realm":"poe2", "league":"Standard", "searchId":"…", "note":"…" },
"itemCount":10, // 0 = all results
"filters":{ "chaos":{ "min":0, "max":0 } } } // currency → {min,max}; 0 = unbounded
// host → client: cancel an in-progress bulk buy
{ "type":"manual_buy_cancel", "id":"mb-…", "seq":13 }
// client → host: lifecycle + progress (ack-tracked like trade_event)
{ "type":"manual_buy_status", "id":"mb-…", "seq":4,
"state":"started|progress|done|cancelled|error",
"processed":3, "bought":2, "failed":1, "total":10, "detail":"" }- 검색을 해석하고, 가장 저렴한 일치 매물을 가져와
filters를 적용하고,itemCount(0 = 전체)로 제한한 다음, 판매자별로 그룹화하여 각 그룹을buy_request(isLastGroup:false)로 구매합니다. - 맨 처음에
manual_buy_statusstarted를 보내고, 그룹이 완료될 때마다progress를, 그리고 정확히 하나의 종료 상태done/cancelled/error를 보냅니다.
-
buy_request와trade_event는 증가하는seq를 담으며, 호스트가 이를 ack합니다. ack되지 않은 프레임을 보관하고 재연결 시 (seq순으로 정렬하여) 다시 보내세요. -
buy_request.id는 멱등성 키입니다 — 다시 보낸 id는 기록된buy_result를 반환합니다(아직 처리 중이면error: duplicate). 구매마다 안정적이고 고유한 id를 사용하세요. -
buy_result는 여러분이 ack해야 하는seq를 담습니다.
{ "type":"error", "code":"entitlement_required|not_in_game|busy|duplicate|bad_request|unauthorized|proto_unsupported|outdated_extension", "id":"r-42", "detail":"..." }const ws = new WebSocket("ws://127.0.0.1:47362");
let ready = false, seq = 0, behavior = {}, searches = [];
const log = (message) => ws.send(JSON.stringify({ type:"log", level:"info", message }));
ws.onopen = () => ws.send(JSON.stringify({ type:"hello", proto:1, client:"MyExt/1.0", version:"1.1.0" }));
ws.onmessage = (e) => {
const m = JSON.parse(e.data);
if (m.type === "welcome" && !m.allowed) console.warn("Trade entitlement required");
if (m.type === "config") { behavior = m.behavior; searches = m.searches; } // program controls you
if (m.type === "status") ready = m.ready;
if (m.type === "buy_result") ws.send(JSON.stringify({ type:"ack", ackSeq:m.seq }));
};
// monitor `searches`, obey `behavior`; after YOUR whisper succeeds and ready === true:
function buy(items) {
ws.send(JSON.stringify({ type:"buy_request", id:"r-"+Date.now(), seq:++seq,
group:{ groupId:"g", isLastGroup:true, items } }));
}- Host: disconnected — POEFixer가 실행 중이 아니거나, 브리지가 시작되지 않았거나, 포트가 틀렸습니다. Settings → Trade에서 브리지를 시작하고, 팝업의 포트를 일치시키세요.
-
entitlement_required— 라이선스에 Trade 기능이 없습니다(서버에서 유료). -
not_in_game— POEFixer가 게임에 attach되어 있지 않거나 인게임 상태가 아닙니다.status.ready를 기다리세요. - 라이브 검색이 전혀 작동하지 않음 — 같은 브라우저에서 트레이드 사이트에 로그인되어 있는지 확인하세요. 확장 프로그램은 검색마다 자체 고정 백그라운드 탭을 자동으로 열고 그 페이지 안에서 라이브 WebSocket을 실행합니다. Trade Link는 **Connecting…**을 표시한 뒤 Live로 바뀝니다. Connecting…에서 멈춰 있으면, 고정 탭을 열어 로그인 상태를 확인하세요.
- 포트 사용 중 — Settings → Trade와 팝업에서 포트를 변경한 다음 브리지를 다시 시작하세요.