| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842 |
- import 'dotenv/config';
- import { pinnacleRequest, getPsteryRelations, updateBaseEvents, notifyException } from "./libs/pinnacleClient.js";
- import { Logs } from "./libs/logs.js";
- import { getData, setData } from "./libs/cache.js";
- const gamesMapCacheFile = 'data/gamesCache.json';
- const globalDataCacheFile = 'data/globalDataCache.json';
- const GLOBAL_DATA = {
- filtedLeagues: [],
- filtedGames: [],
- gamesMap: {},
- straightFixturesVersion: 0,
- straightFixturesCount: 0,
- specialFixturesVersion: 0,
- specialFixturesCount: 0,
- straightOddsVersion: 0,
- // straightOddsCount: 0,
- specialsOddsVersion: 0,
- // specialsOddsCount: 0,
- requestErrorCount: 0,
- loopActive: false,
- loopResultTime: 0,
- };
- const resetVersionsCount = () => {
- GLOBAL_DATA.straightFixturesVersion = 0;
- GLOBAL_DATA.specialFixturesVersion = 0;
- GLOBAL_DATA.straightOddsVersion = 0;
- GLOBAL_DATA.specialsOddsVersion = 0;
- GLOBAL_DATA.straightFixturesCount = 0;
- GLOBAL_DATA.specialFixturesCount = 0;
- }
- const incrementVersionsCount = () => {
- GLOBAL_DATA.straightFixturesCount = 0;
- GLOBAL_DATA.specialFixturesCount = 0;
- }
- /**
- * 获取指定时区当前日期或时间
- * @param {number} offsetHours - 时区相对 UTC 的偏移(例如:-4 表示 GMT-4)
- * @param {boolean} [withTime=false] - 是否返回完整时间(默认只返回日期)
- * @returns {string} 格式化的日期或时间字符串
- */
- const getDateInTimezone = (offsetHours, withTime = false) => {
- const nowUTC = new Date();
- const targetTime = new Date(nowUTC.getTime() + offsetHours * 60 * 60 * 1000);
- const year = targetTime.getUTCFullYear();
- const month = String(targetTime.getUTCMonth() + 1).padStart(2, '0');
- const day = String(targetTime.getUTCDate()).padStart(2, '0');
- if (!withTime) {
- return `${year}-${month}-${day}`;
- }
- const hours = String(targetTime.getUTCHours()).padStart(2, '0');
- const minutes = String(targetTime.getUTCMinutes()).padStart(2, '0');
- const seconds = String(targetTime.getUTCSeconds()).padStart(2, '0');
- return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
- }
- const pinnacleGet = async (endpoint, params) => {
- return pinnacleRequest({
- endpoint,
- params,
- username: process.env.PINNACLE_USERNAME,
- password: process.env.PINNACLE_PASSWORD,
- proxy: process.env.NODE_HTTP_PROXY,
- })
- .catch(err => {
- const source = { endpoint, params };
- if (err?.response?.data) {
- const data = err.response.data;
- Object.assign(source, { data });
- }
- err.source = source;
- return Promise.reject(err);
- });
- };
- // const pinnaclePost = async(endpoint, data) => {
- // return pinnacleRequest({
- // endpoint,
- // data,
- // method: 'POST',
- // username: process.env.PINNACLE_USERNAME,
- // password: process.env.PINNACLE_PASSWORD,
- // proxy: process.env.NODE_HTTP_PROXY,
- // });
- // };
- const isArrayEqualUnordered = (arr1, arr2) => {
- if (arr1.length !== arr2.length) return false;
- const s1 = [...arr1].sort();
- const s2 = [...arr2].sort();
- return s1.every((v, i) => v === s2[i]);
- }
- const getFiltedGames = () => {
- return new Promise(resolve => {
- const updateFiltedGames = () => {
- getPsteryRelations()
- .then(res => {
- if (res.statusCode !== 200) {
- throw new Error(res.message);
- }
- // Logs.outDev('update filted games', res.data);
- const games = res.data.map(game => {
- const { eventId, leagueId } = game?.rel?.ps ?? {};
- return {
- eventId,
- leagueId,
- };
- });
- const newFiltedLeagues = [...new Set(games.map(game => game.leagueId).filter(leagueId => leagueId))];
- if (!isArrayEqualUnordered(newFiltedLeagues, GLOBAL_DATA.filtedLeagues)) {
- Logs.outDev('filted leagues changed', newFiltedLeagues);
- resetVersionsCount();
- GLOBAL_DATA.filtedLeagues = newFiltedLeagues;
- }
- GLOBAL_DATA.filtedGames = games.map(game => game.eventId).filter(eventId => eventId);
- resolve();
- setTimeout(updateFiltedGames, 1000 * 60);
- })
- .catch(err => {
- Logs.err('failed to update filted games:', err.message);
- setTimeout(updateFiltedGames, 1000 * 5);
- });
- }
- updateFiltedGames();
- });
- }
- const getStraightFixtures = async () => {
- if (!GLOBAL_DATA.filtedLeagues.length) {
- return Promise.reject(new Error('no filted leagues'));
- }
- const leagueIds = GLOBAL_DATA.filtedLeagues.join(',');
- let since = GLOBAL_DATA.straightFixturesVersion;
- if (GLOBAL_DATA.straightFixturesCount >= 12) {
- since = 0;
- GLOBAL_DATA.straightFixturesCount = 0;
- }
- if (since == 0) {
- Logs.outDev('full update straight fixtures');
- }
- return pinnacleGet('/v3/fixtures', { sportId: 29, leagueIds, since })
- .then(data => {
- const { league, last } = data;
- if (!last) {
- return {};
- }
- GLOBAL_DATA.straightFixturesVersion = last;
- const games = league?.map(league => {
- const { id: leagueId, events } = league;
- return events?.map(event => {
- const { starts } = event;
- const timestamp = new Date(starts).getTime();
- return { leagueId, ...event, timestamp };
- });
- })
- .flat() ?? [];
- const update = since == 0 ? 'full' : 'increment';
- return { games, update };
- });
- }
- const updateStraightFixtures = async () => {
- return getStraightFixtures()
- .then(data => {
- const { games, update } = data;
- const { gamesMap } = GLOBAL_DATA;
- if (games?.length) {
- games.forEach(game => {
- const { id } = game;
- if (!gamesMap[id]) {
- gamesMap[id] = game;
- }
- else {
- Object.assign(gamesMap[id], game);
- }
- });
- }
- if (update && update == 'full') {
- const gamesSet = new Set(games.map(game => game.id));
- Object.keys(gamesMap).forEach(key => {
- if (!gamesSet.has(+key)) {
- delete gamesMap[key];
- }
- });
- }
- });
- }
- const getStraightOdds = async () => {
- if (!GLOBAL_DATA.filtedLeagues.length) {
- return Promise.reject(new Error('no filted leagues'));
- }
- const leagueIds = GLOBAL_DATA.filtedLeagues.join(',');
- const since = GLOBAL_DATA.straightOddsVersion;
- // if (GLOBAL_DATA.straightOddsCount >= 27) {
- // since = 0;
- // GLOBAL_DATA.straightOddsCount = 3;
- // }
- if (since == 0) {
- Logs.outDev('full update straight odds');
- }
- return pinnacleGet('/v3/odds', { sportId: 29, oddsFormat: 'Decimal', leagueIds, since })
- .then(data => {
- const { leagues, last } = data;
- if (!last) {
- return [];
- }
- GLOBAL_DATA.straightOddsVersion = last;
- const games = leagues?.flatMap(league => league.events);
- return games?.map(item => {
- const { periods, ...rest } = item;
- const period = periods?.find(period => period.number == 0);
- if (!period) {
- return rest;
- }
- return { ...rest, period };
- }) ?? [];
- });
- }
- const updateStraightOdds = async () => {
- return getStraightOdds()
- .then(games => {
- if (games.length) {
- const { gamesMap } = GLOBAL_DATA;
- games.forEach(game => {
- const { id, ...rest } = game;
- const localGame = gamesMap[id];
- if (localGame) {
- Object.assign(localGame, rest);
- }
- });
- }
- });
- }
- const getSpecialFixtures = async () => {
- if (!GLOBAL_DATA.filtedLeagues.length) {
- return Promise.reject(new Error('no filted leagues'));
- }
- const leagueIds = GLOBAL_DATA.filtedLeagues.join(',');
- let since = GLOBAL_DATA.specialFixturesVersion;
- if (GLOBAL_DATA.specialFixturesCount >= 18) {
- since = 0;
- GLOBAL_DATA.specialFixturesCount = 6;
- }
- if (since == 0) {
- Logs.outDev('full update special fixtures');
- }
- return pinnacleGet('/v2/fixtures/special', { sportId: 29, leagueIds, since })
- .then(data => {
- const { leagues, last } = data;
- if (!last) {
- return [];
- }
- GLOBAL_DATA.specialFixturesVersion = last;
- const specials = leagues?.map(league => {
- const { specials } = league;
- return specials?.filter(special => special.event)
- .map(special => {
- const { event: { id: eventId }, ...rest } = special ?? { event: {} };
- return { eventId, ...rest };
- }) ?? [];
- })
- .flat()
- .filter(special => {
- if (special.name != 'Winning Margin' && special.name != 'Exact Total Goals') {
- return false;
- }
- return true;
- }) ?? [];
- const update = since == 0 ? 'full' : 'increment';
- return { specials, update };
- });
- }
- const mergeContestants = (localContestants=[], remoteContestants=[]) => {
- const localContestantsMap = new Map(localContestants.map(contestant => [contestant.id, contestant]));
- remoteContestants.forEach(contestant => {
- const localContestant = localContestantsMap.get(contestant.id);
- if (localContestant) {
- Object.assign(localContestant, contestant);
- }
- else {
- localContestants.push(contestant);
- }
- });
- const remoteContestantsMap = new Map(remoteContestants.map(contestant => [contestant.id, contestant]));
- for (let i = localContestants.length - 1; i >= 0; i--) {
- if (!remoteContestantsMap.has(localContestants[i].id)) {
- localContestants.splice(i, 1);
- }
- }
- return localContestants;
- }
- const updateSpecialFixtures = async () => {
- return getSpecialFixtures()
- .then(data => {
- const { specials, update } = data;
- if (specials?.length) {
- const { gamesMap } = GLOBAL_DATA;
- const gamesSpecialsMap = {};
- specials.forEach(special => {
- const { eventId } = special;
- if (!gamesSpecialsMap[eventId]) {
- gamesSpecialsMap[eventId] = {};
- }
- if (special.name == 'Winning Margin') {
- gamesSpecialsMap[eventId].winningMargin = special;
- }
- else if (special.name == 'Exact Total Goals') {
- gamesSpecialsMap[eventId].exactTotalGoals = special;
- }
- });
- Object.keys(gamesSpecialsMap).forEach(eventId => {
- if (!gamesMap[eventId]) {
- return;
- }
- if (!gamesMap[eventId].specials) {
- gamesMap[eventId].specials = {};
- }
- const localSpecials = gamesMap[eventId].specials;
- const remoteSpecials = gamesSpecialsMap[eventId];
- if (localSpecials.winningMargin && remoteSpecials.winningMargin) {
- const { contestants: winningMarginContestants, ...winningMarginRest } = remoteSpecials.winningMargin;
- Object.assign(localSpecials.winningMargin, winningMarginRest);
- mergeContestants(localSpecials.winningMargin.contestants, winningMarginContestants);
- }
- else if (localSpecials.winningMargin && !remoteSpecials.winningMargin) {
- Logs.outDev('delete winningMargin', localSpecials.winningMargin);
- delete localSpecials.winningMargin;
- }
- else if (remoteSpecials.winningMargin) {
- localSpecials.winningMargin = remoteSpecials.winningMargin;
- }
- if (localSpecials.exactTotalGoals && remoteSpecials.exactTotalGoals) {
- const { contestants: exactTotalGoalsContestants, ...exactTotalGoalsRest } = remoteSpecials.exactTotalGoals;
- Object.assign(localSpecials.exactTotalGoals, exactTotalGoalsRest);
- mergeContestants(localSpecials.exactTotalGoals.contestants, exactTotalGoalsContestants);
- }
- else if (localSpecials.exactTotalGoals && !remoteSpecials.exactTotalGoals) {
- Logs.outDev('delete exactTotalGoals', localSpecials.exactTotalGoals);
- delete localSpecials.exactTotalGoals;
- }
- else if (remoteSpecials.exactTotalGoals) {
- localSpecials.exactTotalGoals = remoteSpecials.exactTotalGoals;
- }
- });
- }
- });
- }
- const getSpecialsOdds = async () => {
- if (!GLOBAL_DATA.filtedLeagues.length) {
- return Promise.reject(new Error('no filted leagues'));
- }
- const leagueIds = GLOBAL_DATA.filtedLeagues.join(',');
- const since = GLOBAL_DATA.specialsOddsVersion;
- // if (GLOBAL_DATA.specialsOddsCount >= 33) {
- // since = 0;
- // GLOBAL_DATA.specialsOddsCount = 9;
- // }
- if (since == 0) {
- Logs.outDev('full update specials odds');
- }
- return pinnacleGet('/v2/odds/special', { sportId: 29, oddsFormat: 'Decimal', leagueIds, since })
- .then(data => {
- const { leagues, last } = data;
- if (!last) {
- return [];
- }
- GLOBAL_DATA.specialsOddsVersion = last;
- return leagues?.flatMap(league => league.specials);
- });
- }
- const updateSpecialsOdds = async () => {
- return getSpecialsOdds()
- .then(specials => {
- if (specials.length) {
- const { gamesMap } = GLOBAL_DATA;
- const contestants = Object.values(gamesMap)
- .filter(game => game.specials)
- .map(game => {
- const { specials } = game;
- const contestants = Object.values(specials).map(special => {
- const { contestants } = special;
- return contestants.map(contestant => [contestant.id, contestant]);
- });
- return contestants;
- }).flat(2);
- const contestantsMap = new Map(contestants);
- const lines = specials.flatMap(special => special.contestantLines);
- lines.forEach(line => {
- const { id, handicap, lineId, max, price } = line;
- const contestant = contestantsMap.get(id);
- if (!contestant) {
- return;
- }
- contestant.handicap = handicap;
- contestant.lineId = lineId;
- contestant.max = max;
- contestant.price = price;
- });
- }
- });
- }
- const getInRunning = async () => {
- return pinnacleGet('/v2/inrunning')
- .then(data => {
- const sportId = 29;
- const leagues = data.sports?.find(sport => sport.id == sportId)?.leagues ?? [];
- return leagues.filter(league => {
- const { id } = league;
- const filtedLeaguesSet = new Set(GLOBAL_DATA.filtedLeagues);
- return filtedLeaguesSet.has(id);
- }).flatMap(league => league.events);
- });
- }
- const updateInRunning = async () => {
- return getInRunning()
- .then(games => {
- if (!games.length) {
- return;
- }
- const { gamesMap } = GLOBAL_DATA;
- games.forEach(game => {
- const { id, state, elapsed } = game;
- const localGame = gamesMap[id];
- if (localGame) {
- Object.assign(localGame, { state, elapsed });
- }
- });
- });
- }
- const ratioAccept = (ratio) => {
- if (ratio > 0) {
- return 'a';
- }
- return '';
- }
- const ratioString = (ratio) => {
- ratio = Math.abs(ratio);
- ratio = ratio.toString();
- ratio = ratio.replace(/\./, '');
- return ratio;
- }
- const parseSpreads = (spreads, wm) => {
- // 让分盘
- if (!spreads?.length) {
- return null;
- }
- const events = {};
- spreads.forEach(spread => {
- const { hdp, home, away } = spread;
- if (!(hdp % 1) || !!(hdp % 0.5)) {
- // 整数或不能被0.5整除的让分盘不处理
- return;
- }
- const ratio_ro = hdp;
- const ratio_r = ratio_ro - wm;
- events[`ior_r${ratioAccept(ratio_r)}h_${ratioString(ratio_r)}`] = {
- v: home,
- r: wm != 0 ? `ior_r${ratioAccept(ratio_ro)}h_${ratioString(ratio_ro)}` : undefined
- };
- events[`ior_r${ratioAccept(-ratio_r)}c_${ratioString(ratio_r)}`] = {
- v: away,
- r: wm != 0 ? `ior_r${ratioAccept(-ratio_ro)}c_${ratioString(ratio_ro)}` : undefined
- };
- });
- return events;
- }
- const parseMoneyline = (moneyline) => {
- // 胜平负
- if (!moneyline) {
- return null;
- }
- const { home, away, draw } = moneyline;
- return {
- 'ior_mh': { v: home },
- 'ior_mc': { v: away },
- 'ior_mn': { v: draw },
- }
- }
- const parseTotals = (totals) => {
- // 大小球盘
- if (!totals?.length) {
- return null;
- }
- const events = {};
- totals.forEach(total => {
- const { points, over, under } = total;
- events[`ior_ouc_${ratioString(points)}`] = { v: over };
- events[`ior_ouh_${ratioString(points)}`] = { v: under };
- });
- return events;
- }
- const parsePeriod = (period, wm) => {
- const { cutoff='', status=0, spreads=[], moneyline={}, totals=[] } = period;
- const cutoffTime = new Date(cutoff).getTime();
- const nowTime = Date.now();
- if (status != 1 || cutoffTime < nowTime) {
- return null;
- }
- const events = {};
- Object.assign(events, parseSpreads(spreads, wm));
- Object.assign(events, parseMoneyline(moneyline));
- Object.assign(events, parseTotals(totals));
- return events;
- }
- const parseWinningMargin = (winningMargin, home, away) => {
- const { cutoff='', status='', contestants=[] } = winningMargin;
- const cutoffTime = new Date(cutoff).getTime();
- const nowTime = Date.now();
- if (status != 'O' || cutoffTime < nowTime || !contestants?.length) {
- return null;
- }
- const events = {};
- contestants.forEach(contestant => {
- const { name, price } = contestant;
- const nr = name.match(/\d+$/)?.[0];
- if (!nr) {
- return;
- }
- let side;
- if (name.startsWith(home)) {
- side = 'h';
- }
- else if (name.startsWith(away)) {
- side = 'c';
- }
- else {
- return;
- }
- events[`ior_wm${side}_${nr}`] = { v: price };
- });
- return events;
- }
- const parseExactTotalGoals = (exactTotalGoals) => {
- const { cutoff='', status='', contestants=[] } = exactTotalGoals;
- const cutoffTime = new Date(cutoff).getTime();
- const nowTime = Date.now();
- if (status != 'O' || cutoffTime < nowTime || !contestants?.length) {
- return null;
- }
- const events = {};
- contestants.forEach(contestant => {
- const { name, price } = contestant;
- if (+name >= 1 && +name <= 7) {
- events[`ior_ot_${name}`] = { v: price };
- }
- });
- return events;
- }
- const parseRbState = (state) => {
- let stage = null;
- if (state == 1) {
- stage = '1H';
- }
- else if (state == 2) {
- stage = 'HT';
- }
- else if (state == 3) {
- stage = '2H';
- }
- return stage;
- }
- const parseGame = (game) => {
- const { eventId=0, originId=0, period={}, specials={}, home, away, marketType, state, elapsed, homeScore=0, awayScore=0 } = game;
- const { winningMargin={}, exactTotalGoals={} } = specials;
- const wm = homeScore - awayScore;
- const score = `${homeScore}-${awayScore}`;
- const events = parsePeriod(period, wm) ?? {};
- const stage = parseRbState(state);
- const retime = elapsed ? `${elapsed}'` : '';
- const evtime = Date.now();
- Object.assign(events, parseWinningMargin(winningMargin, home, away));
- Object.assign(events, parseExactTotalGoals(exactTotalGoals));
- return { eventId, originId, events, evtime, stage, retime, score, wm, marketType };
- }
- const getGames = () => {
- const { filtedGames, gamesMap } = GLOBAL_DATA;
- const filtedGamesSet = new Set(filtedGames);
- const nowTime = Date.now();
- const gamesData = {};
- Object.values(gamesMap).forEach(game => {
- const { id, liveStatus, parentId, resultingUnit, timestamp } = game;
- if (resultingUnit !== 'Regular') {
- return false; // 非常规赛事不处理
- }
- const gmtMinus4Date = getDateInTimezone(-4);
- const todayEndTime = new Date(`${gmtMinus4Date} 23:59:59 GMT-4`).getTime();
- const tomorrowEndTime = todayEndTime + 24 * 60 * 60 * 1000;
- if (liveStatus == 1 && timestamp < nowTime) {
- game.marketType = 2; // 滚球赛事
- }
- else if (liveStatus != 1 && timestamp > nowTime && timestamp <= todayEndTime) {
- game.marketType = 1; // 今日赛事
- }
- else if (liveStatus != 1 && timestamp > todayEndTime && timestamp <= tomorrowEndTime) {
- game.marketType = 0; // 明日早盘赛事
- }
- else {
- game.marketType = -1; // 非近期赛事
- }
- if (game.marketType < 0) {
- return false; // 非近期赛事不处理
- }
- let actived = false;
- if (liveStatus != 1 && filtedGamesSet.has(id)) {
- actived = true; // 在赛前列表中
- game.eventId = id;
- game.originId = 0;
- }
- else if (liveStatus == 1 && filtedGamesSet.has(parentId)) {
- actived = true; // 在滚球列表中
- game.eventId = parentId;
- game.originId = id;
- }
- if (actived) {
- const gameInfo = parseGame(game);
- const { marketType, ...rest } = gameInfo;
- if (!gamesData[marketType]) {
- gamesData[marketType] = [];
- }
- gamesData[marketType].push(rest);
- }
- });
- return gamesData;
- }
- const pinnacleDataLoop = () => {
- updateStraightFixtures()
- .then(() => {
- return Promise.all([
- updateStraightOdds(),
- updateSpecialFixtures(),
- updateInRunning(),
- ]);
- })
- .then(() => {
- return updateSpecialsOdds();
- })
- .then(() => {
- if (!GLOBAL_DATA.loopActive) {
- GLOBAL_DATA.loopActive = true;
- Logs.out('loop active');
- notifyException('Pinnacle API startup.');
- }
- if (GLOBAL_DATA.requestErrorCount > 0) {
- GLOBAL_DATA.requestErrorCount = 0;
- Logs.out('request error count reset');
- }
- const nowTime = Date.now();
- const loopDuration = nowTime - GLOBAL_DATA.loopResultTime;
- GLOBAL_DATA.loopResultTime = nowTime;
- if (loopDuration > 15000) {
- Logs.out('loop duration is too long', loopDuration);
- }
- else {
- Logs.outDev('loop duration', loopDuration);
- }
- const { straightFixturesVersion: sfv, specialFixturesVersion: pfv, straightOddsVersion: sov, specialsOddsVersion: pov } = GLOBAL_DATA;
- const timestamp = Math.max(sfv, pfv, sov, pov);
- const games = getGames();
- const data = { games, timestamp };
- updateBaseEvents(data);
- setData(gamesMapCacheFile, GLOBAL_DATA.gamesMap)
- .then(() => {
- Logs.outDev('games map saved');
- })
- .catch(err => {
- Logs.err('failed to save games map', err.message);
- });
- })
- .catch(err => {
- Logs.err(err.message, err.source);
- GLOBAL_DATA.requestErrorCount++;
- if (GLOBAL_DATA.loopActive && GLOBAL_DATA.requestErrorCount > 5) {
- const exceptionMessage = 'request errors have reached the limit';
- Logs.out(exceptionMessage);
- GLOBAL_DATA.loopActive = false;
- Logs.out('loop inactive');
- notifyException(`Pinnacle API paused. ${exceptionMessage}. ${err.message}`);
- }
- })
- .finally(() => {
- const { loopActive } = GLOBAL_DATA;
- let loopDelay = 1000 * 5;
- if (!loopActive) {
- loopDelay = 1000 * 60;
- resetVersionsCount();
- }
- else {
- incrementVersionsCount();
- }
- setTimeout(pinnacleDataLoop, loopDelay);
- });
- }
- /**
- * 缓存GLOBAL_DATA数据到文件
- */
- const saveGlobalDataToCache = async () => {
- return setData(globalDataCacheFile, GLOBAL_DATA);
- }
- const loadGlobalDataFromCache = async () => {
- return getData(globalDataCacheFile)
- .then(data => {
- if (!data) {
- return;
- }
- Object.keys(GLOBAL_DATA).forEach(key => {
- if (key in data) {
- GLOBAL_DATA[key] = data[key];
- }
- });
- });
- }
- // 监听进程退出事件,保存GLOBAL_DATA数据
- const saveExit = (code) => {
- saveGlobalDataToCache()
- .then(() => {
- Logs.out('global data saved');
- })
- .catch(err => {
- Logs.err('failed to save global data', err.message);
- })
- .finally(() => {
- process.exit(code);
- });
- }
- process.on('SIGINT', () => {
- saveExit(0);
- });
- process.on('SIGTERM', () => {
- saveExit(0);
- });
- process.on('SIGUSR2', () => {
- saveExit(0);
- });
- (() => {
- if (!process.env.PINNACLE_USERNAME || !process.env.PINNACLE_PASSWORD) {
- Logs.err('USERNAME or PASSWORD is not set');
- return;
- }
- loadGlobalDataFromCache()
- .then(() => {
- Logs.out('global data loaded');
- })
- .catch(err => {
- Logs.err('failed to load global data', err.message);
- })
- .finally(() => {
- GLOBAL_DATA.loopResultTime = Date.now();
- GLOBAL_DATA.loopActive = true;
- return getFiltedGames();
- })
- .then(pinnacleDataLoop);
- })();
|