bulltrackers-module 1.0.351 → 1.0.352
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.
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Logic for fetching and storing Popular Investor rankings.
|
|
3
|
+
* Integrates IntelligentProxyManager for IP rotation and IntelligentHeaderManager for
|
|
4
|
+
* user-agent/header rotation and performance tracking.
|
|
5
|
+
*/
|
|
6
|
+
const { IntelligentProxyManager } = require('../../core/utils/intelligent_proxy_manager');
|
|
7
|
+
const { IntelligentHeaderManager } = require('../../core/utils/intelligent_header_manager');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Fetches the top Popular Investors and stores the raw result in Firestore.
|
|
11
|
+
* @param {object} dependencies - Contains db, logger.
|
|
12
|
+
* @param {object} config - Configuration object.
|
|
13
|
+
* @param {string} config.rankingsApiUrl - The eToro Rankings API URL.
|
|
14
|
+
* @param {string} config.rankingsCollectionName - e.g., 'popular_investor_rankings'.
|
|
15
|
+
* @param {object} config.proxyConfig - Configuration for the IntelligentProxyManager.
|
|
16
|
+
* @param {object} config.headerConfig - Configuration for the IntelligentHeaderManager.
|
|
17
|
+
*/
|
|
18
|
+
async function fetchAndStorePopularInvestors(dependencies, config) {
|
|
19
|
+
const { db, logger } = dependencies;
|
|
20
|
+
const { rankingsApiUrl, rankingsCollectionName, proxyConfig, headerConfig } = config;
|
|
21
|
+
|
|
22
|
+
if (!rankingsApiUrl || !rankingsCollectionName || !proxyConfig || !headerConfig) {
|
|
23
|
+
throw new Error("[PopularInvestorFetch] Missing required config (rankingsApiUrl, rankingsCollectionName, proxyConfig, headerConfig).");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const today = new Date().toISOString().split('T')[0]; // YYYY-MM-DD
|
|
27
|
+
logger.log('INFO', `[PopularInvestorFetch] Starting fetch for date: ${today}`);
|
|
28
|
+
|
|
29
|
+
// 1. Initialize Managers
|
|
30
|
+
const headerManager = new IntelligentHeaderManager(db, logger, headerConfig);
|
|
31
|
+
const proxyManager = new IntelligentProxyManager(db, logger, proxyConfig);
|
|
32
|
+
|
|
33
|
+
let data = null;
|
|
34
|
+
let fetchSuccess = false;
|
|
35
|
+
let selectedHeaderId = 'fallback';
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
// 2. Select the best performing header
|
|
39
|
+
const { id, header } = await headerManager.selectHeader();
|
|
40
|
+
selectedHeaderId = id;
|
|
41
|
+
|
|
42
|
+
logger.log('INFO', `[PopularInvestorFetch] Selected header configuration: ${id}`);
|
|
43
|
+
|
|
44
|
+
// Merge selected headers with specific API requirements
|
|
45
|
+
// We prioritize the dynamic headers but ensure critical fields like Accept exist
|
|
46
|
+
const requestHeaders = {
|
|
47
|
+
'Accept': 'application/json',
|
|
48
|
+
'Referer': 'https://www.etoro.com/',
|
|
49
|
+
...header
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// 3. Attempt Fetch via Proxy Manager
|
|
53
|
+
try {
|
|
54
|
+
logger.log('INFO', '[PopularInvestorFetch] Attempting fetch via Proxy Manager...');
|
|
55
|
+
|
|
56
|
+
const response = await proxyManager.fetch(rankingsApiUrl, {
|
|
57
|
+
method: 'GET',
|
|
58
|
+
headers: requestHeaders
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
if (response.ok) {
|
|
62
|
+
data = await response.json();
|
|
63
|
+
fetchSuccess = true;
|
|
64
|
+
logger.log('SUCCESS', '[PopularInvestorFetch] Successfully fetched data via Proxy.');
|
|
65
|
+
} else {
|
|
66
|
+
logger.log('WARN', `[PopularInvestorFetch] Proxy fetch failed with status ${response.status}.`);
|
|
67
|
+
}
|
|
68
|
+
} catch (proxyError) {
|
|
69
|
+
logger.log('ERROR', '[PopularInvestorFetch] Error during Proxy Manager execution.', { errorMessage: proxyError.message });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 4. Fallback: Direct Fetch (if Proxy failed)
|
|
73
|
+
if (!data) {
|
|
74
|
+
logger.log('WARN', '[PopularInvestorFetch] Falling back to Direct Node Fetch...');
|
|
75
|
+
try {
|
|
76
|
+
const directResponse = await fetch(rankingsApiUrl, {
|
|
77
|
+
method: 'GET',
|
|
78
|
+
headers: requestHeaders
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
if (directResponse.ok) {
|
|
82
|
+
data = await directResponse.json();
|
|
83
|
+
fetchSuccess = true;
|
|
84
|
+
logger.log('SUCCESS', '[PopularInvestorFetch] Successfully fetched data via Direct Fetch.');
|
|
85
|
+
} else {
|
|
86
|
+
throw new Error(`Direct fetch failed with status: ${directResponse.status}`);
|
|
87
|
+
}
|
|
88
|
+
} catch (directError) {
|
|
89
|
+
logger.log('ERROR', '[PopularInvestorFetch] Direct fetch failed.', { errorMessage: directError.message });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
} catch (error) {
|
|
94
|
+
logger.log('ERROR', '[PopularInvestorFetch] Critical error in fetch orchestration.', { errorMessage: error.message });
|
|
95
|
+
} finally {
|
|
96
|
+
// 5. Update Header Performance (Reinforcement Learning)
|
|
97
|
+
// This ensures the system "learns" which headers are getting blocked vs accepted
|
|
98
|
+
logger.log('INFO', `[PopularInvestorFetch] Updating header performance for ${selectedHeaderId}: Success=${fetchSuccess}`);
|
|
99
|
+
headerManager.updatePerformance(selectedHeaderId, fetchSuccess);
|
|
100
|
+
await headerManager.flushPerformanceUpdates();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// 6. Final Validation & Storage
|
|
104
|
+
if (data && data.Items && Array.isArray(data.Items)) {
|
|
105
|
+
try {
|
|
106
|
+
const docRef = db.collection(rankingsCollectionName).doc(today);
|
|
107
|
+
|
|
108
|
+
await docRef.set({
|
|
109
|
+
fetchedAt: new Date(),
|
|
110
|
+
totalRows: data.TotalRows,
|
|
111
|
+
itemsCount: data.Items.length,
|
|
112
|
+
...data
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
logger.log('SUCCESS', `[PopularInvestorFetch] Stored ${data.TotalRows} rankings into ${rankingsCollectionName}/${today}`);
|
|
116
|
+
return { success: true, count: data.TotalRows };
|
|
117
|
+
|
|
118
|
+
} catch (dbError) {
|
|
119
|
+
logger.log('ERROR', '[PopularInvestorFetch] Failed to write to Firestore.', { errorMessage: dbError.message });
|
|
120
|
+
throw dbError;
|
|
121
|
+
}
|
|
122
|
+
} else {
|
|
123
|
+
const errorMsg = fetchSuccess
|
|
124
|
+
? '[PopularInvestorFetch] Fetched data format is invalid (missing Items array).'
|
|
125
|
+
: '[PopularInvestorFetch] Failed to fetch data from all sources.';
|
|
126
|
+
|
|
127
|
+
logger.log('ERROR', errorMsg);
|
|
128
|
+
throw new Error(errorMsg);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
module.exports = { fetchAndStorePopularInvestors };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bulltrackers-module",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.352",
|
|
4
4
|
"description": "Helper Functions for Bulltrackers.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"functions/social-orchestrator/",
|
|
20
20
|
"functions/social-task-handler/",
|
|
21
21
|
"functions/price-backfill/",
|
|
22
|
-
"functions/root-data-indexer/"
|
|
22
|
+
"functions/root-data-indexer/",
|
|
23
|
+
"functions/fetch-popular-investors"
|
|
23
24
|
],
|
|
24
25
|
"keywords": [
|
|
25
26
|
"bulltrackers",
|