Platforms.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. import { fork } from "child_process";
  2. import Store from "../state/store.js";
  3. import ProcessData from "../libs/processData.js";
  4. import Logs from "../libs/logs.js";
  5. import { getGamesRelationsMap } from "../libs/getGamesRelations.js";
  6. import { getSoccerGames, getObossOdds } from "./Partner.js";
  7. const getChildOptions = (inspect=9230) => {
  8. return process.env.NODE_ENV == 'development' ? {
  9. execArgv: [`--inspect=${inspect}`],
  10. stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
  11. env: { ...process.env }
  12. } : {
  13. stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
  14. env: { ...process.env }
  15. };
  16. }
  17. const triangleProcess = fork("triangle/main.js", [], getChildOptions(9229));
  18. const triangleData = new ProcessData(triangleProcess, 'triangle');
  19. triangleData.registerResponse('gamesRelations', async () => {
  20. const gamesRelations = Object.values(getGamesRelationsMap(true));
  21. return Promise.resolve(gamesRelations);
  22. });
  23. triangleData.registerRequest('solutions', solutions => {
  24. const oldSolutions = new Map((Store.get('solutions') ?? []).map(item => [item.sid, item]));
  25. const newSolutions = new Map(solutions.map(item => [item.sid, item]));
  26. const changed = {
  27. add: [],
  28. update: [],
  29. remove: [],
  30. }
  31. oldSolutions.forEach((item, sid) => {
  32. if (!newSolutions.has(sid)) {
  33. changed.remove.push(sid);
  34. }
  35. else if (newSolutions.get(sid).sol.win_profit_rate != item.sol.win_profit_rate || JSON.stringify(newSolutions.get(sid).cpr) != JSON.stringify(item.cpr)) {
  36. changed.update.push(sid);
  37. }
  38. });
  39. newSolutions.forEach((item, sid) => {
  40. if (!oldSolutions.has(sid)) {
  41. changed.add.push(sid);
  42. }
  43. });
  44. if (changed.update.length || changed.add.length || changed.remove.length) {
  45. Store.set('solutions', solutions);
  46. // const profitableSolutions = solutions.filter(solution => solution.sol.win_profit_rate > 0);
  47. // if (profitableSolutions.length) {
  48. // Logs.outDev('profitable solutions', profitableSolutions);
  49. // }
  50. }
  51. });
  52. /**
  53. * 通用的平台数据更新函数
  54. * @param {string} platform - 平台名称
  55. * @param {Array} newItems - 新的数据项数组
  56. * @param {string} storeKey - Store 中的键名
  57. * @returns {Promise}
  58. */
  59. const updatePlatformData = async ({ platform, newItems, storeKey }) => {
  60. if (!platform || !newItems?.length) {
  61. return Promise.reject(new Error('invalid request', { cause: 400 }));
  62. }
  63. let changed = false;
  64. const storeData = Store.get(storeKey) ?? {};
  65. const { [platform]: storePlatformItems = [] } = storeData;
  66. const storePlatformItemsMap = new Map(storePlatformItems.map(item => [item.id, item]));
  67. const newPlatformItemsMap = new Map(newItems.map(item => [item.id, item]));
  68. // 删除不存在的项
  69. storePlatformItemsMap.forEach(item => {
  70. if (!newPlatformItemsMap.has(item.id)) {
  71. storePlatformItemsMap.delete(item.id);
  72. changed = true;
  73. }
  74. });
  75. // 添加新的项
  76. newPlatformItemsMap.forEach(item => {
  77. if (!storePlatformItemsMap.has(item.id)) {
  78. storePlatformItemsMap.set(item.id, item);
  79. changed = true;
  80. }
  81. });
  82. // 更新 Store 中的数据
  83. if (changed) {
  84. const updatedPlatformItems = Array.from(storePlatformItemsMap.values());
  85. storeData[platform] = updatedPlatformItems;
  86. Store.set(storeKey, storeData);
  87. }
  88. return Promise.resolve();
  89. };
  90. /**
  91. * 更新联赛数据
  92. * @param {string} platform - 平台名称
  93. * @param {Array} leagues - 联赛数据
  94. * @returns
  95. */
  96. export const updateLeagues = async ({ platform, leagues }) => {
  97. return updatePlatformData({ platform, newItems: leagues, storeKey: 'leagues' });
  98. };
  99. /**
  100. * 获取过滤后的联赛数据
  101. * @param {string} platform - 平台名称
  102. * @returns
  103. */
  104. export const getRelatedLeagues = async (platform) => {
  105. const polymarketLeagues = Store.get('polymarket', 'leagues') ?? [];
  106. const polymarketLeaguesSet = new Set(polymarketLeagues.map(item => item.id));
  107. const leaguesRelations = Store.get('leaguesRelations') ?? {};
  108. const filteredLeagues = Object.values(leaguesRelations).filter(relation => {
  109. return polymarketLeaguesSet.has(relation.platforms.polymarket.id);
  110. }).map(relation => relation.platforms[platform]);
  111. return filteredLeagues;
  112. }
  113. /**
  114. * 更新比赛数据
  115. * @param {string} platform - 平台名称
  116. * @param {Array} games - 比赛数据
  117. * @returns
  118. */
  119. export const updateGames = async ({ platform, games }) => {
  120. return updatePlatformData({ platform, newItems: games, storeKey: 'games' });
  121. };
  122. /**
  123. * 获取过滤后的比赛数据
  124. * @param {string} platform - 平台名称
  125. * @returns
  126. */
  127. export const getRelatedGames = async (platform) => {
  128. const gamesRelations = Store.get('gamesRelations') ?? {};
  129. const filteredGames = Object.values(gamesRelations).map(relation => relation.platforms[platform]);
  130. return filteredGames;
  131. }
  132. /**
  133. * 更新赔率数据
  134. * @param {string} platform - 平台名称
  135. * @param {Array} games - 赔率数据
  136. * @param {number} timestamp - 时间戳
  137. * @returns
  138. */
  139. export const updateOdds = async ({ platform, games, timestamp }) => {
  140. Store.set(platform, { games, timestamp }, 'odds');
  141. return Promise.resolve();
  142. };
  143. /**
  144. * 同步QBoss赔率数据
  145. * @param {*} relationsData
  146. */
  147. const syncObossOdds = (relationsData) => {
  148. const timestamp = Date.now();
  149. const pinnacle = [];
  150. const obsports = [];
  151. const huangguan = [];
  152. relationsData.forEach(item => {
  153. Object.entries(item.rel).forEach(([key, value]) => {
  154. const { eventId: id, ...rest } = value;
  155. const game = { id, ...rest }
  156. if (key === 'pc') {
  157. pinnacle.push(game);
  158. }
  159. else if (key === 'ob') {
  160. obsports.push(game);
  161. }
  162. else if (key === 'hg') {
  163. huangguan.push(game);
  164. }
  165. });
  166. });
  167. Promise.all([
  168. updateOdds({ platform: 'pinnacle', games: pinnacle, timestamp }),
  169. updateOdds({ platform: 'obsports', games: obsports, timestamp }),
  170. updateOdds({ platform: 'huangguan', games: huangguan, timestamp }),
  171. ]);
  172. }
  173. /**
  174. * 定时更新QBoss赔率数据
  175. */
  176. const updateObossOdds = () => {
  177. getObossOdds()
  178. .then(res => {
  179. if (res.statusCode === 200) {
  180. // Logs.outDev('update oboss odds', res.data);
  181. return syncObossOdds(res.data);
  182. }
  183. return Promise.reject(new Error(`status code ${res.statusCode}`));
  184. })
  185. .catch(error => {
  186. Logs.out('failed to update oboss odds', error.message);
  187. Logs.errDev(error);
  188. })
  189. .finally(() => {
  190. setTimeout(() => {
  191. updateObossOdds();
  192. }, 1000 * 2);
  193. });
  194. }
  195. updateObossOdds();
  196. /**
  197. * 同步关联比赛数据
  198. * @param {*} relationsData
  199. */
  200. const syncGamesRelations = (relationsData) => {
  201. const storeRelations = Store.get('gamesRelations') ?? {};
  202. const newRelations = relationsData.map(item => {
  203. const {
  204. event_id: pmId,
  205. ps_event_id: pcId,
  206. ob_event_id: obId,
  207. hg_event_id: hgId,
  208. start_time: startTime,
  209. } = item;
  210. const timestamp = new Date(startTime).getTime();
  211. return {
  212. id: pmId,
  213. timestamp,
  214. platforms: {
  215. polymarket: { id: +pmId },
  216. pinnacle: { id: +pcId },
  217. huangguan: { id: +hgId },
  218. obsports: { id: +obId }
  219. }
  220. }
  221. });
  222. const changed = { add: 0, update: 0, remove: 0 }
  223. const newRelationsSet = new Set(newRelations.map(item => item.id));
  224. Object.keys(storeRelations).forEach(id => {
  225. if (!newRelationsSet.has(+id)) {
  226. delete storeRelations[id];
  227. changed.remove++;
  228. }
  229. });
  230. newRelations.forEach(item => {
  231. if (!storeRelations[item.id]) {
  232. storeRelations[item.id] = item;
  233. changed.add++;
  234. }
  235. });
  236. if (changed.add || changed.update || changed.remove) {
  237. Store.set('gamesRelations', storeRelations);
  238. Logs.outDev('sync games relations', changed, storeRelations);
  239. }
  240. }
  241. /**
  242. * 定时更新关联比赛
  243. */
  244. const updateGamesRelations = () => {
  245. getSoccerGames()
  246. .then(res => {
  247. if (res.success) {
  248. return syncGamesRelations(res.data);
  249. }
  250. return Promise.reject(new Error(res.message));
  251. })
  252. .catch(error => {
  253. Logs.out('failed to update games relations', error.message);
  254. Logs.errDev(error);
  255. })
  256. .finally(() => {
  257. setTimeout(() => {
  258. updateGamesRelations();
  259. }, 1000 * 30);
  260. });
  261. }
  262. updateGamesRelations();
  263. export default {
  264. updateLeagues, getRelatedLeagues,
  265. updateGames, getRelatedGames,
  266. updateOdds,
  267. };