Add proxy support (HTTP + WebSocket)

This commit is contained in:
FrondEnt
2026-01-30 00:22:45 +02:00
parent 331ffcfcdf
commit f67970c34e
8 changed files with 190 additions and 3 deletions
+28
View File
@@ -107,6 +107,34 @@ WSS RPC (optional but recommended for more real-time fallback):
- `POLYGON_WSS_URL` (optional)
- `POLYGON_WSS_URLS` (optional, comma-separated)
### Proxy support
The bot supports HTTP(S) proxies for both HTTP requests (fetch) and WebSocket connections.
Supported env vars (standard):
- `HTTPS_PROXY` / `https_proxy`
- `HTTP_PROXY` / `http_proxy`
- `ALL_PROXY` / `all_proxy`
Examples:
PowerShell:
```powershell
$env:HTTPS_PROXY = "http://127.0.0.1:8080"
# or
$env:ALL_PROXY = "socks5://127.0.0.1:1080"
```
CMD:
```cmd
set HTTPS_PROXY=http://127.0.0.1:8080
REM or
set ALL_PROXY=socks5://127.0.0.1:1080
```
## Run
```bash
+104
View File
@@ -9,6 +9,9 @@
"version": "0.1.0",
"dependencies": {
"ethers": "^6.11.1",
"https-proxy-agent": "^7.0.6",
"socks-proxy-agent": "^8.0.5",
"undici": "^6.21.3",
"ws": "^8.18.0"
}
},
@@ -57,6 +60,32 @@
"integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==",
"license": "MIT"
},
"node_modules/agent-base": {
"version": "7.1.4",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
"integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
"license": "MIT",
"engines": {
"node": ">= 14"
}
},
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/ethers": {
"version": "6.16.0",
"resolved": "https://registry.npmjs.org/ethers/-/ethers-6.16.0.tgz",
@@ -106,12 +135,87 @@
}
}
},
"node_modules/https-proxy-agent": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.2",
"debug": "4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/ip-address": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
"integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
"license": "MIT",
"engines": {
"node": ">= 12"
}
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/smart-buffer": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
"integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
"license": "MIT",
"engines": {
"node": ">= 6.0.0",
"npm": ">= 3.0.0"
}
},
"node_modules/socks": {
"version": "2.8.7",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz",
"integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==",
"license": "MIT",
"dependencies": {
"ip-address": "^10.0.1",
"smart-buffer": "^4.2.0"
},
"engines": {
"node": ">= 10.0.0",
"npm": ">= 3.0.0"
}
},
"node_modules/socks-proxy-agent": {
"version": "8.0.5",
"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz",
"integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==",
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.2",
"debug": "^4.3.4",
"socks": "^2.8.3"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/tslib": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
"integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==",
"license": "0BSD"
},
"node_modules/undici": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz",
"integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==",
"license": "MIT",
"engines": {
"node": ">=18.17"
}
},
"node_modules/undici-types": {
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
+3
View File
@@ -8,6 +8,9 @@
},
"dependencies": {
"ethers": "^6.11.1",
"https-proxy-agent": "^7.0.6",
"socks-proxy-agent": "^8.0.5",
"undici": "^6.21.3",
"ws": "^8.18.0"
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
import WebSocket from "ws";
import { CONFIG } from "../config.js";
import { wsAgentForUrl } from "../net/proxy.js";
function toNumber(x) {
const n = Number(x);
@@ -22,7 +23,7 @@ export function startBinanceTradeStream({ symbol = CONFIG.symbol, onUpdate } = {
if (closed) return;
const url = buildWsUrl(symbol);
ws = new WebSocket(url);
ws = new WebSocket(url, { agent: wsAgentForUrl(url) });
ws.on("open", () => {
reconnectMs = 500;
+2 -1
View File
@@ -1,6 +1,7 @@
import WebSocket from "ws";
import { ethers } from "ethers";
import { CONFIG } from "../config.js";
import { wsAgentForUrl } from "../net/proxy.js";
const ANSWER_UPDATED_TOPIC0 = ethers.id("AnswerUpdated(int256,uint256,uint256)");
@@ -55,7 +56,7 @@ export function startChainlinkPriceStream({
const url = wssUrls[urlIndex % wssUrls.length];
urlIndex += 1;
ws = new WebSocket(url);
ws = new WebSocket(url, { agent: wsAgentForUrl(url) });
const send = (obj) => {
try {
+3 -1
View File
@@ -1,5 +1,6 @@
import WebSocket from "ws";
import { CONFIG } from "../config.js";
import { wsAgentForUrl } from "../net/proxy.js";
function safeJsonParse(s) {
try {
@@ -46,7 +47,8 @@ export function startPolymarketChainlinkPriceStream({
if (closed) return;
ws = new WebSocket(wsUrl, {
handshakeTimeout: 10_000
handshakeTimeout: 10_000,
agent: wsAgentForUrl(wsUrl)
});
const scheduleReconnect = () => {
+3
View File
@@ -24,6 +24,7 @@ import { startBinanceTradeStream } from "./data/binanceWs.js";
import fs from "node:fs";
import path from "node:path";
import readline from "node:readline";
import { applyGlobalProxyFromEnv } from "./net/proxy.js";
function countVwapCrosses(closes, vwapSeries, lookback) {
if (closes.length < lookback || vwapSeries.length < lookback) return null;
@@ -37,6 +38,8 @@ function countVwapCrosses(closes, vwapSeries, lookback) {
return crosses;
}
applyGlobalProxyFromEnv();
function fmtTimeLeft(mins) {
const totalSeconds = Math.max(0, Math.floor(mins * 60));
const m = Math.floor(totalSeconds / 60);
+45
View File
@@ -0,0 +1,45 @@
import { ProxyAgent, setGlobalDispatcher } from "undici";
import { HttpsProxyAgent } from "https-proxy-agent";
import { SocksProxyAgent } from "socks-proxy-agent";
function readEnv(name) {
const v = process.env[name];
return v && String(v).trim() ? String(v).trim() : "";
}
export function getProxyUrlFor(targetUrl) {
const u = String(targetUrl || "");
const isHttps = u.startsWith("https://") || u.startsWith("wss://");
const isHttp = u.startsWith("http://") || u.startsWith("ws://");
const all = readEnv("ALL_PROXY") || readEnv("all_proxy");
const https = readEnv("HTTPS_PROXY") || readEnv("https_proxy");
const http = readEnv("HTTP_PROXY") || readEnv("http_proxy");
if (isHttps) return https || all || "";
if (isHttp) return http || all || "";
return all || https || http || "";
}
export function applyGlobalProxyFromEnv() {
const proxyUrl = getProxyUrlFor("https://example.com");
if (!proxyUrl) return false;
try {
setGlobalDispatcher(new ProxyAgent(proxyUrl));
return true;
} catch {
return false;
}
}
export function wsAgentForUrl(wsUrl) {
const proxyUrl = getProxyUrlFor(wsUrl);
if (!proxyUrl) return undefined;
const lower = proxyUrl.toLowerCase();
if (lower.startsWith("socks://") || lower.startsWith("socks5://") || lower.startsWith("socks4://")) {
return new SocksProxyAgent(proxyUrl);
}
return new HttpsProxyAgent(proxyUrl);
}