| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315 |
- import dotenv from 'dotenv';
- import axios from "axios";
- import { HttpsProxyAgent } from "https-proxy-agent";
- import { randomUUID } from 'crypto';
- import Logs from "./logs.js";
- import getDateInTimezone from "./getDateInTimezone.js";
- dotenv.config();
- const axiosDefaultOptions = {
- baseURL: "",
- url: "",
- method: "GET",
- headers: {},
- params: {},
- data: {},
- timeout: 10000,
- };
- const pinnacleWebOptions = {
- ...axiosDefaultOptions,
- baseURL: "https://www.part987.com",
- headers: {
- "accept": "application/json, text/plain, */*",
- "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36",
- "x-app-data": "directusToken=TwEdnphtyxsfMpXoJkCkWaPsL2KJJ3lo;lang=zh_CN;dpVXz=ZDfaFZUP9"
- },
- }
- export const pinnacleRequest = async (options) => {
- const { url, username, password, ...optionsRest } = options;
- if (!url || !username || !password) {
- throw new Error("url、username、password is required");
- }
- const authHeader = `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`;
- const axiosConfig = { ...axiosDefaultOptions, ...optionsRest, url, baseURL: "https://api.pinnacle888.com" };
- Object.assign(axiosConfig.headers, {
- "Authorization": authHeader,
- "Accept": "application/json",
- });
- const proxy = process.env.NODE_HTTP_PROXY;
- if (proxy) {
- axiosConfig.proxy = false;
- axiosConfig.httpsAgent = new HttpsProxyAgent(proxy);
- }
- return axios(axiosConfig).then(res => {
- // Logs.out('pinnacle request', url, axiosConfig, res.data);
- return res.data;
- });
- }
- /**
- * Pinnacle API Get请求
- * @param {*} url
- * @param {*} params
- * @returns
- */
- export const pinnacleGet = async (url, params) => {
- return pinnacleRequest({
- url,
- params,
- username: process.env.PINNACLE_USERNAME,
- password: process.env.PINNACLE_PASSWORD,
- })
- .catch(err => {
- const source = { url, params };
- if (err?.response?.data) {
- const data = err.response.data;
- Object.assign(source, { data });
- }
- err.source = source;
- return Promise.reject(err);
- });
- }
- /**
- * Pinnacle API Post请求
- * @param {*} url
- * @param {*} data
- * @returns
- */
- export const pinnaclePost = async (url, data) => {
- return pinnacleRequest({
- url,
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- data,
- username: process.env.PINNACLE_USERNAME,
- password: process.env.PINNACLE_PASSWORD,
- });
- }
- /**
- * 清理对象中的undefined值
- * @param {*} obj
- * @returns {Object}
- */
- const cleanUndefined = (obj) => {
- return Object.fromEntries(
- Object.entries(obj).filter(([, v]) => v !== undefined)
- );
- }
- /**
- * 获取直盘线
- */
- export const getLineInfo = async (info = {}) => {
- 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)
- .then(ret => ({ info: ret, line: data }))
- .catch(err => {
- Logs.errDev('get line info error', err);
- return Promise.reject(err);
- });
- }
- /**
- * 获取账户余额
- */
- export const getAccountBalance = async () => {
- return pinnacleGet('/v1/client/balance');
- }
- /**
- * 下注
- */
- export const placeOrder = async ({ info, line, stakeSize }) => {
- // return Promise.resolve({info, line, stakeSize});
- 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,
- });
- Logs.outDev('pinnacle place order data', data);
- return pinnaclePost('/v4/bets/special', { bets: [data] })
- .then(ret => ret.bets?.[0] ?? ret)
- .then(ret => {
- Logs.outDev('pinnacle place order', ret, uuid);
- return ret;
- })
- .catch(err => {
- Logs.outDev('pinnacle place order error', err.response.data, uuid);
- Logs.errDev(err);
- return Promise.reject(err);
- });
- }
- else {
- 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,
- });
- Logs.outDev('pinnacle place order data', data);
- return pinnaclePost('/v4/bets/place', data)
- .then(ret => {
- Logs.outDev('pinnacle place order', ret, uuid);
- return ret;
- })
- .catch(err => {
- Logs.outDev('pinnacle place order error', err.response.data, uuid);
- Logs.errDev(err);
- return Promise.reject(err);
- });
- }
- }
- /**
- * 获取Web端联赛数据
- * @param {*} marketType 0: 早盘赛事, 1: 今日赛事
- * @param {*} locale en_US or zh_CN
- * @returns
- */
- export const pinnacleWebLeagues = async (marketType = 1, locale = "zh_CN") => {
- const dateString = marketType == 1 ? getDateInTimezone(-4) : getDateInTimezone(-4, Date.now()+24*60*60*1000);
- const nowTime = Date.now();
- const axiosConfig = {
- ...pinnacleWebOptions,
- url: "/sports-service/sv/compact/leagues",
- params: {
- btg: 1, c: "", d: dateString,
- l: true, mk: marketType,
- pa: 0, pn: -1, sp: 29, tm: 0,
- locale, _: nowTime, withCredentials: true
- },
- };
- const proxy = process.env.NODE_HTTP_PROXY;
- if (proxy) {
- axiosConfig.proxy = false;
- axiosConfig.httpsAgent = new HttpsProxyAgent(proxy);
- }
- return axios(axiosConfig).then(res => res.data?.[0]?.[2]?.map(item => {
- const [ id, , name ] = item;
- return [id, { id, name }];
- }) ?? []);
- }
- /**
- * 获取Web比赛列表
- */
- export const pinnacleWebGames = async (leagues=[], marketType=1, locale="zh_CN") => {
- const dateString = marketType == 1 ? getDateInTimezone(-4) : getDateInTimezone(-4, Date.now()+24*60*60*1000);
- const nowTime = Date.now();
- const axiosConfig = {
- ...pinnacleWebOptions,
- url: "/sports-service/sv/odds/events",
- params: {
- sp: 29, lg: leagues.join(','), ev: "",
- mk: marketType, btg: 1, ot: 1,
- d: dateString, o: 0, l: 100, v: 0,
- me: 0, more: false, tm: 0, pa: 0, c: "",
- g: "QQ==", cl: 100, pimo: "0,1,8,39,2,3,6,7,4,5",
- inl: false, _: nowTime, locale
- },
- };
- const proxy = process.env.NODE_HTTP_PROXY;
- if (proxy) {
- axiosConfig.proxy = false;
- axiosConfig.httpsAgent = new HttpsProxyAgent(proxy);
- }
- return axios(axiosConfig).then(res => res.data.n?.[0]?.[2]?.map(league => {
- const [leagueId, leagueName, games] = league;
- return games.map(game => {
- const [id, teamHomeName, teamAwayName, , timestamp] = game;
- const startTime = getDateInTimezone('+8', timestamp, true);
- return { id, leagueId, leagueName, teamHomeName, teamAwayName, timestamp, startTime }
- })
- }).flat().filter(game => {
- const { teamHomeName, teamAwayName } = game;
- if (teamHomeName.startsWith('主队')) {
- return false;
- }
- else if (teamAwayName.startsWith('客队')) {
- return false;
- }
- else if (teamHomeName.startsWith('Home Teams')) {
- return false;
- }
- else if (teamAwayName.startsWith('Away Teams')) {
- return false;
- }
- return true;
- }) ?? []);
- }
- export const platformRequest = async (options) => {
- const { url } = options;
- if (!url) {
- throw new Error("url is required");
- }
- const mergedOptions = {
- ...axiosDefaultOptions,
- ...options,
- baseURL: "http://127.0.0.1:9020",
- };
- return axios(mergedOptions).then(res => res.data);
- }
- export const platformPost = async (url, data) => {
- return platformRequest({
- url,
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- data,
- });
- }
- export const platformGet = async (url, params) => {
- return platformRequest({ url, method: 'GET', params });
- }
- /**
- * 通知异常
- * @param {*} message
- */
- export const notifyException = async (message) => {
- Logs.out('notify exception', message);
- }
|