Platforms.js 8.2 KB

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