pinnacleClient.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import axios from "axios";
  2. import { HttpsProxyAgent } from "https-proxy-agent";
  3. import { randomUUID } from 'crypto';
  4. import Logs from "./logs.js";
  5. const axiosDefaultOptions = {
  6. baseURL: "",
  7. url: "",
  8. method: "GET",
  9. headers: {},
  10. params: {},
  11. data: {},
  12. timeout: 10000,
  13. };
  14. export const pinnacleRequest = async (options, channel) => {
  15. const { url, ...optionsRest } = options;
  16. const username = process.env.PINNACLE_USERNAME;
  17. const password = process.env.PINNACLE_PASSWORD;
  18. if (!url || !channel && (!username || !password)) {
  19. throw new Error("url、username、password、channel is required");
  20. }
  21. const authHeader = channel ? `Basic ${channel}` : `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`;
  22. const axiosConfig = { ...axiosDefaultOptions, ...optionsRest, url, baseURL: "https://api.pinnacle888.com" };
  23. Object.assign(axiosConfig.headers, {
  24. "Authorization": authHeader,
  25. "Accept": "application/json",
  26. });
  27. const proxy = process.env.NODE_HTTP_PROXY;
  28. if (proxy) {
  29. axiosConfig.proxy = false;
  30. axiosConfig.httpsAgent = new HttpsProxyAgent(proxy);
  31. }
  32. Logs.outDev('pinnacle request', url, axiosConfig, { channel, username, password });
  33. return axios(axiosConfig).then(res => {
  34. return res.data;
  35. });
  36. }
  37. /**
  38. * Pinnacle API Get请求
  39. * @param {*} url
  40. * @param {*} params
  41. * @returns
  42. */
  43. export const pinnacleGet = async (url, params, channel) => {
  44. return pinnacleRequest({
  45. url,
  46. params
  47. }, channel)
  48. .catch(err => {
  49. Logs.errDev('pinnacle get error', err);
  50. if (err.response?.data) {
  51. err.data = err.response.data;
  52. err.cause = err.response.status;
  53. }
  54. return Promise.reject(err);
  55. });
  56. }
  57. /**
  58. * Pinnacle API Post请求
  59. * @param {*} url
  60. * @param {*} data
  61. * @returns
  62. */
  63. export const pinnaclePost = async (url, data, channel) => {
  64. return pinnacleRequest({
  65. url,
  66. method: 'POST',
  67. headers: {
  68. 'Content-Type': 'application/json',
  69. },
  70. data
  71. }, channel);
  72. }
  73. /**
  74. * 清理对象中的undefined值
  75. * @param {*} obj
  76. * @returns {Object}
  77. */
  78. const cleanUndefined = (obj) => {
  79. return Object.fromEntries(
  80. Object.entries(obj).filter(([, v]) => v !== undefined)
  81. );
  82. }
  83. /**
  84. * 获取直盘线
  85. */
  86. export const getLineInfo = async ({ info = {}, channel }) => {
  87. const {
  88. leagueId, eventId, betType, handicap, team, side,
  89. specialId, contestantId,
  90. periodNumber=0, oddsFormat='Decimal', sportId=29
  91. } = info;
  92. let url = '/v2/line/';
  93. let data = { sportId, leagueId, eventId, betType, handicap, periodNumber, team, side, oddsFormat };
  94. if (specialId) {
  95. url = '/v2/line/special';
  96. data = { specialId, contestantId, oddsFormat };
  97. }
  98. data = cleanUndefined(data);
  99. return pinnacleGet(url, data, channel)
  100. .then(ret => ({ info: ret, line: data }))
  101. .catch(err => {
  102. Logs.outDev('get line info error', err.data);
  103. Logs.errDev(err);
  104. return Promise.reject(err);
  105. });
  106. }
  107. /**
  108. * 获取账户余额
  109. */
  110. export const getAccountBalance = async () => {
  111. return pinnacleGet('/v1/client/balance');
  112. }
  113. /**
  114. * 下注
  115. */
  116. export const placeOrder = async ({ info, line, stakeSize }, channel) => {
  117. // return Promise.resolve({info, line, stakeSize});
  118. const uuid = randomUUID()
  119. if (line.specialId) {
  120. const data = cleanUndefined({
  121. oddsFormat: line.oddsFormat,
  122. uniqueRequestId: uuid,
  123. acceptBetterLine: true,
  124. stake: stakeSize,
  125. winRiskStake: 'RISK',
  126. lineId: info.lineId,
  127. specialId: info.specialId,
  128. contestantId: info.contestantId,
  129. });
  130. Logs.outDev('pinnacle place order data', data);
  131. return pinnaclePost('/v4/bets/special', { bets: [data] }, channel)
  132. .then(ret => ret.bets?.[0] ?? ret)
  133. .then(ret => {
  134. Logs.outDev('pinnacle place order', ret, uuid);
  135. return ret;
  136. })
  137. .catch(err => {
  138. Logs.outDev('pinnacle place order error', err.data, uuid);
  139. Logs.errDev(err);
  140. return Promise.reject(err);
  141. });
  142. }
  143. else {
  144. const data = cleanUndefined({
  145. oddsFormat: line.oddsFormat,
  146. uniqueRequestId: uuid,
  147. acceptBetterLine: true,
  148. stake: stakeSize,
  149. winRiskStake: 'RISK',
  150. lineId: info.lineId,
  151. altLineId: info.altLineId,
  152. fillType: 'NORMAL',
  153. sportId: line.sportId,
  154. eventId: line.eventId,
  155. periodNumber: line.periodNumber,
  156. betType: line.betType,
  157. team: line.team,
  158. side: line.side,
  159. handicap: line.handicap,
  160. });
  161. Logs.outDev('pinnacle place order data', data);
  162. return pinnaclePost('/v4/bets/place', data, channel)
  163. .then(ret => {
  164. Logs.outDev('pinnacle place order', ret, uuid);
  165. return ret;
  166. })
  167. .catch(err => {
  168. Logs.outDev('pinnacle place order error', err.data, uuid);
  169. Logs.errDev(err);
  170. return Promise.reject(err);
  171. });
  172. }
  173. }