bulltrackers-module 1.0.448 → 1.0.450
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.
|
@@ -199,8 +199,8 @@ async function loadDailySocialPostInsights(config, deps, dateString) {
|
|
|
199
199
|
signedIn: {} // Map<UserId, Map<PostId, Data>> - For Signed-In Users
|
|
200
200
|
};
|
|
201
201
|
|
|
202
|
-
const PI_COL_NAME = config.piSocialCollectionName || 'pi_social_posts';
|
|
203
|
-
const SIGNED_IN_COL_NAME = config.signedInUserSocialCollection || '
|
|
202
|
+
const PI_COL_NAME = config.piSocialCollectionName || config.piSocialCollection || 'pi_social_posts';
|
|
203
|
+
const SIGNED_IN_COL_NAME = config.signedInUserSocialCollection || 'signed_in_users_social';
|
|
204
204
|
|
|
205
205
|
// 2. Define Time Range (UTC Day)
|
|
206
206
|
const startDate = new Date(dateString + 'T00:00:00Z');
|
|
@@ -232,7 +232,7 @@ async function loadDailySocialPostInsights(config, deps, dateString) {
|
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
234
|
else if (path.includes(SIGNED_IN_COL_NAME)) {
|
|
235
|
-
// Path format: .../
|
|
235
|
+
// Path format: .../signed_in_users_social/{userId}/posts/{postId}
|
|
236
236
|
const parts = path.split('/');
|
|
237
237
|
const colIndex = parts.indexOf(SIGNED_IN_COL_NAME);
|
|
238
238
|
if (colIndex !== -1 && parts[colIndex + 1]) {
|
|
@@ -70,7 +70,7 @@ exports.runRootDataIndexer = async (config, dependencies) => {
|
|
|
70
70
|
|
|
71
71
|
// Collection Names (Fail-safe defaults)
|
|
72
72
|
const PI_SOCIAL_COLL_NAME = collections.piSocial || 'pi_social_posts';
|
|
73
|
-
const SIGNED_IN_SOCIAL_COLL_NAME = collections.signedInUserSocialCollection || '
|
|
73
|
+
const SIGNED_IN_SOCIAL_COLL_NAME = collections.signedInUserSocialCollection || 'signed_in_users_social';
|
|
74
74
|
|
|
75
75
|
const scanMode = targetDate ? 'SINGLE_DATE' : 'FULL_SCAN';
|
|
76
76
|
logger.log('INFO', `[RootDataIndexer] Starting Root Data Availability Scan... Mode: ${scanMode}`, { targetDate });
|
|
@@ -252,8 +252,14 @@ exports.runRootDataIndexer = async (config, dependencies) => {
|
|
|
252
252
|
if (!universalSocialSnap.empty) {
|
|
253
253
|
universalSocialSnap.docs.forEach(doc => {
|
|
254
254
|
const path = doc.ref.path;
|
|
255
|
-
|
|
256
|
-
|
|
255
|
+
// Use includes() to match collection name anywhere in path (more robust)
|
|
256
|
+
// Path format: {collectionName}/{userId}/posts/{postId}
|
|
257
|
+
if (path.includes(`/${PI_SOCIAL_COLL_NAME}/`) || path.startsWith(`${PI_SOCIAL_COLL_NAME}/`)) {
|
|
258
|
+
foundPISocial = true;
|
|
259
|
+
}
|
|
260
|
+
if (path.includes(`/${SIGNED_IN_SOCIAL_COLL_NAME}/`) || path.startsWith(`${SIGNED_IN_SOCIAL_COLL_NAME}/`)) {
|
|
261
|
+
foundSignedInSocial = true;
|
|
262
|
+
}
|
|
257
263
|
});
|
|
258
264
|
}
|
|
259
265
|
|
|
@@ -133,7 +133,25 @@ async function getAdvancedAnalysisFromGemini(dependencies, snippet) {
|
|
|
133
133
|
};
|
|
134
134
|
|
|
135
135
|
} catch (e) {
|
|
136
|
-
logger.log("ERROR", "[Gemini AI] JSON handling failed",
|
|
136
|
+
logger.log("ERROR", "[Gemini AI] JSON handling failed", {
|
|
137
|
+
error: e.message,
|
|
138
|
+
stack: e.stack,
|
|
139
|
+
snippet: snippet.substring(0, 100),
|
|
140
|
+
part: part ? {
|
|
141
|
+
hasJson: !!part.json,
|
|
142
|
+
hasText: !!part.text,
|
|
143
|
+
textPreview: part.text ? part.text.substring(0, 200) : null
|
|
144
|
+
} : null,
|
|
145
|
+
resultStructure: result?.response ? {
|
|
146
|
+
hasCandidates: !!result.response.candidates,
|
|
147
|
+
candidatesLength: result.response.candidates?.length,
|
|
148
|
+
firstCandidate: result.response.candidates?.[0] ? {
|
|
149
|
+
hasContent: !!result.response.candidates[0].content,
|
|
150
|
+
hasParts: !!result.response.candidates[0].content?.parts,
|
|
151
|
+
partsLength: result.response.candidates[0].content?.parts?.length
|
|
152
|
+
} : null
|
|
153
|
+
} : null
|
|
154
|
+
});
|
|
137
155
|
return { overallSentiment: "Neutral", topics: [], isSpam: false, qualityScore: 0.5 };
|
|
138
156
|
}
|
|
139
157
|
}
|
|
@@ -235,21 +253,36 @@ exports.handleSocialTask = async (message, context, config, dependencies) => {
|
|
|
235
253
|
existingDocs.forEach(d => existingIds.add(d.id));
|
|
236
254
|
}
|
|
237
255
|
|
|
256
|
+
// Filter and prepare posts: only new posts in English
|
|
257
|
+
const newPostsWithDiscussion = discussions
|
|
258
|
+
.filter(d => {
|
|
259
|
+
const post = d.post;
|
|
260
|
+
if (!post || !post.id || !post.message?.text || existingIds.has(post.id)) return false;
|
|
261
|
+
const lang = (post.message.languageCode || 'unknown').toLowerCase();
|
|
262
|
+
return lang === 'en' || lang === 'en-gb';
|
|
263
|
+
})
|
|
264
|
+
// Sort by text length (longer posts are generally more useful for analysis)
|
|
265
|
+
.sort((a, b) => (b.post.message.text?.length || 0) - (a.post.message.text?.length || 0));
|
|
266
|
+
|
|
267
|
+
// COST OPTIMIZATION: Cap AI processing to top 5 posts per user to reduce costs
|
|
268
|
+
// Remaining posts will be stored with default AI values
|
|
269
|
+
const MAX_POSTS_FOR_AI = 5;
|
|
270
|
+
const postsForAI = newPostsWithDiscussion.slice(0, MAX_POSTS_FOR_AI);
|
|
271
|
+
const postsWithoutAI = newPostsWithDiscussion.slice(MAX_POSTS_FOR_AI);
|
|
272
|
+
|
|
273
|
+
logger.log('INFO', `[SocialTask/${taskId}] Filtered ${newPostsWithDiscussion.length} new posts. Sending ${postsForAI.length} to AI, ${postsWithoutAI.length} without AI.`);
|
|
274
|
+
|
|
238
275
|
const batch = db.batch();
|
|
239
276
|
let pageBatchCount = 0;
|
|
240
277
|
|
|
241
|
-
|
|
278
|
+
// Process posts that will get AI analysis (top 5 by text length)
|
|
279
|
+
for (const discussion of postsForAI) {
|
|
242
280
|
if (totalSaved + pageBatchCount >= MAX_POSTS_TO_STORE) {
|
|
243
281
|
keepFetching = false;
|
|
244
282
|
break;
|
|
245
283
|
}
|
|
246
284
|
|
|
247
285
|
const post = discussion.post;
|
|
248
|
-
if (!post || !post.id || !post.message?.text || existingIds.has(post.id)) continue;
|
|
249
|
-
|
|
250
|
-
const lang = (post.message.languageCode || 'unknown').toLowerCase();
|
|
251
|
-
if (lang !== 'en' && lang !== 'en-gb') continue;
|
|
252
|
-
|
|
253
286
|
const snippet = post.message.text.substring(0, 500);
|
|
254
287
|
const aiResult = await getAdvancedAnalysisFromGemini(dependencies, snippet);
|
|
255
288
|
|
|
@@ -260,7 +293,7 @@ exports.handleSocialTask = async (message, context, config, dependencies) => {
|
|
|
260
293
|
username: post.owner?.username,
|
|
261
294
|
createdAt: post.created,
|
|
262
295
|
fetchedAt: FieldValue.serverTimestamp(),
|
|
263
|
-
snippet: snippet,
|
|
296
|
+
snippet: snippet,
|
|
264
297
|
stats: {
|
|
265
298
|
likes: discussion.emotionsData?.like?.paging?.totalCount || 0,
|
|
266
299
|
comments: discussion.summary?.totalCommentsAndReplies || 0
|
|
@@ -274,6 +307,40 @@ exports.handleSocialTask = async (message, context, config, dependencies) => {
|
|
|
274
307
|
pageBatchCount++;
|
|
275
308
|
}
|
|
276
309
|
|
|
310
|
+
// Process remaining posts WITHOUT AI analysis (to avoid costs, but still store them)
|
|
311
|
+
for (const discussion of postsWithoutAI) {
|
|
312
|
+
if (totalSaved + pageBatchCount >= MAX_POSTS_TO_STORE) {
|
|
313
|
+
keepFetching = false;
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const post = discussion.post;
|
|
318
|
+
const docData = {
|
|
319
|
+
postId: post.id,
|
|
320
|
+
text: post.message.text,
|
|
321
|
+
ownerId: post.owner?.id,
|
|
322
|
+
username: post.owner?.username,
|
|
323
|
+
createdAt: post.created,
|
|
324
|
+
fetchedAt: FieldValue.serverTimestamp(),
|
|
325
|
+
snippet: post.message.text.substring(0, 500),
|
|
326
|
+
stats: {
|
|
327
|
+
likes: discussion.emotionsData?.like?.paging?.totalCount || 0,
|
|
328
|
+
comments: discussion.summary?.totalCommentsAndReplies || 0
|
|
329
|
+
},
|
|
330
|
+
aiAnalysis: {
|
|
331
|
+
overallSentiment: "Neutral",
|
|
332
|
+
topics: [],
|
|
333
|
+
isSpam: false,
|
|
334
|
+
qualityScore: 0.5
|
|
335
|
+
}, // Default values when AI is skipped to reduce costs
|
|
336
|
+
tags: post.tags?.map(t => t.market?.symbolName).filter(Boolean) || []
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
batch.set(db.collection(targetCollectionPath).doc(post.id), docData);
|
|
340
|
+
batch.set(processedRef.doc(post.id), { processedAt: FieldValue.serverTimestamp() });
|
|
341
|
+
pageBatchCount++;
|
|
342
|
+
}
|
|
343
|
+
|
|
277
344
|
if (pageBatchCount > 0) {
|
|
278
345
|
await batch.commit();
|
|
279
346
|
totalSaved += pageBatchCount;
|