@gotza02/sequential-thinking 10000.1.0 → 10000.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dashboard/server.d.ts +0 -34
- package/dist/dashboard/server.js +50 -269
- package/dist/tools/sports/core/alert-manager.d.ts +1 -50
- package/dist/tools/sports/core/alert-manager.js +62 -123
- package/dist/tools/sports/core/data-quality.d.ts +0 -44
- package/dist/tools/sports/core/data-quality.js +49 -266
- package/dist/tools/sports/core/historical-analyzer.d.ts +0 -54
- package/dist/tools/sports/core/historical-analyzer.js +56 -256
- package/dist/tools/sports/core/index.d.ts +1 -1
- package/dist/tools/sports/core/index.js +1 -1
- package/dist/tools/sports/core/ml-prediction.d.ts +7 -65
- package/dist/tools/sports/core/ml-prediction.js +43 -185
- package/dist/tools/sports/core/realtime-manager.d.ts +1 -52
- package/dist/tools/sports/core/realtime-manager.js +18 -127
- package/package.json +1 -1
|
@@ -1,132 +1,77 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* REALTIME DATA MANAGER
|
|
3
|
-
*
|
|
3
|
+
* Polling-based real-time data streaming
|
|
4
4
|
*/
|
|
5
5
|
import { EventEmitter } from 'events';
|
|
6
6
|
import { CacheService } from './cache.js';
|
|
7
|
-
import { logger } from '../../../utils.js';
|
|
8
7
|
import { REALTIME_CONFIG, CACHE_CONFIG } from './constants.js';
|
|
8
|
+
// Simple logger fallback
|
|
9
|
+
const logger = {
|
|
10
|
+
info: (...args) => console.error('[INFO]', ...args),
|
|
11
|
+
warn: (...args) => console.warn('[WARN]', ...args),
|
|
12
|
+
error: (...args) => console.error('[ERROR]', ...args),
|
|
13
|
+
debug: (...args) => { }
|
|
14
|
+
};
|
|
9
15
|
export class RealtimeDataManager extends EventEmitter {
|
|
10
16
|
cache;
|
|
11
|
-
wsConnections = new Map();
|
|
12
17
|
pollingIntervals = new Map();
|
|
13
|
-
reconnectAttempts = new Map();
|
|
14
18
|
isRunning = false;
|
|
15
19
|
constructor() {
|
|
16
20
|
super();
|
|
17
21
|
this.cache = new CacheService();
|
|
18
22
|
this.setMaxListeners(100);
|
|
19
23
|
}
|
|
20
|
-
/**
|
|
21
|
-
* Start the realtime manager
|
|
22
|
-
*/
|
|
23
24
|
async start() {
|
|
24
25
|
if (this.isRunning)
|
|
25
26
|
return;
|
|
26
27
|
this.isRunning = true;
|
|
27
28
|
logger.info('[RealtimeManager] Started');
|
|
28
|
-
// Start polling for live data
|
|
29
29
|
this.startLiveScoresPolling();
|
|
30
30
|
this.startOddsPolling();
|
|
31
31
|
}
|
|
32
|
-
/**
|
|
33
|
-
* Stop the realtime manager
|
|
34
|
-
*/
|
|
35
32
|
stop() {
|
|
36
33
|
this.isRunning = false;
|
|
37
|
-
// Close all WebSocket connections
|
|
38
|
-
for (const [matchId, ws] of this.wsConnections.entries()) {
|
|
39
|
-
ws.close();
|
|
40
|
-
}
|
|
41
|
-
this.wsConnections.clear();
|
|
42
|
-
// Clear all polling intervals
|
|
43
34
|
for (const [key, interval] of this.pollingIntervals.entries()) {
|
|
44
35
|
clearInterval(interval);
|
|
45
36
|
}
|
|
46
37
|
this.pollingIntervals.clear();
|
|
47
38
|
logger.info('[RealtimeManager] Stopped');
|
|
48
39
|
}
|
|
49
|
-
/**
|
|
50
|
-
* Subscribe to a specific match's events
|
|
51
|
-
*/
|
|
52
40
|
subscribeToMatch(matchId, callback) {
|
|
53
41
|
const eventName = `match:${matchId}`;
|
|
54
42
|
this.on(eventName, callback);
|
|
55
|
-
|
|
56
|
-
this.connectToMatchStream(matchId);
|
|
57
|
-
// Return unsubscribe function
|
|
43
|
+
this.startMatchEventsPolling(matchId);
|
|
58
44
|
return () => {
|
|
59
45
|
this.off(eventName, callback);
|
|
60
46
|
this.unsubscribeFromMatch(matchId);
|
|
61
47
|
};
|
|
62
48
|
}
|
|
63
|
-
/**
|
|
64
|
-
* Subscribe to odds changes for a match
|
|
65
|
-
*/
|
|
66
49
|
subscribeToOdds(matchId, callback) {
|
|
67
50
|
const eventName = `odds:${matchId}`;
|
|
68
51
|
this.on(eventName, callback);
|
|
69
|
-
return () =>
|
|
70
|
-
this.off(eventName, callback);
|
|
71
|
-
};
|
|
52
|
+
return () => this.off(eventName, callback);
|
|
72
53
|
}
|
|
73
|
-
/**
|
|
74
|
-
* Subscribe to all live events
|
|
75
|
-
*/
|
|
76
54
|
subscribeToAllEvents(callback) {
|
|
77
55
|
this.on('event', callback);
|
|
78
56
|
return () => this.off('event', callback);
|
|
79
57
|
}
|
|
80
|
-
/**
|
|
81
|
-
* Process and broadcast an event
|
|
82
|
-
*/
|
|
83
58
|
processEvent(event) {
|
|
84
|
-
// Update cache
|
|
85
59
|
this.updateCache(event);
|
|
86
|
-
// Emit to specific match listeners
|
|
87
60
|
this.emit(`match:${event.matchId}`, event);
|
|
88
|
-
// Emit to odds listeners if it's an odds change
|
|
89
61
|
if (event.type === 'odds_change') {
|
|
90
62
|
this.emit(`odds:${event.matchId}`, event);
|
|
91
63
|
}
|
|
92
|
-
// Emit to global listeners
|
|
93
64
|
this.emit('event', event);
|
|
94
|
-
// Log event
|
|
95
|
-
logger.debug(`[Realtime] Event: ${event.type} | Match: ${event.matchId} | Min: ${event.minute}`);
|
|
96
65
|
}
|
|
97
|
-
/**
|
|
98
|
-
* Get current live matches
|
|
99
|
-
*/
|
|
100
66
|
getLiveMatches() {
|
|
101
67
|
const cached = this.cache.get('live:all');
|
|
102
68
|
return cached || [];
|
|
103
69
|
}
|
|
104
|
-
/**
|
|
105
|
-
* Get match events history
|
|
106
|
-
*/
|
|
107
70
|
getMatchEvents(matchId) {
|
|
108
71
|
const cached = this.cache.get(`events:${matchId}`);
|
|
109
72
|
return cached || [];
|
|
110
73
|
}
|
|
111
|
-
/**
|
|
112
|
-
* Connect to match stream (WebSocket or polling)
|
|
113
|
-
*/
|
|
114
|
-
async connectToMatchStream(matchId) {
|
|
115
|
-
// For now, use polling fallback
|
|
116
|
-
// In production, this would try WebSocket first
|
|
117
|
-
this.startMatchEventsPolling(matchId);
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Unsubscribe from a match
|
|
121
|
-
*/
|
|
122
74
|
unsubscribeFromMatch(matchId) {
|
|
123
|
-
// Close WebSocket if exists
|
|
124
|
-
const ws = this.wsConnections.get(matchId);
|
|
125
|
-
if (ws) {
|
|
126
|
-
ws.close();
|
|
127
|
-
this.wsConnections.delete(matchId);
|
|
128
|
-
}
|
|
129
|
-
// Clear polling if no more listeners
|
|
130
75
|
const listenerCount = this.listenerCount(`match:${matchId}`);
|
|
131
76
|
if (listenerCount === 0) {
|
|
132
77
|
const interval = this.pollingIntervals.get(`events:${matchId}`);
|
|
@@ -136,22 +81,14 @@ export class RealtimeDataManager extends EventEmitter {
|
|
|
136
81
|
}
|
|
137
82
|
}
|
|
138
83
|
}
|
|
139
|
-
/**
|
|
140
|
-
* Start polling for live scores
|
|
141
|
-
*/
|
|
142
84
|
startLiveScoresPolling() {
|
|
143
85
|
const interval = setInterval(async () => {
|
|
144
86
|
if (!this.isRunning)
|
|
145
87
|
return;
|
|
146
88
|
try {
|
|
147
|
-
// Fetch live scores from API
|
|
148
|
-
// This would call your API provider
|
|
149
89
|
const liveScores = await this.fetchLiveScores();
|
|
150
|
-
// Update cache
|
|
151
90
|
this.cache.set('live:all', liveScores, CACHE_CONFIG.TTL.LIVE_SCORES);
|
|
152
|
-
// Emit update
|
|
153
91
|
this.emit('live_scores', liveScores);
|
|
154
|
-
// Check for score changes and emit events
|
|
155
92
|
this.detectScoreChanges(liveScores);
|
|
156
93
|
}
|
|
157
94
|
catch (error) {
|
|
@@ -160,9 +97,6 @@ export class RealtimeDataManager extends EventEmitter {
|
|
|
160
97
|
}, REALTIME_CONFIG.POLLING_INTERVALS.LIVE_SCORES);
|
|
161
98
|
this.pollingIntervals.set('live_scores', interval);
|
|
162
99
|
}
|
|
163
|
-
/**
|
|
164
|
-
* Start polling for odds
|
|
165
|
-
*/
|
|
166
100
|
startOddsPolling() {
|
|
167
101
|
const interval = setInterval(async () => {
|
|
168
102
|
if (!this.isRunning)
|
|
@@ -174,7 +108,6 @@ export class RealtimeDataManager extends EventEmitter {
|
|
|
174
108
|
if (odds) {
|
|
175
109
|
const cachedOdds = this.cache.get(`odds:${match.id}`);
|
|
176
110
|
if (cachedOdds) {
|
|
177
|
-
// Detect changes
|
|
178
111
|
this.detectOddsChanges(match.id, cachedOdds, odds);
|
|
179
112
|
}
|
|
180
113
|
this.cache.set(`odds:${match.id}`, odds, CACHE_CONFIG.TTL.ODDS);
|
|
@@ -187,9 +120,6 @@ export class RealtimeDataManager extends EventEmitter {
|
|
|
187
120
|
}, REALTIME_CONFIG.POLLING_INTERVALS.ODDS);
|
|
188
121
|
this.pollingIntervals.set('odds', interval);
|
|
189
122
|
}
|
|
190
|
-
/**
|
|
191
|
-
* Start polling for specific match events
|
|
192
|
-
*/
|
|
193
123
|
startMatchEventsPolling(matchId) {
|
|
194
124
|
if (this.pollingIntervals.has(`events:${matchId}`))
|
|
195
125
|
return;
|
|
@@ -199,7 +129,6 @@ export class RealtimeDataManager extends EventEmitter {
|
|
|
199
129
|
try {
|
|
200
130
|
const events = await this.fetchMatchEvents(matchId);
|
|
201
131
|
const cachedEvents = this.getMatchEvents(matchId);
|
|
202
|
-
// Find new events
|
|
203
132
|
const newEvents = events.filter(e => !cachedEvents.some(ce => ce.timestamp === e.timestamp && ce.type === e.type));
|
|
204
133
|
for (const event of newEvents) {
|
|
205
134
|
this.processEvent(event);
|
|
@@ -211,49 +140,32 @@ export class RealtimeDataManager extends EventEmitter {
|
|
|
211
140
|
}, REALTIME_CONFIG.POLLING_INTERVALS.MATCH_EVENTS);
|
|
212
141
|
this.pollingIntervals.set(`events:${matchId}`, interval);
|
|
213
142
|
}
|
|
214
|
-
/**
|
|
215
|
-
* Detect score changes and emit goal events
|
|
216
|
-
*/
|
|
217
143
|
detectScoreChanges(matches) {
|
|
218
144
|
for (const match of matches) {
|
|
219
145
|
const cachedMatch = this.cache.get(`match:${match.id}`);
|
|
220
146
|
if (cachedMatch && cachedMatch.score && match.score) {
|
|
221
|
-
// Check for home team goal
|
|
222
147
|
if (match.score.home > cachedMatch.score.home) {
|
|
223
148
|
this.processEvent({
|
|
224
149
|
type: 'goal',
|
|
225
150
|
matchId: match.id,
|
|
226
151
|
timestamp: Date.now(),
|
|
227
152
|
minute: match.minute,
|
|
228
|
-
data: {
|
|
229
|
-
team: 'home',
|
|
230
|
-
score: match.score,
|
|
231
|
-
previousScore: cachedMatch.score,
|
|
232
|
-
},
|
|
153
|
+
data: { team: 'home', score: match.score, previousScore: cachedMatch.score },
|
|
233
154
|
});
|
|
234
155
|
}
|
|
235
|
-
// Check for away team goal
|
|
236
156
|
if (match.score.away > cachedMatch.score.away) {
|
|
237
157
|
this.processEvent({
|
|
238
158
|
type: 'goal',
|
|
239
159
|
matchId: match.id,
|
|
240
160
|
timestamp: Date.now(),
|
|
241
161
|
minute: match.minute,
|
|
242
|
-
data: {
|
|
243
|
-
team: 'away',
|
|
244
|
-
score: match.score,
|
|
245
|
-
previousScore: cachedMatch.score,
|
|
246
|
-
},
|
|
162
|
+
data: { team: 'away', score: match.score, previousScore: cachedMatch.score },
|
|
247
163
|
});
|
|
248
164
|
}
|
|
249
165
|
}
|
|
250
|
-
// Update match cache
|
|
251
166
|
this.cache.set(`match:${match.id}`, match, CACHE_CONFIG.TTL.MATCH_DETAILS);
|
|
252
167
|
}
|
|
253
168
|
}
|
|
254
|
-
/**
|
|
255
|
-
* Detect odds changes and emit events
|
|
256
|
-
*/
|
|
257
169
|
detectOddsChanges(matchId, oldOdds, newOdds) {
|
|
258
170
|
const markets = [
|
|
259
171
|
{ key: 'homeWin', name: 'Home Win' },
|
|
@@ -265,7 +177,6 @@ export class RealtimeDataManager extends EventEmitter {
|
|
|
265
177
|
const newValue = newOdds[market.key];
|
|
266
178
|
if (oldValue && newValue && oldValue !== newValue) {
|
|
267
179
|
const change = ((newValue - oldValue) / oldValue) * 100;
|
|
268
|
-
// Only emit if change is significant (>5%)
|
|
269
180
|
if (Math.abs(change) > 5) {
|
|
270
181
|
this.processEvent({
|
|
271
182
|
type: 'odds_change',
|
|
@@ -285,47 +196,27 @@ export class RealtimeDataManager extends EventEmitter {
|
|
|
285
196
|
}
|
|
286
197
|
}
|
|
287
198
|
}
|
|
288
|
-
/**
|
|
289
|
-
* Update cache with event
|
|
290
|
-
*/
|
|
291
199
|
updateCache(event) {
|
|
292
|
-
// Invalidate related cache entries
|
|
293
200
|
this.cache.invalidatePattern(`match:${event.matchId}*`);
|
|
294
|
-
// Store event in history
|
|
295
201
|
const eventsKey = `events:${event.matchId}`;
|
|
296
202
|
const existingEvents = this.cache.get(eventsKey) || [];
|
|
297
203
|
existingEvents.push(event);
|
|
298
|
-
|
|
299
|
-
if (existingEvents.length > 100) {
|
|
204
|
+
if (existingEvents.length > 100)
|
|
300
205
|
existingEvents.shift();
|
|
301
|
-
}
|
|
302
206
|
this.cache.set(eventsKey, existingEvents, CACHE_CONFIG.TTL.LIVE_EVENTS);
|
|
303
207
|
}
|
|
304
|
-
|
|
305
|
-
async
|
|
306
|
-
|
|
307
|
-
return [];
|
|
308
|
-
}
|
|
309
|
-
async fetchOdds(matchId) {
|
|
310
|
-
// Implement with your API provider
|
|
311
|
-
return null;
|
|
312
|
-
}
|
|
313
|
-
async fetchMatchEvents(matchId) {
|
|
314
|
-
// Implement with your API provider
|
|
315
|
-
return [];
|
|
316
|
-
}
|
|
208
|
+
async fetchLiveScores() { return []; }
|
|
209
|
+
async fetchOdds(matchId) { return null; }
|
|
210
|
+
async fetchMatchEvents(matchId) { return []; }
|
|
317
211
|
}
|
|
318
|
-
// Singleton instance
|
|
319
212
|
let globalRealtimeManager = null;
|
|
320
213
|
export function getRealtimeManager() {
|
|
321
|
-
if (!globalRealtimeManager)
|
|
214
|
+
if (!globalRealtimeManager)
|
|
322
215
|
globalRealtimeManager = new RealtimeDataManager();
|
|
323
|
-
}
|
|
324
216
|
return globalRealtimeManager;
|
|
325
217
|
}
|
|
326
218
|
export function resetRealtimeManager() {
|
|
327
|
-
if (globalRealtimeManager)
|
|
219
|
+
if (globalRealtimeManager)
|
|
328
220
|
globalRealtimeManager.stop();
|
|
329
|
-
}
|
|
330
221
|
globalRealtimeManager = null;
|
|
331
222
|
}
|