@aikeytake/social-automation 2.0.5 → 2.0.7
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/package.json +2 -2
- package/src/index.js +101 -70
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aikeytake/social-automation",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.7",
|
|
4
4
|
"description": "Content research and aggregation tool for AI agents",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"author": "aikeytake",
|
|
25
25
|
"license": "MIT",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@aikeytake/social-automation": "2.0.
|
|
27
|
+
"@aikeytake/social-automation": "2.0.5",
|
|
28
28
|
"@supabase/supabase-js": "^2.47.0",
|
|
29
29
|
"axios": "^1.7.9",
|
|
30
30
|
"cheerio": "^1.0.0",
|
package/src/index.js
CHANGED
|
@@ -7,7 +7,15 @@ import apiFetch from './fetchers/api.js';
|
|
|
7
7
|
import fs from 'fs';
|
|
8
8
|
import path from 'path';
|
|
9
9
|
import { fileURLToPath } from 'url';
|
|
10
|
-
import
|
|
10
|
+
import { createRequire } from 'module';
|
|
11
|
+
|
|
12
|
+
dotenv.config();
|
|
13
|
+
|
|
14
|
+
const require = createRequire(import.meta.url);
|
|
15
|
+
const defaultConfig = require('../config/sources.json');
|
|
16
|
+
|
|
17
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
const logger = createLogger('Main');
|
|
11
19
|
|
|
12
20
|
dotenv.config();
|
|
13
21
|
|
|
@@ -133,20 +141,26 @@ function getDateString() {
|
|
|
133
141
|
}
|
|
134
142
|
|
|
135
143
|
async function saveSourceData(source, items, today) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
fs.
|
|
144
|
+
try {
|
|
145
|
+
const todayFolder = path.join(__dirname, '../data', today);
|
|
146
|
+
if (!fs.existsSync(todayFolder)) {
|
|
147
|
+
fs.mkdirSync(todayFolder, { recursive: true });
|
|
148
|
+
}
|
|
149
|
+
const filePath = path.join(todayFolder, `${source}.json`);
|
|
150
|
+
const data = {
|
|
151
|
+
date: today,
|
|
152
|
+
source: source,
|
|
153
|
+
total_items: items.length,
|
|
154
|
+
scraped_at: new Date().toISOString(),
|
|
155
|
+
items: items
|
|
156
|
+
};
|
|
157
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
|
|
158
|
+
} catch (err) {
|
|
159
|
+
// Vercel ephemeral filesystem - ignore write errors when saving to filesystem
|
|
160
|
+
if (err.code !== 'ENOENT' && err.code !== 'EACCES') {
|
|
161
|
+
logger.warn(`saveSourceData skipped: ${err.message}`);
|
|
162
|
+
}
|
|
139
163
|
}
|
|
140
|
-
|
|
141
|
-
const filePath = path.join(todayFolder, `${source}.json`);
|
|
142
|
-
const data = {
|
|
143
|
-
date: today,
|
|
144
|
-
source: source,
|
|
145
|
-
total_items: items.length,
|
|
146
|
-
scraped_at: new Date().toISOString(),
|
|
147
|
-
items: items
|
|
148
|
-
};
|
|
149
|
-
fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
|
|
150
164
|
}
|
|
151
165
|
|
|
152
166
|
async function saveToSupabase(supabase, items, date) {
|
|
@@ -177,66 +191,83 @@ async function saveToSupabase(supabase, items, date) {
|
|
|
177
191
|
}
|
|
178
192
|
|
|
179
193
|
async function generateCombinedFiles(results, today) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
// Load all source files
|
|
183
|
-
const allItems = [];
|
|
184
|
-
const sourceFiles = fs.readdirSync(todayFolder)
|
|
185
|
-
.filter(f => f.endsWith('.json') && f !== 'all.json' && f !== 'trending.json');
|
|
186
|
-
for (const file of sourceFiles) {
|
|
187
|
-
const filePath = path.join(todayFolder, file);
|
|
188
|
-
const content = fs.readFileSync(filePath, 'utf-8');
|
|
189
|
-
const data = JSON.parse(content);
|
|
190
|
-
allItems.push(...(data.items || []));
|
|
191
|
-
}
|
|
194
|
+
try {
|
|
195
|
+
const todayFolder = path.join(__dirname, '../data', today);
|
|
192
196
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
for (const item of scoredItems) {
|
|
213
|
-
const source = item.source || 'unknown';
|
|
214
|
-
if (!trendingBySource[source]) trendingBySource[source] = 0;
|
|
215
|
-
if (trendingBySource[source] < 5) {
|
|
216
|
-
trendingBySource[source]++;
|
|
217
|
-
finalTrending.push(item);
|
|
197
|
+
// Load all source files
|
|
198
|
+
const allItems = [];
|
|
199
|
+
let sourceFiles = [];
|
|
200
|
+
try {
|
|
201
|
+
sourceFiles = fs.readdirSync(todayFolder)
|
|
202
|
+
.filter(f => f.endsWith('.json') && f !== 'all.json' && f !== 'trending.json');
|
|
203
|
+
} catch {
|
|
204
|
+
// Folder may not exist - skip loading
|
|
205
|
+
sourceFiles = [];
|
|
206
|
+
}
|
|
207
|
+
for (const file of sourceFiles) {
|
|
208
|
+
try {
|
|
209
|
+
const filePath = path.join(todayFolder, file);
|
|
210
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
211
|
+
const data = JSON.parse(content);
|
|
212
|
+
allItems.push(...(data.items || []));
|
|
213
|
+
} catch {
|
|
214
|
+
// Skip unreadable files
|
|
215
|
+
}
|
|
218
216
|
}
|
|
219
|
-
if (finalTrending.length >= 20) break;
|
|
220
|
-
}
|
|
221
217
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
218
|
+
// Save all.json
|
|
219
|
+
const allData = {
|
|
220
|
+
date: today,
|
|
221
|
+
generated_at: new Date().toISOString(),
|
|
222
|
+
total_items: allItems.length,
|
|
223
|
+
sources: results.sources,
|
|
224
|
+
items: allItems
|
|
225
|
+
};
|
|
226
|
+
fs.writeFileSync(path.join(todayFolder, 'all.json'), JSON.stringify(allData, null, 2));
|
|
227
|
+
|
|
228
|
+
// Generate trending.json (top 20 by score with source diversity)
|
|
229
|
+
const scoredItems = allItems
|
|
230
|
+
.filter(item => item.metadata?.score || item.engagement?.upvotes || item.engagement?.points || 0)
|
|
231
|
+
.map(item => ({ ...item, combined_score: calculateScore(item) }))
|
|
232
|
+
.sort((a, b) => b.combined_score - a.combined_score);
|
|
233
|
+
|
|
234
|
+
// Apply source diversity: max 5 items per source
|
|
235
|
+
const trendingBySource = {};
|
|
236
|
+
const finalTrending = [];
|
|
237
|
+
for (const item of scoredItems) {
|
|
238
|
+
const source = item.source || 'unknown';
|
|
239
|
+
if (!trendingBySource[source]) trendingBySource[source] = 0;
|
|
240
|
+
if (trendingBySource[source] < 5) {
|
|
241
|
+
trendingBySource[source]++;
|
|
242
|
+
finalTrending.push(item);
|
|
243
|
+
}
|
|
244
|
+
if (finalTrending.length >= 20) break;
|
|
245
|
+
}
|
|
237
246
|
|
|
238
|
-
|
|
239
|
-
|
|
247
|
+
const trendingData = {
|
|
248
|
+
date: today,
|
|
249
|
+
generated_at: new Date().toISOString(),
|
|
250
|
+
total_items: finalTrending.length,
|
|
251
|
+
items: finalTrending.map((item, index) => ({
|
|
252
|
+
rank: index + 1,
|
|
253
|
+
score: item.combined_score,
|
|
254
|
+
sources: getItemSources(item),
|
|
255
|
+
title: item.title,
|
|
256
|
+
url: item.url || item.link,
|
|
257
|
+
summary: extractSummary(item),
|
|
258
|
+
keywords: extractKeywords(item),
|
|
259
|
+
engagement: item.engagement || item.metadata || {}
|
|
260
|
+
}))
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
fs.writeFileSync(path.join(todayFolder, 'trending.json'), JSON.stringify(trendingData, null, 2));
|
|
264
|
+
logger.success(`✅ Generated: trending.json (${finalTrending.length} items)`);
|
|
265
|
+
} catch (err) {
|
|
266
|
+
// Vercel ephemeral filesystem - ignore write errors
|
|
267
|
+
if (err.code !== 'ENOENT' && err.code !== 'EACCES') {
|
|
268
|
+
logger.warn(`generateCombinedFiles skipped: ${err.message}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
240
271
|
}
|
|
241
272
|
|
|
242
273
|
function calculateScore(item) {
|