import axios from "axios"; import { randomUUID } from "crypto"; import { HttpsProxyAgent } from "https-proxy-agent"; const PINNACLE_HOST = "https://api.pinnacle888.com"; const axiosDefaultOptions = { baseURL: "", url: "", method: "GET", headers: {}, params: {}, data: {}, timeout: 10000, }; const cleanUndefined = (obj) => { return Object.fromEntries( Object.entries(obj).filter(([, value]) => value !== undefined) ); } export const createPinnacleSdk = (config = {}) => { const proxyAgent = config.httpProxy ? new HttpsProxyAgent(config.httpProxy) : undefined; const pinnacleRequest = async (options, channel) => { const { url, ...optionsRest } = options; const { username, password } = config; if (!url || !channel && (!username || !password)) { throw new Error("url、username、password、channel is required"); } const authHeader = channel ? `Basic ${channel}` : `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`; const axiosConfig = { ...axiosDefaultOptions, ...optionsRest, url, baseURL: PINNACLE_HOST, proxy: false, ...(proxyAgent ? { httpsAgent: proxyAgent } : {}), headers: { ...axiosDefaultOptions.headers, ...optionsRest.headers, Authorization: authHeader, Accept: "application/json", }, }; return axios(axiosConfig).then(res => res.data); } const pinnacleGet = async (url, params, channel) => { return pinnacleRequest({ url, params }, channel) .catch(err => { if (err.response?.data) { err.data = err.response.data; err.cause = err.response.status; } return Promise.reject(err); }); } const pinnaclePost = async (url, data, channel) => { return pinnacleRequest({ url, method: "POST", headers: { "Content-Type": "application/json", }, data, }, channel); } const getLineInfo = async ({ info = {}, channel } = {}) => { const { leagueId, eventId, betType, handicap, team, side, specialId, contestantId, periodNumber = 0, oddsFormat = "Decimal", sportId = 29, } = info; let url = "/v2/line/"; let data = { sportId, leagueId, eventId, betType, handicap, periodNumber, team, side, oddsFormat }; if (specialId) { url = "/v2/line/special"; data = { specialId, contestantId, oddsFormat }; } data = cleanUndefined(data); return pinnacleGet(url, data, channel) .then(ret => ({ info: ret, line: data })); } const getAccountBalance = async (channel) => { return pinnacleGet("/v1/client/balance", undefined, channel); } const placeOrder = async ({ info, line, stakeSize } = {}, channel) => { const uuid = randomUUID(); if (line.specialId) { const data = cleanUndefined({ oddsFormat: line.oddsFormat, uniqueRequestId: uuid, acceptBetterLine: true, stake: stakeSize, winRiskStake: "RISK", lineId: info.lineId, specialId: info.specialId, contestantId: info.contestantId, }); return pinnaclePost("/v4/bets/special", { bets: [data] }, channel) .then(ret => ret.bets?.[0] ?? ret); } const data = cleanUndefined({ oddsFormat: line.oddsFormat, uniqueRequestId: uuid, acceptBetterLine: true, stake: stakeSize, winRiskStake: "RISK", lineId: info.lineId, altLineId: info.altLineId, fillType: "NORMAL", sportId: line.sportId, eventId: line.eventId, periodNumber: line.periodNumber, betType: line.betType, team: line.team, side: line.side, handicap: line.handicap, }); return pinnaclePost("/v4/bets/place", data, channel); } return { pinnacleRequest, pinnacleGet, pinnaclePost, getLineInfo, getAccountBalance, placeOrder, }; } export default createPinnacleSdk;