Add proxy support (HTTP + WebSocket)
This commit is contained in:
@@ -107,6 +107,34 @@ WSS RPC (optional but recommended for more real-time fallback):
|
|||||||
- `POLYGON_WSS_URL` (optional)
|
- `POLYGON_WSS_URL` (optional)
|
||||||
- `POLYGON_WSS_URLS` (optional, comma-separated)
|
- `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
|
## Run
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
Generated
+104
@@ -9,6 +9,9 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ethers": "^6.11.1",
|
"ethers": "^6.11.1",
|
||||||
|
"https-proxy-agent": "^7.0.6",
|
||||||
|
"socks-proxy-agent": "^8.0.5",
|
||||||
|
"undici": "^6.21.3",
|
||||||
"ws": "^8.18.0"
|
"ws": "^8.18.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -57,6 +60,32 @@
|
|||||||
"integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==",
|
"integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/ethers": {
|
||||||
"version": "6.16.0",
|
"version": "6.16.0",
|
||||||
"resolved": "https://registry.npmjs.org/ethers/-/ethers-6.16.0.tgz",
|
"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": {
|
"node_modules/tslib": {
|
||||||
"version": "2.7.0",
|
"version": "2.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
|
||||||
"integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==",
|
"integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==",
|
||||||
"license": "0BSD"
|
"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": {
|
"node_modules/undici-types": {
|
||||||
"version": "6.19.8",
|
"version": "6.19.8",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ethers": "^6.11.1",
|
"ethers": "^6.11.1",
|
||||||
|
"https-proxy-agent": "^7.0.6",
|
||||||
|
"socks-proxy-agent": "^8.0.5",
|
||||||
|
"undici": "^6.21.3",
|
||||||
"ws": "^8.18.0"
|
"ws": "^8.18.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import WebSocket from "ws";
|
import WebSocket from "ws";
|
||||||
import { CONFIG } from "../config.js";
|
import { CONFIG } from "../config.js";
|
||||||
|
import { wsAgentForUrl } from "../net/proxy.js";
|
||||||
|
|
||||||
function toNumber(x) {
|
function toNumber(x) {
|
||||||
const n = Number(x);
|
const n = Number(x);
|
||||||
@@ -22,7 +23,7 @@ export function startBinanceTradeStream({ symbol = CONFIG.symbol, onUpdate } = {
|
|||||||
if (closed) return;
|
if (closed) return;
|
||||||
|
|
||||||
const url = buildWsUrl(symbol);
|
const url = buildWsUrl(symbol);
|
||||||
ws = new WebSocket(url);
|
ws = new WebSocket(url, { agent: wsAgentForUrl(url) });
|
||||||
|
|
||||||
ws.on("open", () => {
|
ws.on("open", () => {
|
||||||
reconnectMs = 500;
|
reconnectMs = 500;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import WebSocket from "ws";
|
import WebSocket from "ws";
|
||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
import { CONFIG } from "../config.js";
|
import { CONFIG } from "../config.js";
|
||||||
|
import { wsAgentForUrl } from "../net/proxy.js";
|
||||||
|
|
||||||
const ANSWER_UPDATED_TOPIC0 = ethers.id("AnswerUpdated(int256,uint256,uint256)");
|
const ANSWER_UPDATED_TOPIC0 = ethers.id("AnswerUpdated(int256,uint256,uint256)");
|
||||||
|
|
||||||
@@ -55,7 +56,7 @@ export function startChainlinkPriceStream({
|
|||||||
const url = wssUrls[urlIndex % wssUrls.length];
|
const url = wssUrls[urlIndex % wssUrls.length];
|
||||||
urlIndex += 1;
|
urlIndex += 1;
|
||||||
|
|
||||||
ws = new WebSocket(url);
|
ws = new WebSocket(url, { agent: wsAgentForUrl(url) });
|
||||||
|
|
||||||
const send = (obj) => {
|
const send = (obj) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import WebSocket from "ws";
|
import WebSocket from "ws";
|
||||||
import { CONFIG } from "../config.js";
|
import { CONFIG } from "../config.js";
|
||||||
|
import { wsAgentForUrl } from "../net/proxy.js";
|
||||||
|
|
||||||
function safeJsonParse(s) {
|
function safeJsonParse(s) {
|
||||||
try {
|
try {
|
||||||
@@ -46,7 +47,8 @@ export function startPolymarketChainlinkPriceStream({
|
|||||||
if (closed) return;
|
if (closed) return;
|
||||||
|
|
||||||
ws = new WebSocket(wsUrl, {
|
ws = new WebSocket(wsUrl, {
|
||||||
handshakeTimeout: 10_000
|
handshakeTimeout: 10_000,
|
||||||
|
agent: wsAgentForUrl(wsUrl)
|
||||||
});
|
});
|
||||||
|
|
||||||
const scheduleReconnect = () => {
|
const scheduleReconnect = () => {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { startBinanceTradeStream } from "./data/binanceWs.js";
|
|||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import readline from "node:readline";
|
import readline from "node:readline";
|
||||||
|
import { applyGlobalProxyFromEnv } from "./net/proxy.js";
|
||||||
|
|
||||||
function countVwapCrosses(closes, vwapSeries, lookback) {
|
function countVwapCrosses(closes, vwapSeries, lookback) {
|
||||||
if (closes.length < lookback || vwapSeries.length < lookback) return null;
|
if (closes.length < lookback || vwapSeries.length < lookback) return null;
|
||||||
@@ -37,6 +38,8 @@ function countVwapCrosses(closes, vwapSeries, lookback) {
|
|||||||
return crosses;
|
return crosses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
applyGlobalProxyFromEnv();
|
||||||
|
|
||||||
function fmtTimeLeft(mins) {
|
function fmtTimeLeft(mins) {
|
||||||
const totalSeconds = Math.max(0, Math.floor(mins * 60));
|
const totalSeconds = Math.max(0, Math.floor(mins * 60));
|
||||||
const m = Math.floor(totalSeconds / 60);
|
const m = Math.floor(totalSeconds / 60);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user