pinnacleClient.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. import dotenv from 'dotenv';
  2. import https from 'https';
  3. import axios from "axios";
  4. import { HttpsProxyAgent } from "https-proxy-agent";
  5. import Logs from "./logs.js";
  6. import { getAccountInfo } from "./getAccount.js";
  7. dotenv.config();
  8. const axiosDefaultOptions = {
  9. baseURL: "",
  10. url: "",
  11. method: "GET",
  12. headers: {},
  13. params: {},
  14. data: {},
  15. timeout: 10000,
  16. };
  17. const BaseURL = {
  18. pinnacle: "https://api.pinnacle888.com",
  19. ps3838: "https://api.ps3838.com",
  20. pstery: "http://127.0.0.1:9055",
  21. }
  22. const betPostPaths = new Set([
  23. '/v4/bets/place',
  24. '/v4/bets/parlay',
  25. '/v4/bets/teaser',
  26. '/v4/bets/special',
  27. ]);
  28. const cloneJsonData = (data) => {
  29. if (data === undefined || data === null) {
  30. return data;
  31. }
  32. return JSON.parse(JSON.stringify(data));
  33. }
  34. const toAccountCurrencyStake = (amount, accountInfo) => {
  35. const value = Number(amount);
  36. if (!Number.isFinite(value)) {
  37. return amount;
  38. }
  39. const currency = accountInfo?.currency?.toUpperCase() || 'USD';
  40. if (currency === 'USD') {
  41. return Math.round(value);
  42. }
  43. const rate = Number(accountInfo?.currencyInfo?.rate);
  44. if (!Number.isFinite(rate) || rate <= 0) {
  45. const error = new Error(`currency rate is not ready: ${currency}`);
  46. error.cause = 503;
  47. error.data = { currency };
  48. throw error;
  49. }
  50. return Math.round(value * rate);
  51. }
  52. const convertBetStakeToUsd = (data, accountInfo) => {
  53. if (!data || typeof data !== 'object') {
  54. return data;
  55. }
  56. const converted = cloneJsonData(data);
  57. const requests = Array.isArray(converted.bets) ? converted.bets : [converted];
  58. requests.forEach(request => {
  59. if (!request || typeof request !== 'object') {
  60. return;
  61. }
  62. if (request.stake !== undefined) {
  63. request.stake = toAccountCurrencyStake(request.stake, accountInfo);
  64. }
  65. if (request.riskAmount !== undefined) {
  66. request.riskAmount = toAccountCurrencyStake(request.riskAmount, accountInfo);
  67. }
  68. });
  69. return converted;
  70. }
  71. export const pinnacleRequest = async (options, channel) => {
  72. const accountInfo = getAccountInfo() ?? {};
  73. const { username, password, localAddress, platform } = accountInfo;
  74. const { data, params, url, ...optionsRest } = options;
  75. if (!url || !channel && (!username || !password)) {
  76. throw new Error("url、username、password、channel is required");
  77. }
  78. const requestData = !channel && optionsRest.method === 'POST' && betPostPaths.has(url)
  79. ? convertBetStakeToUsd(data, accountInfo)
  80. : data;
  81. Logs.outDev('pinnacle request', { url, channel, username, password, localAddress, platform });
  82. const authHeader = channel ? `Basic ${channel}` : `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`;
  83. const axiosConfig = { ...axiosDefaultOptions, ...optionsRest, data: requestData, params, url, baseURL: BaseURL[platform] ?? BaseURL.pinnacle };
  84. Object.assign(axiosConfig.headers, {
  85. "Authorization": authHeader,
  86. "Accept": "application/json",
  87. });
  88. const proxy = process.env.NODE_HTTP_PROXY;
  89. if (proxy) {
  90. axiosConfig.proxy = false;
  91. axiosConfig.httpsAgent = new HttpsProxyAgent(proxy);
  92. // Logs.outDev('pinnacle request using proxy', axiosConfig.httpsAgent);
  93. }
  94. else if (localAddress) {
  95. axiosConfig.httpsAgent = new https.Agent({ localAddress });
  96. }
  97. return axios(axiosConfig)
  98. .then(res => {
  99. // Logs.out('pinnacle request', url, axiosConfig, res.data);
  100. return res.data;
  101. })
  102. .catch(err => {
  103. let user_name = '';
  104. if (channel) {
  105. user_name = Buffer.from(channel, "base64").toString().split(':')[0];
  106. }
  107. else {
  108. user_name = username;
  109. }
  110. const source = { url, params, username: user_name };
  111. if (err?.response?.data) {
  112. const data = err.response.data;
  113. Object.assign(source, { data });
  114. }
  115. err.source = source;
  116. return Promise.reject(err);
  117. });
  118. }
  119. /**
  120. * Pinnacle API Get请求
  121. * @param {*} url
  122. * @param {*} params
  123. * @returns
  124. */
  125. export const pinnacleGet = async (url, params, channel) => {
  126. return pinnacleRequest({
  127. url,
  128. params
  129. }, channel)
  130. }
  131. /**
  132. * Pinnacle API Post请求
  133. * @param {*} url
  134. * @param {*} data
  135. * @returns
  136. */
  137. export const pinnaclePost = async (url, data, channel) => {
  138. return pinnacleRequest({
  139. url,
  140. method: 'POST',
  141. headers: {
  142. 'Content-Type': 'application/json',
  143. },
  144. data
  145. }, channel);
  146. }
  147. export const getPsteryRelations = async (mk=-1) => {
  148. const axiosConfig = {
  149. baseURL: BaseURL.pstery,
  150. url: '/api/pstery/get_games_relation',
  151. method: 'GET',
  152. params: {
  153. mk,
  154. },
  155. proxy: false,
  156. };
  157. return axios(axiosConfig).then(res => res.data);
  158. }
  159. export const updateBaseEvents = async (data) => {
  160. const axiosConfig = {
  161. baseURL: BaseURL.pstery,
  162. url: '/api/pstery/update_base_events',
  163. method: 'POST',
  164. headers: {
  165. 'Content-Type': 'application/json',
  166. },
  167. data: JSON.stringify(data),
  168. proxy: false,
  169. };
  170. axios(axiosConfig).then(res => res.data)
  171. .then(() => {
  172. Logs.outDev('update base events success', data);
  173. })
  174. .catch(err => {
  175. Logs.err('failed to update base events:', err.response?.data ?? err.message);
  176. });
  177. }
  178. export const notifyException = async (message) => {
  179. const axiosConfig = {
  180. baseURL: BaseURL.pstery,
  181. url: '/api/pstery/notify_exception',
  182. method: 'POST',
  183. data: { message },
  184. proxy: false,
  185. };
  186. axios(axiosConfig).then(res => res.data)
  187. .then(() => {
  188. Logs.out('notify exception success');
  189. })
  190. .catch(err => {
  191. Logs.err('failed to notify exception:', err.message);
  192. });
  193. }