bulltrackers-module 1.0.170 → 1.0.172

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.
@@ -15,87 +15,40 @@ async function lookupUsernames(cids, { logger, headerManager, proxyManager }, co
15
15
  if (!cids?.length) return [];
16
16
  logger.log('INFO', `[lookupUsernames] Looking up usernames for ${cids.length} CIDs.`);
17
17
 
18
- // --- REFACTOR 1: REMOVE CONCURRENCY ---
18
+ // --- Set concurrency to 1 because appscript gets really fucked up with undocumented rate limits if we try spam it concurrently, a shame but that's life. DO NOT CHANGE THIS
19
19
  const limit = pLimit(1);
20
- // --- END REFACTOR 1 ---
21
-
22
20
  const { USERNAME_LOOKUP_BATCH_SIZE, ETORO_API_RANKINGS_URL } = config;
23
-
24
21
  const batches = [];
25
- for (let i = 0; i < cids.length; i += USERNAME_LOOKUP_BATCH_SIZE) {
26
- batches.push(cids.slice(i, i + USERNAME_LOOKUP_BATCH_SIZE).map(Number));
27
- }
28
-
29
- const batchPromises = batches.map((batch, index) => limit(async () => {
30
- const batchId = `batch-${index + 1}`;
22
+ for (let i = 0; i < cids.length; i += USERNAME_LOOKUP_BATCH_SIZE) { batches.push(cids.slice(i, i + USERNAME_LOOKUP_BATCH_SIZE).map(Number)); }
23
+ const batchPromises = batches.map((batch, index) => limit(async () => { const batchId = `batch-${index + 1}`;
31
24
  logger.log('INFO', `[lookupUsernames/${batchId}] Processing batch of ${batch.length} CIDs...`);
32
-
33
25
  const header = await headerManager.selectHeader();
34
- if (!header) {
35
- logger.log('ERROR', `[lookupUsernames/${batchId}] Could not select a header.`);
36
- return null;
37
- }
38
-
26
+ if (!header) { logger.log('ERROR', `[lookupUsernames/${batchId}] Could not select a header.`); return null; }
39
27
  let wasSuccess = false;
40
28
  let proxyUsed = true;
41
29
  let response;
42
30
  const url = `${ETORO_API_RANKINGS_URL}?Period=LastTwoYears`;
43
31
  const options = { method: 'POST', headers: { ...header.header, 'Content-Type': 'application/json' }, body: JSON.stringify(batch) };
44
-
45
32
  try {
46
- // --- REFACTOR 3: ADD FALLBACK ---
47
33
  logger.log('TRACE', `[lookupUsernames/${batchId}] Attempting fetch via AppScript proxy...`);
48
34
  response = await proxyManager.fetch(url, options);
49
35
  if (!response.ok) throw new Error(`AppScript proxy failed with status ${response.status}`);
50
-
51
- wasSuccess = true;
36
+ wasSuccess = true; // Yay we win
52
37
  logger.log('INFO', `[lookupUsernames/${batchId}] AppScript proxy fetch successful.`);
53
-
54
- } catch (proxyError) {
55
- logger.log('WARN', `[lookupUsernames/${batchId}] AppScript proxy fetch FAILED. Error: ${proxyError.message}. Attempting direct node-fetch fallback.`, {
56
- error: proxyError.message,
57
- source: 'AppScript'
58
- });
59
-
38
+ } catch (proxyError) { logger.log('WARN', `[lookupUsernames/${batchId}] AppScript proxy fetch FAILED. Error: ${proxyError.message}. Attempting direct node-fetch fallback.`, { error: proxyError.message, source: 'AppScript' }); // SHIT we failed...
60
39
  proxyUsed = false; // Don't penalize header for proxy failure
61
-
62
- try {
63
- response = await fetch(url, options); // Direct node-fetch
64
- if (!response.ok) {
65
- const errorText = await response.text();
66
- throw new Error(`Direct fetch failed with status ${response.status}. Response: ${errorText.substring(0, 200)}`);
67
- }
68
- logger.log('INFO', `[lookupUsernames/${batchId}] Direct node-fetch fallback successful.`);
69
-
70
- } catch (fallbackError) {
71
- logger.log('ERROR', `[lookupUsernames/${batchId}] Direct node-fetch fallback FAILED. Giving up on this batch.`, {
72
- error: fallbackError.message,
73
- source: 'eToro/Network'
74
- });
40
+ try { response = await fetch(url, options); // Ok let's try again with node, using GCP IP pools
41
+ if (!response.ok) { const errorText = await response.text(); throw new Error(`Direct fetch failed with status ${response.status}. Response: ${errorText.substring(0, 200)}`); }
42
+ logger.log('INFO', `[lookupUsernames/${batchId}] Direct node-fetch fallback successful.`); // Yay we win
43
+ } catch (fallbackError) { logger.log('ERROR', `[lookupUsernames/${batchId}] Direct node-fetch fallback FAILED. Giving up on this batch.`, { error: fallbackError.message, source: 'eToro/Network' }); // SHIT, we failed here too
75
44
  return null; // Give up on this batch
76
45
  }
77
- // --- END REFACTOR 3 ---
78
- } finally {
79
- if (proxyUsed) {
80
- headerManager.updatePerformance(header.id, wasSuccess);
81
- }
82
- }
83
-
84
- try {
85
- const data = await response.json();
86
- return data;
87
- } catch (parseError) {
88
- logger.log('ERROR', `[lookupUsernames/${batchId}] Failed to parse JSON response.`, { error: parseError.message });
89
- return null;
90
- }
91
- }));
92
-
93
- const results = await Promise.allSettled(batchPromises);
94
-
95
- const allUsers = results
96
- .filter(r => r.status === 'fulfilled' && r.value && Array.isArray(r.value))
97
- .flatMap(r => r.value);
46
+ } finally { if (proxyUsed) { headerManager.updatePerformance(header.id, wasSuccess); } } // If we used Appscript IP Pool and not GCP IP Pool, record performance
47
+ try { const data = await response.json(); return data;
48
+ } catch (parseError) { logger.log('ERROR', `[lookupUsernames/${batchId}] Failed to parse JSON response.`, { error: parseError.message }); return null; } }));
98
49
 
50
+ const results = await Promise.allSettled(batchPromises);
51
+ const allUsers = results .filter(r => r.status === 'fulfilled' && r.value && Array.isArray(r.value)) .flatMap(r => r.value);
99
52
  logger.log('INFO', `[lookupUsernames] Found ${allUsers.length} public users out of ${cids.length}.`);
100
53
  return allUsers;
101
54
  }
@@ -110,8 +63,6 @@ async function handleUpdate(task, taskId, { logger, headerManager, proxyManager,
110
63
  const today = new Date().toISOString().slice(0, 10);
111
64
  const portfolioBlockId = `${Math.floor(parseInt(userId) / 1000000)}M`;
112
65
  let isPrivate = false;
113
-
114
- // --- REFACTOR 2: ADD VERBOSE LOGGING (with User ID) ---
115
66
  logger.log('INFO', `[handleUpdate/${userId}] Starting update task. Type: ${userType}. Instruments: ${instrumentsToProcess.join(', ')}`);
116
67
 
117
68
  // --- 1. Process History Fetch (Sequentially) ---
@@ -123,81 +74,52 @@ async function handleUpdate(task, taskId, { logger, headerManager, proxyManager,
123
74
  if (!batchManager.checkAndSetHistoryFetched(userId)) {
124
75
  logger.log('INFO', `[handleUpdate/${userId}] Attempting history fetch.`);
125
76
  historyHeader = await headerManager.selectHeader();
126
- if (!historyHeader) {
127
- logger.log('WARN', `[handleUpdate/${userId}] Could not select history header. Skipping history.`);
77
+ if (!historyHeader) { logger.log('WARN', `[handleUpdate/${userId}] Could not select history header. Skipping history.`);
128
78
  } else {
129
79
  const historyUrl = `${config.ETORO_API_USERSTATS_URL}${username}/trades/oneYearAgo?CopyAsAsset=true`;
130
80
  const options = { headers: historyHeader.header };
131
81
  let response;
132
-
133
- try {
134
- // --- REFACTOR 3: ADD FALLBACK ---
135
- logger.log('TRACE', `[handleUpdate/${userId}] Attempting history fetch via AppScript proxy...`);
82
+ try { logger.log('TRACE', `[handleUpdate/${userId}] Attempting history fetch via AppScript proxy...`);
136
83
  response = await proxyManager.fetch(historyUrl, options);
137
- if (!response.ok) throw new Error(`AppScript proxy failed with status ${response.status}`);
138
- wasHistorySuccess = true;
84
+ if (!response.ok) throw new Error(`AppScript proxy failed with status ${response.status}`); // SHIT we failed here
85
+ wasHistorySuccess = true; // Appscript worked, we are very smart
139
86
 
140
87
  } catch (proxyError) {
141
- logger.log('WARN', `[handleUpdate/${userId}] History fetch via AppScript proxy FAILED. Error: ${proxyError.message}. Attempting direct node-fetch fallback.`, {
142
- error: proxyError.message,
143
- source: 'AppScript'
144
- });
88
+ logger.log('WARN', `[handleUpdate/${userId}] History fetch via AppScript proxy FAILED. Error: ${proxyError.message}. Attempting direct node-fetch fallback.`, { error: proxyError.message, source: 'AppScript' }); // SHIT we failed here
145
89
  proxyUsedForHistory = false;
146
-
147
- try {
148
- response = await fetch(historyUrl, options); // Direct node-fetch
149
- if (!response.ok) {
150
- const errorText = await response.text();
151
- throw new Error(`Direct fetch failed with status ${response.status}. Response: ${errorText.substring(0, 200)}`);
152
- }
153
- wasHistorySuccess = true; // Fallback succeeded
90
+ try { response = await fetch(historyUrl, options);
91
+ if (!response.ok) { const errorText = await response.text();
92
+ throw new Error(`Direct fetch failed with status ${response.status}. Response: ${errorText.substring(0, 200)}`); } // SHIT we failed here too
93
+ wasHistorySuccess = true; // Fallback succeeded, we are so smart
154
94
 
155
- } catch (fallbackError) {
156
- logger.log('ERROR', `[handleUpdate/${userId}] History fetch direct fallback FAILED.`, {
157
- error: fallbackError.message,
158
- source: 'eToro/Network'
159
- });
160
- wasHistorySuccess = false;
95
+ } catch (fallbackError) { logger.log('ERROR', `[handleUpdate/${userId}] History fetch direct fallback FAILED.`, { error: fallbackError.message, source: 'eToro/Network' }); // We are dumb, everything failed
96
+ wasHistorySuccess = false; // Nope we are dumb....
161
97
  }
162
- // --- END REFACTOR 3 ---
163
98
  }
164
99
 
165
- if (wasHistorySuccess) {
166
- logger.log('INFO', `[handleUpdate/${userId}] History fetch successful.`);
100
+ if (wasHistorySuccess) { logger.log('INFO', `[handleUpdate/${userId}] History fetch successful.`); // Some method worked, we are very smart
167
101
  const data = await response.json();
168
- await batchManager.addToTradingHistoryBatch(userId, portfolioBlockId, today, data, userType);
169
- }
102
+ await batchManager.addToTradingHistoryBatch(userId, portfolioBlockId, today, data, userType); }
170
103
  }
171
- } else {
172
- logger.log('TRACE', `[handleUpdate/${userId}] History fetch skipped (already fetched by this instance).`);
173
- }
174
- } catch (err) {
175
- logger.log('ERROR', `[handleUpdate/${userId}] Unhandled error during history processing.`, { error: err.message });
176
- wasHistorySuccess = false;
177
- } finally {
178
- if (historyHeader && proxyUsedForHistory) {
179
- headerManager.updatePerformance(historyHeader.id, wasHistorySuccess);
180
- }
181
- }
104
+ } else { logger.log('TRACE', `[handleUpdate/${userId}] History fetch skipped (already fetched by this instance).`); }
105
+ } catch (err) { logger.log('ERROR', `[handleUpdate/${userId}] Unhandled error during history processing.`, { error: err.message }); wasHistorySuccess = false; // We fucked up.
106
+ } finally { if (historyHeader && proxyUsedForHistory) { headerManager.updatePerformance(historyHeader.id, wasHistorySuccess); } } // If we used appscript proxy, record performance, otherwise fuck off.
182
107
 
183
108
  // --- 2. Process Portfolio Fetches (Sequentially) ---
184
109
  logger.log('INFO', `[handleUpdate/${userId}] Starting ${instrumentsToProcess.length} sequential portfolio fetches.`);
185
110
 
186
- for (const instId of instrumentsToProcess) {
187
- if (isPrivate) {
188
- logger.log('INFO', `[handleUpdate/${userId}] Skipping remaining instruments because user was marked as private.`);
189
- break;
111
+ for (const instId of instrumentsToProcess) {
112
+ if (isPrivate) {
113
+ logger.log('INFO', `[handleUpdate/${userId}] Skipping remaining instruments because user was marked as private.`);
114
+ break;
190
115
  }
191
116
 
192
117
  const portfolioHeader = await headerManager.selectHeader();
193
- if (!portfolioHeader) {
194
- logger.log('ERROR', `[handleUpdate/${userId}] Could not select portfolio header for instId ${instId}. Skipping this instrument.`);
118
+ if (!portfolioHeader) { logger.log('ERROR', `[handleUpdate/${userId}] Could not select portfolio header for instId ${instId}. Skipping this instrument.`);
195
119
  continue;
196
120
  }
197
121
 
198
- const portfolioUrl = userType === 'speculator'
199
- ? `${config.ETORO_API_POSITIONS_URL}?cid=${userId}&InstrumentID=${instId}`
200
- : `${config.ETORO_API_PORTFOLIO_URL}?cid=${userId}`;
122
+ const portfolioUrl = userType === 'speculator' ? `${config.ETORO_API_POSITIONS_URL}?cid=${userId}&InstrumentID=${instId}` : `${config.ETORO_API_PORTFOLIO_URL}?cid=${userId}`;
201
123
 
202
124
  const options = { headers: portfolioHeader.header };
203
125
  let response;
@@ -208,73 +130,53 @@ async function handleUpdate(task, taskId, { logger, headerManager, proxyManager,
208
130
  // --- REFACTOR 3: ADD FALLBACK ---
209
131
  logger.log('TRACE', `[handleUpdate/${userId}] Attempting portfolio fetch for instId ${instId} via AppScript proxy...`);
210
132
  response = await proxyManager.fetch(portfolioUrl, options);
211
- if (!response.ok) throw new Error(`AppScript proxy failed with status ${response.status}`);
212
- wasPortfolioSuccess = true;
133
+ if (!response.ok) throw new Error(`AppScript proxy failed with status ${response.status}`); // SHIT we failed here
134
+ wasPortfolioSuccess = true; // Oh we are smart, worked first time.
213
135
 
214
- } catch (proxyError) {
215
- logger.log('WARN', `[handleUpdate/${userId}] Portfolio fetch for instId ${instId} via AppScript proxy FAILED. Error: ${proxyError.message}. Attempting direct node-fetch fallback.`, {
216
- error: proxyError.message,
217
- source: 'AppScript'
218
- });
219
- proxyUsedForPortfolio = false;
136
+ } catch (proxyError) { // try fallback with local node fetch using GCP IP Pools
137
+ logger.log('WARN', `[handleUpdate/${userId}] Portfolio fetch for instId ${instId} via AppScript proxy FAILED. Error: ${proxyError.message}. Attempting direct node-fetch fallback.`, { error: proxyError.message, source: 'AppScript' });
138
+ proxyUsedForPortfolio = false; // We are not using Appscript proxy here as fallback is GCP based, so false
220
139
 
221
140
  try {
222
141
  response = await fetch(portfolioUrl, options); // Direct node-fetch
223
142
  if (!response.ok) {
224
143
  const errorText = await response.text();
225
- throw new Error(`Direct fetch failed with status ${response.status}. Response: ${errorText.substring(0, 200)}`);
144
+ throw new Error(`Direct fetch failed with status ${response.status}. Response: ${errorText.substring(0, 200)}`); // SHIT we failed here
226
145
  }
227
- wasPortfolioSuccess = true; // Fallback succeeded
146
+ wasPortfolioSuccess = true; // Fallback succeeded we are so smart
228
147
 
229
148
  } catch (fallbackError) {
230
- logger.log('ERROR', `[handleUpdate/${userId}] Portfolio fetch for instId ${instId} direct fallback FAILED.`, {
231
- error: fallbackError.message,
232
- source: 'eToro/Network'
233
- });
149
+ logger.log('ERROR', `[handleUpdate/${userId}] Portfolio fetch for instId ${instId} direct fallback FAILED.`, { error: fallbackError.message, source: 'eToro/Network' });
234
150
  wasPortfolioSuccess = false;
235
151
  }
236
- // --- END REFACTOR 3 ---
237
152
  }
238
153
 
239
154
  // --- 4. Process Portfolio Result (with verbose, raw logging) ---
240
155
  if (wasPortfolioSuccess) {
241
156
  const body = await response.text();
242
- if (body.includes("user is PRIVATE")) {
243
- isPrivate = true;
244
- logger.log('WARN', `[handleUpdate/${userId}] User is PRIVATE. Marking for removal.`);
157
+ if (body.includes("user is PRIVATE")) { isPrivate = true; logger.log('WARN', `[handleUpdate/${userId}] User is PRIVATE. Marking for removal.`);
245
158
  break; // Stop processing more portfolios for this private user
246
159
  }
247
160
 
248
161
  try {
249
162
  const portfolioJson = JSON.parse(body);
250
163
  await batchManager.addToPortfolioBatch(userId, portfolioBlockId, today, portfolioJson, userType, instId);
251
- logger.log('INFO', `[handleUpdate/${userId}] Successfully processed portfolio for instId ${instId}.`);
252
-
253
- } catch (parseError) {
254
- // --- REFACTOR 4: RETURN EXACT PAGE RESPONSE ---
164
+ if (userType === 'speculator') { logger.log('INFO', `[handleUpdate/${userId}] Successfully processed portfolio for instId ${instId}.`); // Only speculators have an instid, so this is conditional
165
+ } else { logger.log('INFO', `[handleUpdate/${userId}] Successfully processed full portfolio (normal user).`); } // Normal users
166
+
167
+ } catch (parseError) { // Idk why this would happen, but if it does....log.
255
168
  wasPortfolioSuccess = false; // Mark as failure
256
- logger.log('ERROR', `[handleUpdate/${userId}] FAILED TO PARSE JSON RESPONSE. RAW BODY:`, {
257
- url: portfolioUrl,
258
- parseErrorMessage: parseError.message,
259
- rawResponseText: body // <--- THIS LOGS THE FULL HTML/ERROR RESPONSE
260
- });
261
- // --- END REFACTOR 4 ---
169
+ logger.log('ERROR', `[handleUpdate/${userId}] FAILED TO PARSE JSON RESPONSE. RAW BODY:`, { url: portfolioUrl, parseErrorMessage: parseError.message, rawResponseText: body }); // Return full response
262
170
  }
263
- } else {
264
- logger.log('WARN', `[handleUpdate/${userId}] Portfolio fetch failed for instId ${instId}. No response to process.`);
265
- }
171
+ } else { logger.log('WARN', `[handleUpdate/${userId}] Portfolio fetch failed for instId ${instId}. No response to process.`); }
266
172
 
267
- if (proxyUsedForPortfolio) {
268
- headerManager.updatePerformance(portfolioHeader.id, wasPortfolioSuccess);
269
- }
270
- } // --- End of sequential portfolio loop ---
173
+ if (proxyUsedForPortfolio) { headerManager.updatePerformance(portfolioHeader.id, wasPortfolioSuccess); }
174
+ }
271
175
 
272
176
  // --- 5. Handle Private Users & Timestamps ---
273
177
  if (isPrivate) {
274
178
  logger.log('WARN', `[handleUpdate/${userId}] Removing private user from updates.`);
275
- for (const instrumentId of instrumentsToProcess) {
276
- await batchManager.deleteFromTimestampBatch(userId, userType, instId);
277
- }
179
+ for (const instrumentId of instrumentsToProcess) { await batchManager.deleteFromTimestampBatch(userId, userType, instId); }
278
180
  const blockCountsRef = db.doc(config.FIRESTORE_DOC_SPECULATOR_BLOCK_COUNTS);
279
181
  for (const instrumentId of instrumentsToProcess) {
280
182
  const incrementField = `counts.${instrumentId}_${Math.floor(userId/1e6)*1e6}`;
@@ -284,12 +186,8 @@ async function handleUpdate(task, taskId, { logger, headerManager, proxyManager,
284
186
  }
285
187
 
286
188
  // If not private, update all timestamps
287
- for (const instrumentId of instrumentsToProcess) {
288
- await batchManager.updateUserTimestamp(userId, userType, instId);
289
- }
290
- if (userType === 'speculator') {
291
- await batchManager.addSpeculatorTimestampFix(userId, String(Math.floor(userId/1e6)*1e6));
292
- }
189
+ for (const instrumentId of instrumentsToProcess) { await batchManager.updateUserTimestamp(userId, userType, instId); }
190
+ if (userType === 'speculator') { await batchManager.addSpeculatorTimestampFix(userId, String(Math.floor(userId/1e6)*1e6)); }
293
191
 
294
192
  logger.log('INFO', `[handleUpdate/${userId}] Update task finished successfully.`);
295
193
  // 'finally' block for header flushing is handled by the main handler_creator.js
@@ -14,13 +14,10 @@ async function fetchAndVerifyUser(user, { logger, headerManager, proxyManager },
14
14
  const logPrefix = `[VERIFY/${taskId}/${cid}]`; // --- REFACTOR 2: VERBOSE LOGGING ---
15
15
 
16
16
  const selectedHeader = await headerManager.selectHeader();
17
- if (!selectedHeader) {
18
- logger.log('WARN', `${logPrefix} Could not select a header. Skipping user.`);
19
- return null;
20
- }
17
+ if (!selectedHeader) { logger.log('WARN', `${logPrefix} Could not select a header. Skipping user.`); return null; }
21
18
 
22
19
  let wasSuccess = false;
23
- let proxyUsed = true;
20
+ let proxyUsed = true;
24
21
 
25
22
  try { // Outer try for the whole operation
26
23
  let response;
@@ -30,30 +27,24 @@ async function fetchAndVerifyUser(user, { logger, headerManager, proxyManager },
30
27
  try {
31
28
  // --- REFACTOR 3: ADD FALLBACK ---
32
29
  logger.log('TRACE', `${logPrefix} Attempting portfolio fetch via AppScript proxy...`);
33
- response = await proxyManager.fetch(url, options);
34
- if (!response.ok) throw new Error(`AppScript proxy failed with status ${response.status}`);
30
+ response = await proxyManager.fetch(url, options); // Try....
31
+ if (!response.ok) throw new Error(`AppScript proxy failed with status ${response.status}`); // Fail
35
32
 
36
- } catch (proxyError) {
37
- logger.log('WARN', `${logPrefix} AppScript proxy fetch FAILED. Error: ${proxyError.message}. Attempting direct node-fetch fallback.`, {
38
- error: proxyError.message,
39
- source: 'AppScript'
40
- });
33
+ } catch (proxyError) { // We fucked up, appscript proxy failed, log error type
34
+ logger.log('WARN', `${logPrefix} AppScript proxy fetch FAILED. Error: ${proxyError.message}. Attempting direct node-fetch fallback.`, { error: proxyError.message, source: 'AppScript' });
41
35
  proxyUsed = false;
42
36
 
43
37
  try {
44
- response = await fetch(url, options); // Direct node-fetch
45
- if (!response.ok) {
38
+ response = await fetch(url, options); // Direct node-fetch as fallback
39
+ if (!response.ok) { // we fucked up here too, log error text
46
40
  const errorText = await response.text();
47
41
  throw new Error(`Direct fetch failed with status ${response.status}. Response: ${errorText.substring(0, 200)}`);
48
42
  }
49
43
  // Fallback succeeded, but we don't set wasSuccess yet,
50
44
  // as we still need to parse the body.
51
45
 
52
- } catch (fallbackError) {
53
- logger.log('ERROR', `${logPrefix} Direct node-fetch fallback FAILED.`, {
54
- error: fallbackError.message,
55
- source: 'eToro/Network'
56
- });
46
+ } catch (fallbackError) { // log error type for fallback
47
+ logger.log('ERROR', `${logPrefix} Direct node-fetch fallback FAILED.`, { error: fallbackError.message, source: 'eToro/Network' });
57
48
  throw fallbackError; // Throw to be caught by outer try
58
49
  }
59
50
  // --- END REFACTOR 3 ---
@@ -62,46 +53,38 @@ async function fetchAndVerifyUser(user, { logger, headerManager, proxyManager },
62
53
  // --- If we are here, `response` is valid ---
63
54
  let portfolioData;
64
55
  const body = await response.text();
65
-
66
- try {
67
- // --- REFACTOR 4: LOG RAW RESPONSE ON PARSE FAILURE ---
68
- portfolioData = JSON.parse(body);
69
- } catch (parseError) {
70
- logger.log('ERROR', `${logPrefix} FAILED TO PARSE JSON RESPONSE. RAW BODY:`, {
71
- parseErrorMessage: parseError.message,
72
- rawResponseText: body
73
- });
56
+ // --- REFACTOR 4: LOG RAW RESPONSE ON PARSE FAILURE ---
57
+ try { portfolioData = JSON.parse(body);
58
+ } catch (parseError) { // If we can't parse JSON, try raw log, shouldn't happen but this world is weird....
59
+ logger.log('ERROR', `${logPrefix} FAILED TO PARSE JSON RESPONSE. RAW BODY:`, { parseErrorMessage: parseError.message, rawResponseText: body });
74
60
  throw new Error(`Failed to parse JSON for user ${cid}.`);
75
61
  }
76
62
  // --- END REFACTOR 4 ---
77
63
 
78
64
  // --- Original logic ---
79
65
  if (userType === 'speculator') {
80
- const instruments = portfolioData.AggregatedPositions.map(p => p.InstrumentID).filter(id => SPECULATOR_INSTRUMENTS_ARRAY.includes(id));
66
+ const instruments = portfolioData.AggregatedPositions.map(p => p.InstrumentID).filter(id => SPECULATOR_INSTRUMENTS_ARRAY.includes(id));
81
67
  if (!instruments.length) {
82
68
  logger.log('TRACE', `${logPrefix} Verified user, but not a speculator (no matching assets).`);
83
- wasSuccess = true; // API call *worked*
69
+ wasSuccess = true; // API call *worked* we are so smart
84
70
  return null;
85
71
  }
86
- logger.log('INFO', `${logPrefix} Verified as SPECULATOR.`);
87
- wasSuccess = true;
88
- return { type: 'speculator', userId: cid, isBronze: user.isBronze, username: user.username, updateData: { instruments, lastVerified: new Date(), lastHeldSpeculatorAsset: new Date() } };
72
+ logger.log('INFO', `${logPrefix} Verified as SPECULATOR.`);
73
+ wasSuccess = true; // Mark that we are very high IQ
74
+ return { type: 'speculator', userId: cid, isBronze: user.isBronze, username: user.username, updateData: { instruments, lastVerified: new Date(), lastHeldSpeculatorAsset: new Date() } }; // Return values
89
75
  }
90
-
91
- logger.log('INFO', `${logPrefix} Verified as NORMAL user.`);
92
- wasSuccess = true;
93
- return { type: 'normal', userId: cid, isBronze: user.isBronze, username: user.username, updateData: { lastVerified: new Date() } };
76
+ // Other user types are just normal users....
77
+ logger.log('INFO', `${logPrefix} Verified as NORMAL user.`);
78
+ wasSuccess = true; // We are smart, this worked
79
+ return { type: 'normal', userId: cid, isBronze: user.isBronze, username: user.username, updateData: { lastVerified: new Date() } }; // Return values
94
80
 
95
81
  } catch (err) {
96
82
  // This catches proxy, fallback, or parse errors
97
- logger.log('WARN', `${logPrefix} Error processing user.`, { errorMessage: err.message });
83
+ logger.log('WARN', `${logPrefix} Error processing user.`, { errorMessage: err.message }); // Some shit broke
98
84
  wasSuccess = false; // Ensure it's marked as failure
99
85
  return null;
100
86
  } finally {
101
- if (selectedHeader && proxyUsed) {
102
- // Only update performance if the proxy was used
103
- headerManager.updatePerformance(selectedHeader.id, wasSuccess);
104
- }
87
+ if (selectedHeader && proxyUsed) { headerManager.updatePerformance(selectedHeader.id, wasSuccess); } // Only update performance if the proxy was used
105
88
  }
106
89
  }
107
90
 
@@ -118,7 +101,7 @@ async function handleVerify(task, taskId, { db, logger, ...dependencies }, confi
118
101
  logger.log('INFO', `[VERIFY/${taskId}] Starting sequential verification for ${users.length} users...`);
119
102
  const results = [];
120
103
  for (const user of users) {
121
- // Await each user one by one
104
+ // Await each user one by one otherwise appscript starts to cry and internally throttle...then rate limit...then fuck everything up and break and then i want to die.
122
105
  const result = await fetchAndVerifyUser(user, { db, logger, ...dependencies }, { ...config, userType }, taskId);
123
106
  results.push(result); // Push the actual result (or null)
124
107
  }
@@ -136,7 +119,7 @@ async function handleVerify(task, taskId, { db, logger, ...dependencies }, confi
136
119
  else normalUpdates[`users.${d.userId}`] = d.updateData;
137
120
  }
138
121
  });
139
-
122
+ // Process responses in object keys
140
123
  if (Object.keys(speculatorUpdates).length || Object.keys(normalUpdates).length) {
141
124
  const blockRef = db.collection(userType === 'speculator' ? config.FIRESTORE_COLLECTION_SPECULATOR_BLOCKS : config.FIRESTORE_COLLECTION_NORMAL_PORTFOLIOS).doc(String(blockId));
142
125
  batch.set(blockRef, userType === 'speculator' ? speculatorUpdates : normalUpdates, { merge: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.170",
3
+ "version": "1.0.172",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -27,10 +27,6 @@
27
27
  "calculations",
28
28
  "finance"
29
29
  ],
30
- "scripts": {
31
- "postpublish": "node ./auto-deploy.js",
32
- "release": "node ./release.js"
33
- },
34
30
  "dependencies": {
35
31
  "@google-cloud/firestore": "^7.11.3",
36
32
  "@google-cloud/pubsub": "latest",