瀏覽代碼

更新提交策略方式

flyzto 3 周之前
父節點
當前提交
6f51c29248

+ 5 - 7
pinnacle/libs/pinnacleClient.js

@@ -31,7 +31,9 @@ const pinnacleWebOptions = {
 
 
 export const pinnacleRequest = async (options) => {
 export const pinnacleRequest = async (options) => {
 
 
-  const { url, username, password, ...optionsRest } = options;
+  const { url, ...optionsRest } = options;
+  const username = process.env.PINNACLE_USERNAME;
+  const password = process.env.PINNACLE_PASSWORD;
   if (!url || !username || !password) {
   if (!url || !username || !password) {
     throw new Error("url、username、password is required");
     throw new Error("url、username、password is required");
   }
   }
@@ -62,9 +64,7 @@ export const pinnacleRequest = async (options) => {
 export const pinnacleGet = async (url, params) => {
 export const pinnacleGet = async (url, params) => {
   return pinnacleRequest({
   return pinnacleRequest({
     url,
     url,
-    params,
-    username: process.env.PINNACLE_USERNAME,
-    password: process.env.PINNACLE_PASSWORD,
+    params
   })
   })
   .catch(err => {
   .catch(err => {
     const source = { url, params };
     const source = { url, params };
@@ -90,9 +90,7 @@ export const pinnaclePost = async (url, data) => {
     headers: {
     headers: {
       'Content-Type': 'application/json',
       'Content-Type': 'application/json',
     },
     },
-    data,
-    username: process.env.PINNACLE_USERNAME,
-    password: process.env.PINNACLE_PASSWORD,
+    data
   });
   });
 }
 }
 
 

+ 67 - 58
pinnacle/main.js

@@ -20,8 +20,9 @@ const gamesMapCacheFile = path.join(__dirname, './cache/pinnacleGamesCache.json'
 const globalDataCacheFile = path.join(__dirname, './cache/pinnacleGlobalDataCache.json');
 const globalDataCacheFile = path.join(__dirname, './cache/pinnacleGlobalDataCache.json');
 
 
 const GLOBAL_DATA = {
 const GLOBAL_DATA = {
-  filteredLeagues: [],
-  filteredGames: [],
+  relatedLeagues: [],
+  selectedLeagues: [],
+  relatedGames: [],
   gamesMap: {},
   gamesMap: {},
   straightFixturesVersion: 0,
   straightFixturesVersion: 0,
   straightFixturesCount: 0,
   straightFixturesCount: 0,
@@ -65,12 +66,12 @@ const updateWebLeagues = async () => {
     return Array.from(leaguesMap.values()).sort((a, b) => a.id - b.id);
     return Array.from(leaguesMap.values()).sort((a, b) => a.id - b.id);
   })
   })
   .then(leagues => {
   .then(leagues => {
-    Logs.outDev('update leagues list', leagues);
+    Logs.outDev('update leagues list', leagues.length);
     return platformPost('/api/platforms/update_leagues', { platform: 'pinnacle', leagues });
     return platformPost('/api/platforms/update_leagues', { platform: 'pinnacle', leagues });
   })
   })
   .then(() => {
   .then(() => {
     Logs.outDev('leagues list updated');
     Logs.outDev('leagues list updated');
-    return Promise.resolve(60);
+    return Promise.resolve({ delay: 60 });
   });
   });
 }
 }
 
 
@@ -79,7 +80,8 @@ const updateWebLeagues = async () => {
  */
  */
 const updateWebLeaguesLoop = () => {
 const updateWebLeaguesLoop = () => {
   updateWebLeagues()
   updateWebLeagues()
-  .then(delay => {
+  .then(data => {
+    const { delay=5 } = data ?? {};
     setTimeout(() => {
     setTimeout(() => {
       updateWebLeaguesLoop();
       updateWebLeaguesLoop();
     }, 1000 * delay);
     }, 1000 * delay);
@@ -96,23 +98,23 @@ const updateWebLeaguesLoop = () => {
  * 更新Web比赛列表
  * 更新Web比赛列表
  */
  */
 const updateWebGames = async () => {
 const updateWebGames = async () => {
-  if (!GLOBAL_DATA.filteredLeagues.length) {
-    return Promise.resolve(5);
+  if (!GLOBAL_DATA.relatedLeagues.length) {
+    return Promise.resolve({ delay: 5 });
   }
   }
-  const getWebGamesToday = pinnacleWebGames(GLOBAL_DATA.filteredLeagues, 1);
-  const getWebGamesTomorrow = pinnacleWebGames(GLOBAL_DATA.filteredLeagues, 0);
+  const getWebGamesToday = pinnacleWebGames(GLOBAL_DATA.relatedLeagues, 1);
+  const getWebGamesTomorrow = pinnacleWebGames(GLOBAL_DATA.relatedLeagues, 0);
   return Promise.all([getWebGamesToday, getWebGamesTomorrow])
   return Promise.all([getWebGamesToday, getWebGamesTomorrow])
   .then(data => {
   .then(data => {
     const [webGamesToday, webGamesTomorrow] = data;
     const [webGamesToday, webGamesTomorrow] = data;
     return webGamesToday.concat(webGamesTomorrow).sort((a, b) => a.timestamp - b.timestamp);
     return webGamesToday.concat(webGamesTomorrow).sort((a, b) => a.timestamp - b.timestamp);
   })
   })
   .then(games => {
   .then(games => {
-    Logs.outDev('update games list', games);
+    Logs.outDev('update games list', games.length);
     return platformPost('/api/platforms/update_games', { platform: 'pinnacle', games });
     return platformPost('/api/platforms/update_games', { platform: 'pinnacle', games });
   })
   })
   .then(() => {
   .then(() => {
     Logs.outDev('games list updated');
     Logs.outDev('games list updated');
-    return Promise.resolve(60);
+    return Promise.resolve({ delay: 60 });
   });
   });
 }
 }
 
 
@@ -121,7 +123,8 @@ const updateWebGames = async () => {
  */
  */
 const updateWebGamesLoop = () => {
 const updateWebGamesLoop = () => {
   updateWebGames()
   updateWebGames()
-  .then(delay => {
+  .then(data => {
+    const { delay=5 } = data ?? {};
     setTimeout(() => {
     setTimeout(() => {
       updateWebGamesLoop();
       updateWebGamesLoop();
     }, 1000 * delay);
     }, 1000 * delay);
@@ -138,18 +141,18 @@ const updateWebGamesLoop = () => {
  * 获取过滤后的联赛数据
  * 获取过滤后的联赛数据
  * @returns
  * @returns
  */
  */
-const updateFilteredLeagues = () => {
-  platformGet('/api/platforms/get_filtered_leagues', { platform: 'pinnacle' })
+const updateRelatedLeagues = () => {
+  platformGet('/api/platforms/get_related_leagues', { platform: 'pinnacle' })
   .then(res => {
   .then(res => {
-    const { data: filteredLeagues } = res;
-    GLOBAL_DATA.filteredLeagues = filteredLeagues.map(item => item.id);
+    const { data: relatedLeagues } = res;
+    GLOBAL_DATA.relatedLeagues = relatedLeagues.map(item => item.id);
   })
   })
   .catch(error => {
   .catch(error => {
     Logs.err('failed to update filtered leagues', error.message);
     Logs.err('failed to update filtered leagues', error.message);
   })
   })
   .finally(() => {
   .finally(() => {
     setTimeout(() => {
     setTimeout(() => {
-      updateFilteredLeagues();
+      updateRelatedLeagues();
     }, 1000 * 10);
     }, 1000 * 10);
   });
   });
 }
 }
@@ -158,18 +161,19 @@ const updateFilteredLeagues = () => {
  * 获取过滤后的比赛数据
  * 获取过滤后的比赛数据
  * @returns
  * @returns
  */
  */
-const updateFilteredGames = () => {
-  platformGet('/api/platforms/get_filtered_games', { platform: 'pinnacle' })
+const updateRelatedGames = () => {
+  platformGet('/api/platforms/get_related_games', { platform: 'pinnacle' })
   .then(res => {
   .then(res => {
-    const { data: filteredGames } = res;
-    GLOBAL_DATA.filteredGames = filteredGames.map(item => item.id);
+    const { data: relatedGames } = res;
+    GLOBAL_DATA.relatedGames = relatedGames.map(item => item.id);
+    GLOBAL_DATA.selectedLeagues = [...new Set(relatedGames.map(item => item.leagueId))];
   })
   })
   .catch(error => {
   .catch(error => {
-    Logs.err('failed to update filtered games', error.message);
+    Logs.err('failed to update related games', error.message);
   })
   })
   .finally(() => {
   .finally(() => {
     setTimeout(() => {
     setTimeout(() => {
-      updateFilteredGames();
+      updateRelatedGames();
     }, 1000 * 10);
     }, 1000 * 10);
   });
   });
 }
 }
@@ -179,11 +183,13 @@ const updateFilteredGames = () => {
  * @returns
  * @returns
  */
  */
 const getStraightFixtures = async () => {
 const getStraightFixtures = async () => {
-  if (!GLOBAL_DATA.filteredLeagues.length) {
-    resetVersionsCount();
-    return Promise.reject(new Error('no filtered leagues', { cause: 400 }));
+  if (!GLOBAL_DATA.selectedLeagues.length) {
+    // resetVersionsCount();
+    GLOBAL_DATA.straightFixturesVersion = 0;
+    return Promise.resolve({ games: [], update: 'full' });
+    // return Promise.reject(new Error('no related leagues', { cause: 400 }));
   }
   }
-  const leagueIds = GLOBAL_DATA.filteredLeagues.join(',');
+  const leagueIds = GLOBAL_DATA.selectedLeagues.join(',');
   let since = GLOBAL_DATA.straightFixturesVersion;
   let since = GLOBAL_DATA.straightFixturesVersion;
   if (GLOBAL_DATA.straightFixturesCount >= 12) {
   if (GLOBAL_DATA.straightFixturesCount >= 12) {
     since = 0;
     since = 0;
@@ -250,11 +256,13 @@ const updateStraightFixtures = async () => {
  * @returns
  * @returns
  */
  */
 const getStraightOdds = async () => {
 const getStraightOdds = async () => {
-  if (!GLOBAL_DATA.filteredLeagues.length) {
-    resetVersionsCount();
-    return Promise.reject(new Error('no filtered leagues', { cause: 400 }));
+  if (!GLOBAL_DATA.selectedLeagues.length) {
+    // resetVersionsCount();
+    GLOBAL_DATA.straightOddsVersion = 0;
+    return Promise.resolve([]);
+    // return Promise.reject(new Error('no related leagues', { cause: 400 }));
   }
   }
-  const leagueIds = GLOBAL_DATA.filteredLeagues.join(',');
+  const leagueIds = GLOBAL_DATA.selectedLeagues.join(',');
   const since = GLOBAL_DATA.straightOddsVersion;
   const since = GLOBAL_DATA.straightOddsVersion;
   // if (GLOBAL_DATA.straightOddsCount >= 27) {
   // if (GLOBAL_DATA.straightOddsCount >= 27) {
   //   since = 0;
   //   since = 0;
@@ -312,11 +320,13 @@ const updateStraightOdds = async () => {
  * @returns
  * @returns
  */
  */
 const getSpecialFixtures = async () => {
 const getSpecialFixtures = async () => {
-  if (!GLOBAL_DATA.filteredLeagues.length) {
-    resetVersionsCount();
-    return Promise.reject(new Error('no filtered leagues', { cause: 400 }));
+  if (!GLOBAL_DATA.selectedLeagues.length) {
+    // resetVersionsCount();
+    GLOBAL_DATA.specialFixturesVersion = 0;
+    return Promise.resolve({ specials: [], update: 'full' });
+    // return Promise.reject(new Error('no related leagues', { cause: 400 }));
   }
   }
-  const leagueIds = GLOBAL_DATA.filteredLeagues.join(',');
+  const leagueIds = GLOBAL_DATA.selectedLeagues.join(',');
   let since = GLOBAL_DATA.specialFixturesVersion;
   let since = GLOBAL_DATA.specialFixturesVersion;
   if (GLOBAL_DATA.specialFixturesCount >= 18) {
   if (GLOBAL_DATA.specialFixturesCount >= 18) {
     since = 0;
     since = 0;
@@ -446,11 +456,13 @@ const updateSpecialFixtures = async () => {
  * @returns
  * @returns
  */
  */
 const getSpecialsOdds = async () => {
 const getSpecialsOdds = async () => {
-  if (!GLOBAL_DATA.filteredLeagues.length) {
-    resetVersionsCount();
-    return Promise.reject(new Error('no filtered leagues', { cause: 400 }));
+  if (!GLOBAL_DATA.selectedLeagues.length) {
+    // resetVersionsCount();
+    GLOBAL_DATA.specialsOddsVersion = 0;
+    return Promise.resolve([]);
+    // return Promise.reject(new Error('no related leagues', { cause: 400 }));
   }
   }
-  const leagueIds = GLOBAL_DATA.filteredLeagues.join(',');
+  const leagueIds = GLOBAL_DATA.selectedLeagues.join(',');
   const since = GLOBAL_DATA.specialsOddsVersion;
   const since = GLOBAL_DATA.specialsOddsVersion;
   // if (GLOBAL_DATA.specialsOddsCount >= 33) {
   // if (GLOBAL_DATA.specialsOddsCount >= 33) {
   //   since = 0;
   //   since = 0;
@@ -517,12 +529,12 @@ const getInRunning = async () => {
     const sportId = 29;
     const sportId = 29;
     const leagues = data.sports?.find(sport => sport.id == sportId)?.leagues ?? [];
     const leagues = data.sports?.find(sport => sport.id == sportId)?.leagues ?? [];
     return leagues.filter(league => {
     return leagues.filter(league => {
-      if (!GLOBAL_DATA.filteredLeagues.length) {
+      if (!GLOBAL_DATA.selectedLeagues.length) {
         return true;
         return true;
       }
       }
       const { id } = league;
       const { id } = league;
-      const filteredLeaguesSet = new Set(GLOBAL_DATA.filteredLeagues);
-      return filteredLeaguesSet.has(id);
+      const selectedLeaguesSet = new Set(GLOBAL_DATA.selectedLeagues);
+      return selectedLeaguesSet.has(id);
     }).flatMap(league => league.events);
     }).flatMap(league => league.events);
   });
   });
 }
 }
@@ -554,14 +566,8 @@ const updateInRunning = async () => {
  * @returns {Object}
  * @returns {Object}
  */
  */
 const getGamesEvents = () => {
 const getGamesEvents = () => {
-  const {
-    filteredGames, gamesMap,
-    straightFixturesVersion: sfv,
-    specialFixturesVersion: pfv,
-    straightOddsVersion: sov,
-    specialsOddsVersion: pov
-  } = GLOBAL_DATA;
-  const filteredGamesSet = new Set(filteredGames);
+  const { relatedGames, gamesMap } = GLOBAL_DATA;
+  const relatedGamesSet = new Set(relatedGames);
   const nowTime = Date.now();
   const nowTime = Date.now();
   const gamesData = {};
   const gamesData = {};
   Object.values(gamesMap).forEach(game => {
   Object.values(gamesMap).forEach(game => {
@@ -592,12 +598,12 @@ const getGamesEvents = () => {
     }
     }
 
 
     let actived = false;
     let actived = false;
-    if (liveStatus != 1 && (filteredGamesSet.has(id) || !filteredGames.length)) {
+    if (liveStatus != 1 && (relatedGamesSet.has(id) || !relatedGames.length)) {
       actived = true;  // 在赛前列表中
       actived = true;  // 在赛前列表中
       game.id = id;
       game.id = id;
       game.originId = 0;
       game.originId = 0;
     }
     }
-    else if (liveStatus == 1 && (filteredGamesSet.has(parentId) || !filteredGames.length)) {
+    else if (liveStatus == 1 && (relatedGamesSet.has(parentId) || !relatedGames.length)) {
       actived = true;  // 在滚球列表中
       actived = true;  // 在滚球列表中
       game.id = parentId;
       game.id = parentId;
       game.originId = id;
       game.originId = id;
@@ -612,7 +618,7 @@ const getGamesEvents = () => {
     }
     }
   });
   });
   const games = Object.values(gamesData).flat();
   const games = Object.values(gamesData).flat();
-  const timestamp = Math.max(sfv, pfv, sov, pov);
+  const timestamp = nowTime;
   const data = { games, timestamp };
   const data = { games, timestamp };
   return data;
   return data;
 }
 }
@@ -676,10 +682,13 @@ const pinnacleDataLoop = () => {
     }
     }
   })
   })
   .finally(() => {
   .finally(() => {
-    const { loopActive } = GLOBAL_DATA;
-    let loopDelay = 1000 * 5;
+    const { loopActive, selectedLeagues } = GLOBAL_DATA;
+    let loopDelay = 5_000;
     if (!loopActive) {
     if (!loopActive) {
-      loopDelay = 1000 * 60;
+      loopDelay = 60_000;
+      resetVersionsCount();
+    }
+    else if (!selectedLeagues.length) {
       resetVersionsCount();
       resetVersionsCount();
     }
     }
     else {
     else {
@@ -716,8 +725,8 @@ const main = () => {
     Logs.err('USERNAME or PASSWORD is not set');
     Logs.err('USERNAME or PASSWORD is not set');
     return;
     return;
   }
   }
-  updateFilteredLeagues();
-  updateFilteredGames();
+  updateRelatedLeagues();
+  updateRelatedGames();
   loadGlobalDataFromCache()
   loadGlobalDataFromCache()
   .then(() => {
   .then(() => {
     Logs.out('global data loaded');
     Logs.out('global data loaded');

+ 10 - 10
polymarket/main.js

@@ -19,7 +19,7 @@ const GLOBAL_DATA = {
   marketsData: {},
   marketsData: {},
   clobTokenMap: {},
   clobTokenMap: {},
   filteredLeagues: [],
   filteredLeagues: [],
-  filteredGames: [],
+  relatedGames: [],
   marketWsClient: null,
   marketWsClient: null,
   wsClientData: null,
   wsClientData: null,
   lastChangeTime: 0,
   lastChangeTime: 0,
@@ -86,7 +86,7 @@ const updateLeaguesAndGames = (marketsData) => {
     return { leagues, games };
     return { leagues, games };
   })
   })
   .then(data => {
   .then(data => {
-    Logs.outDev('update leagues and games list', data);
+    Logs.outDev('update leagues and games list', data.leagues.length, data.games.length);
     return submitLeaguesAndGames(data);
     return submitLeaguesAndGames(data);
   })
   })
   .then(() => {
   .then(() => {
@@ -101,19 +101,19 @@ const updateLeaguesAndGames = (marketsData) => {
  * 获取过滤后的比赛数据
  * 获取过滤后的比赛数据
  * @returns
  * @returns
  */
  */
-const updateFilteredGames = () => {
-  platformGet('/api/platforms/get_filtered_games', { platform: 'polymarket' })
+const updateRelatedGames = () => {
+  platformGet('/api/platforms/get_related_games', { platform: 'polymarket' })
   .then(res => {
   .then(res => {
-    const { data: filteredGames } = res;
-    Logs.outDev('filteredGames updated', filteredGames);
-    GLOBAL_DATA.filteredGames = filteredGames.map(item => item.id);
+    const { data: relatedGames } = res;
+    Logs.outDev('relatedGames updated', relatedGames.length);
+    GLOBAL_DATA.relatedGames = relatedGames.map(item => item.id);
   })
   })
   .catch(error => {
   .catch(error => {
-    Logs.out('failed to update filtered games', error.message);
+    Logs.out('failed to update related games', error.message);
   })
   })
   .finally(() => {
   .finally(() => {
     setTimeout(() => {
     setTimeout(() => {
-      updateFilteredGames();
+      updateRelatedGames();
     }, 1000 * 10);
     }, 1000 * 10);
   });
   });
 }
 }
@@ -330,7 +330,7 @@ const main = () => {
   GLOBAL_DATA.marketWsClient = marketWsClient;
   GLOBAL_DATA.marketWsClient = marketWsClient;
   marketWsClient.connect();
   marketWsClient.connect();
   marketWsClient.on('open', () => {
   marketWsClient.on('open', () => {
-    updateFilteredGames();
+    updateRelatedGames();
     updateGamesMarkets();
     updateGamesMarkets();
     syncMarketsData();
     syncMarketsData();
     updateOddsLoop();
     updateOddsLoop();

+ 23 - 0
server/libs/getSolutions.js

@@ -0,0 +1,23 @@
+export const getSolutionsWithRelations = async (solutionsList, gamesRelations, maxLength=0) => {
+  const selectedRelations = {};
+  solutionsList.forEach(solution => {
+    const { info: { id }, ...solutionRest } = solution;
+    if (!gamesRelations[id]) {
+      return;
+    }
+    if (!selectedRelations[id]) {
+      selectedRelations[id] = { ...gamesRelations[id] };
+    }
+    if (!selectedRelations[id]['solutions']) {
+      selectedRelations[id]['solutions'] = [];
+    }
+    if (maxLength > 0 && selectedRelations[id]['solutions'].length >= maxLength) {
+      return;
+    }
+    selectedRelations[id]['solutions'].push(solutionRest);
+  });
+  const relationsList = Object.values(selectedRelations).sort((a, b) => {
+    return b.solutions[0].sol.win_profit_rate - a.solutions[0].sol.win_profit_rate;
+  });
+  return Promise.resolve(relationsList);
+}

+ 2 - 18
server/models/Games.js

@@ -1,4 +1,5 @@
 import GetTranslation from "../libs/getTranslation.js";
 import GetTranslation from "../libs/getTranslation.js";
+import { getSolutionsWithRelations } from "../libs/getSolutions.js";
 import Store from "../state/store.js";
 import Store from "../state/store.js";
 
 
 import { getPlatformIorsDetailInfo, getSolutionByLatestIors, getSoulutionBetResult } from "./Markets.js";
 import { getPlatformIorsDetailInfo, getSolutionByLatestIors, getSoulutionBetResult } from "./Markets.js";
@@ -133,24 +134,7 @@ export const getSolutions = async ({ min_profit_rate = 0 } = {}) => {
     return b.sol.win_profit_rate - a.sol.win_profit_rate;
     return b.sol.win_profit_rate - a.sol.win_profit_rate;
   });
   });
   const gamesRelations = Store.get('gamesRelations') ?? {};
   const gamesRelations = Store.get('gamesRelations') ?? {};
-  const selectedRelations = {};
-  solutionsList.forEach(solution => {
-    const { info: { id }, ...solutionRest } = solution;
-    if (!gamesRelations[id]) {
-      return;
-    }
-    if (!selectedRelations[id]) {
-      selectedRelations[id] = { ...gamesRelations[id] };
-    }
-    if (!selectedRelations[id]['solutions']) {
-      selectedRelations[id]['solutions'] = [];
-    }
-    selectedRelations[id]['solutions'].push(solutionRest);
-  });
-  const relationsList = Object.values(selectedRelations).sort((a, b) => {
-    return b.solutions[0].sol.win_profit_rate - a.solutions[0].sol.win_profit_rate;
-  });
-  return Promise.resolve(relationsList);
+  return getSolutionsWithRelations(solutionsList, gamesRelations, 5);
 }
 }
 
 
 /**
 /**

+ 55 - 0
server/models/Partner.js

@@ -0,0 +1,55 @@
+import crypto from "crypto";
+import axios from "axios";
+
+const API_BASE_URL = 'https://dev.api.ppai.win/api/p/gate';
+const APP_ID = 'ppai.partner.taoge';
+const APP_KEY = '7F9AE8D6D2B9D39B7D76AA1EC06A5534';
+
+/**
+ * 生成SHA256
+ */
+const generateSHA256 = (message) => {
+  return crypto.createHash('sha256').update(message).digest('hex');
+}
+
+/**
+ * 生成随机 nonce
+ */
+const generateNonce = () => {
+  return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
+}
+
+/**
+ * 生成签名
+ */
+const generateSignature = (appKey, action, timestamp, nonce) => {
+  return generateSHA256(`${appKey}${action}${timestamp}${nonce}`);
+}
+
+/**
+ * 获取时间戳 秒级
+ */
+const getTimestamp = () => {
+  return Math.floor(Date.now() / 1000);
+}
+
+/**
+ * 请求数据接口
+ */
+export const requestData = async (action, data) => {
+  const timestamp = getTimestamp();
+  const nonce = generateNonce();
+  const sign = generateSignature(APP_KEY, action, timestamp, nonce);
+  const appid = APP_ID;
+  const requestData = { action, appid, nonce, sign, timestamp };
+  if (Object.keys(data).length > 0) {
+    requestData.params = data;
+  }
+  return axios.post(API_BASE_URL, requestData).then(res => res.data);
+}
+
+export const updateSolutions = async (solutions) => {
+  return requestData('opps.soccer', { solutions });
+}
+
+export default { updateSolutions };

+ 22 - 4
server/models/Platforms.js

@@ -4,6 +4,9 @@ import Store from "../state/store.js";
 import ProcessData from "../libs/processData.js";
 import ProcessData from "../libs/processData.js";
 import Logs from "../libs/logs.js";
 import Logs from "../libs/logs.js";
 
 
+import { getSolutionsWithRelations } from "../libs/getSolutions.js";
+import { updateSolutions } from "./Partner.js";
+
 // import { getPlatformIorInfo } from "./Markets.js";
 // import { getPlatformIorInfo } from "./Markets.js";
 
 
 const getChildOptions = (inspect=9230) => {
 const getChildOptions = (inspect=9230) => {
@@ -64,6 +67,21 @@ triangleData.registerRequest('solutions', solutions => {
   });
   });
   if (changed.update.length || changed.add.length || changed.remove.length) {
   if (changed.update.length || changed.add.length || changed.remove.length) {
     Store.set('solutions', solutions);
     Store.set('solutions', solutions);
+    const gamesRelations = Store.get('gamesRelations') ?? {};
+    getSolutionsWithRelations(solutions, gamesRelations, 5)
+    .then(solutionsList => {
+      Logs.out('solutions with relations', solutionsList);
+      updateSolutions(solutionsList)
+      .then(res => {
+        Logs.out('updateSolutions res', res);
+      })
+      .catch(error => {
+        Logs.err('updateSolutions error', error);
+      });
+    })
+    .catch(error => {
+      Logs.err('getSolutionsWithRelations error', error);
+    });
   }
   }
 });
 });
 
 
@@ -126,7 +144,7 @@ export const updateLeagues = async ({ platform, leagues }) => {
  * @param {string} platform - 平台名称
  * @param {string} platform - 平台名称
  * @returns
  * @returns
  */
  */
-export const getFilteredLeagues = async (platform) => {
+export const getRelatedLeagues = async (platform) => {
   const polymarketLeagues = Store.get('polymarket', 'leagues') ?? [];
   const polymarketLeagues = Store.get('polymarket', 'leagues') ?? [];
   const polymarketLeaguesSet = new Set(polymarketLeagues.map(item => item.id));
   const polymarketLeaguesSet = new Set(polymarketLeagues.map(item => item.id));
   const leaguesRelations = Store.get('leaguesRelations') ?? {};
   const leaguesRelations = Store.get('leaguesRelations') ?? {};
@@ -151,7 +169,7 @@ export const updateGames = async ({ platform, games }) => {
  * @param {string} platform - 平台名称
  * @param {string} platform - 平台名称
  * @returns
  * @returns
  */
  */
-export const getFilteredGames = async (platform) => {
+export const getRelatedGames = async (platform) => {
   const gamesRelations = Store.get('gamesRelations') ?? {};
   const gamesRelations = Store.get('gamesRelations') ?? {};
   const filteredGames = Object.values(gamesRelations).map(relation => relation.platforms[platform]);
   const filteredGames = Object.values(gamesRelations).map(relation => relation.platforms[platform]);
   return filteredGames;
   return filteredGames;
@@ -170,7 +188,7 @@ export const updateOdds = async ({ platform, games, timestamp }) => {
 };
 };
 
 
 export default {
 export default {
-  updateLeagues, getFilteredLeagues,
-  updateGames, getFilteredGames,
+  updateLeagues, getRelatedLeagues,
+  updateGames, getRelatedGames,
   updateOdds,
   updateOdds,
 };
 };

+ 132 - 0
server/package-lock.json

@@ -10,6 +10,7 @@
       "license": "ISC",
       "license": "ISC",
       "dependencies": {
       "dependencies": {
         "@google-cloud/translate": "^9.3.0",
         "@google-cloud/translate": "^9.3.0",
+        "axios": "^1.13.5",
         "cookie-parser": "^1.4.7",
         "cookie-parser": "^1.4.7",
         "dayjs": "^1.11.19",
         "dayjs": "^1.11.19",
         "dotenv": "^17.2.3",
         "dotenv": "^17.2.3",
@@ -286,6 +287,23 @@
         "node": ">=8"
         "node": ">=8"
       }
       }
     },
     },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+      "license": "MIT"
+    },
+    "node_modules/axios": {
+      "version": "1.13.5",
+      "resolved": "https://registry.npmmirror.com/axios/-/axios-1.13.5.tgz",
+      "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==",
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.15.11",
+        "form-data": "^4.0.5",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
     "node_modules/balanced-match": {
     "node_modules/balanced-match": {
       "version": "1.0.2",
       "version": "1.0.2",
       "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
       "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -503,6 +521,18 @@
       "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
       "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
       "license": "MIT"
       "license": "MIT"
     },
     },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "license": "MIT",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
     "node_modules/content-disposition": {
     "node_modules/content-disposition": {
       "version": "1.0.1",
       "version": "1.0.1",
       "resolved": "https://registry.npmmirror.com/content-disposition/-/content-disposition-1.0.1.tgz",
       "resolved": "https://registry.npmmirror.com/content-disposition/-/content-disposition-1.0.1.tgz",
@@ -599,6 +629,15 @@
         }
         }
       }
       }
     },
     },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
     "node_modules/depd": {
     "node_modules/depd": {
       "version": "2.0.0",
       "version": "2.0.0",
       "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz",
       "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz",
@@ -721,6 +760,21 @@
         "node": ">= 0.4"
         "node": ">= 0.4"
       }
       }
     },
     },
+    "node_modules/es-set-tostringtag": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+      "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.6",
+        "has-tostringtag": "^1.0.2",
+        "hasown": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/escalade": {
     "node_modules/escalade": {
       "version": "3.2.0",
       "version": "3.2.0",
       "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz",
       "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz",
@@ -862,6 +916,26 @@
         "url": "https://opencollective.com/express"
         "url": "https://opencollective.com/express"
       }
       }
     },
     },
+    "node_modules/follow-redirects": {
+      "version": "1.15.11",
+      "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz",
+      "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/foreground-child": {
     "node_modules/foreground-child": {
       "version": "3.3.1",
       "version": "3.3.1",
       "resolved": "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.3.1.tgz",
       "resolved": "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.3.1.tgz",
@@ -878,6 +952,43 @@
         "url": "https://github.com/sponsors/isaacs"
         "url": "https://github.com/sponsors/isaacs"
       }
       }
     },
     },
+    "node_modules/form-data": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz",
+      "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+      "license": "MIT",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "es-set-tostringtag": "^2.1.0",
+        "hasown": "^2.0.2",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/form-data/node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/form-data/node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
     "node_modules/formdata-polyfill": {
     "node_modules/formdata-polyfill": {
       "version": "4.0.10",
       "version": "4.0.10",
       "resolved": "https://registry.npmmirror.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
       "resolved": "https://registry.npmmirror.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
@@ -1098,6 +1209,21 @@
         "url": "https://github.com/sponsors/ljharb"
         "url": "https://github.com/sponsors/ljharb"
       }
       }
     },
     },
+    "node_modules/has-tostringtag": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+      "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+      "license": "MIT",
+      "dependencies": {
+        "has-symbols": "^1.0.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/hasown": {
     "node_modules/hasown": {
       "version": "2.0.2",
       "version": "2.0.2",
       "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz",
       "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz",
@@ -1597,6 +1723,12 @@
         "node": ">= 0.10"
         "node": ">= 0.10"
       }
       }
     },
     },
+    "node_modules/proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+      "license": "MIT"
+    },
     "node_modules/qs": {
     "node_modules/qs": {
       "version": "6.14.1",
       "version": "6.14.1",
       "resolved": "https://registry.npmmirror.com/qs/-/qs-6.14.1.tgz",
       "resolved": "https://registry.npmmirror.com/qs/-/qs-6.14.1.tgz",

+ 1 - 0
server/package.json

@@ -12,6 +12,7 @@
   "license": "ISC",
   "license": "ISC",
   "dependencies": {
   "dependencies": {
     "@google-cloud/translate": "^9.3.0",
     "@google-cloud/translate": "^9.3.0",
+    "axios": "^1.13.5",
     "cookie-parser": "^1.4.7",
     "cookie-parser": "^1.4.7",
     "dayjs": "^1.11.19",
     "dayjs": "^1.11.19",
     "dotenv": "^17.2.3",
     "dotenv": "^17.2.3",

+ 4 - 4
server/routes/platforms.js

@@ -14,9 +14,9 @@ router.post('/update_leagues', (req, res) => {
   });
   });
 });
 });
 
 
-router.get('/get_filtered_leagues', (req, res) => {
+router.get('/get_related_leagues', (req, res) => {
   const { platform } = req.query;
   const { platform } = req.query;
-  Platforms.getFilteredLeagues(platform).then(leagues => {
+  Platforms.getRelatedLeagues(platform).then(leagues => {
     res.sendSuccess(leagues);
     res.sendSuccess(leagues);
   })
   })
   .catch(err => {
   .catch(err => {
@@ -35,9 +35,9 @@ router.post('/update_games', (req, res) => {
   });
   });
 });
 });
 
 
-router.get('/get_filtered_games', (req, res) => {
+router.get('/get_related_games', (req, res) => {
   const { platform } = req.query;
   const { platform } = req.query;
-  Platforms.getFilteredGames(platform)
+  Platforms.getRelatedGames(platform)
   .then(games => {
   .then(games => {
     res.sendSuccess(games);
     res.sendSuccess(games);
   })
   })

+ 5 - 1
server/triangle/main.js

@@ -21,13 +21,17 @@ const eventMatch = () => {
       Logs.outDev('relation list is empty');
       Logs.outDev('relation list is empty');
     }
     }
 
 
-    // Logs.outDev('relations', relations);
+    Logs.outDev('relations', relations);
 
 
     const passableEvents = getPassableEvents(relations);
     const passableEvents = getPassableEvents(relations);
     const solutions = eventsCombination(passableEvents);
     const solutions = eventsCombination(passableEvents);
     if (solutions?.length) {
     if (solutions?.length) {
+      Logs.outDev('solutions', solutions);
       processData.post('solutions', solutions);
       processData.post('solutions', solutions);
     }
     }
+    else {
+      Logs.outDev('no solutions');
+    }
   })
   })
   .finally(() => {
   .finally(() => {
     setTimeout(() => {
     setTimeout(() => {