vipcare 0.6.1 → 0.6.3
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/bin/vip.js +29 -8
- package/lib/card.js +3 -0
- package/lib/fetchers/search.js +16 -2
- package/package.json +1 -1
package/bin/vip.js
CHANGED
|
@@ -64,14 +64,19 @@ function gatherData(person) {
|
|
|
64
64
|
|
|
65
65
|
if (person.linkedinUrl) sources.push(person.linkedinUrl);
|
|
66
66
|
|
|
67
|
+
// Collect all search results into one consolidated file
|
|
68
|
+
const searchEntries = [];
|
|
69
|
+
|
|
67
70
|
if (person.rawSnippets.length) {
|
|
68
71
|
rawParts.push('=== Web Search Results ===');
|
|
69
|
-
rawParts.push(...person.rawSnippets);
|
|
70
|
-
// Save each snippet as a raw source file
|
|
71
72
|
person.rawSnippets.forEach((snippet, i) => {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
if (!snippet.trim()) return; // skip empty
|
|
74
|
+
if (snippet.trim().length < 30) return; // too short to be useful
|
|
75
|
+
const lines = snippet.trim().split('\n');
|
|
76
|
+
if (lines.length >= 2 && lines[0].trim() === lines[1].trim()) return; // title == body
|
|
77
|
+
rawParts.push(snippet);
|
|
78
|
+
const url = person.otherUrls?.[i] || '';
|
|
79
|
+
if (url) searchEntries.push({ url, snippet });
|
|
75
80
|
});
|
|
76
81
|
}
|
|
77
82
|
|
|
@@ -79,13 +84,28 @@ function gatherData(person) {
|
|
|
79
84
|
console.log(c.dim(` Searching the web for ${person.name}...`));
|
|
80
85
|
const results = searchPerson(person.name);
|
|
81
86
|
for (const r of results) {
|
|
82
|
-
|
|
87
|
+
if (!r.body?.trim() && !r.title?.trim()) continue; // skip empty
|
|
88
|
+
const snippet = `${r.title}\n${r.body}`;
|
|
89
|
+
if (snippet.trim().length < 30) continue; // too short to be useful
|
|
90
|
+
if (r.title?.trim() === r.body?.trim()) continue; // title == body (junk category)
|
|
91
|
+
if (!r.body?.trim() || r.body.trim().length < 20) continue; // no real body content
|
|
92
|
+
rawParts.push(snippet);
|
|
83
93
|
if (!sources.includes(r.url)) sources.push(r.url);
|
|
84
|
-
|
|
85
|
-
saveRawSource(personSlug, safeName, `# ${r.title}\n\nSource: ${r.url}\nFetched: ${new Date().toISOString()}\n\n${r.body}`);
|
|
94
|
+
searchEntries.push({ url: r.url, snippet });
|
|
86
95
|
}
|
|
87
96
|
}
|
|
88
97
|
|
|
98
|
+
// Save all search results in ONE file
|
|
99
|
+
if (searchEntries.length) {
|
|
100
|
+
const timestamp = new Date().toISOString();
|
|
101
|
+
const content = `# Web Search Results\n\nFetched: ${timestamp}\nQuery: ${person.name}\n\n` +
|
|
102
|
+
searchEntries
|
|
103
|
+
.filter(e => e.snippet.trim())
|
|
104
|
+
.map(e => `---\n\n**Source:** ${e.url}\n\n${e.snippet}`)
|
|
105
|
+
.join('\n\n');
|
|
106
|
+
saveRawSource(personSlug, 'web_search', content);
|
|
107
|
+
}
|
|
108
|
+
|
|
89
109
|
console.log(c.dim(` Raw data saved to ${path.join(getProfilesDir(), '.raw', personSlug)}/`));
|
|
90
110
|
return [rawParts.join('\n\n'), sources];
|
|
91
111
|
}
|
|
@@ -495,6 +515,7 @@ program.command('youtube-search')
|
|
|
495
515
|
|
|
496
516
|
// --- card ---
|
|
497
517
|
program.command('card')
|
|
518
|
+
.alias('open-cards')
|
|
498
519
|
.description('Generate and serve H5 baseball card page')
|
|
499
520
|
.option('-o, --output <path>', 'Output HTML file', path.join(os.homedir(), '.vip', 'cards', 'index.html'))
|
|
500
521
|
.option('-p, --port <port>', 'Server port', '3000')
|
package/lib/card.js
CHANGED
|
@@ -19,8 +19,11 @@ export function generateCards(profiles, outputPath) {
|
|
|
19
19
|
outputPath = path.join(os.homedir(), '.vip', 'cards', 'index.html');
|
|
20
20
|
}
|
|
21
21
|
const cards = [];
|
|
22
|
+
const seenSlugs = new Set();
|
|
22
23
|
|
|
23
24
|
for (const p of profiles) {
|
|
25
|
+
if (seenSlugs.has(p.slug)) continue; // skip duplicates
|
|
26
|
+
seenSlugs.add(p.slug);
|
|
24
27
|
const content = loadProfile(p.slug);
|
|
25
28
|
if (!content) continue;
|
|
26
29
|
|
package/lib/fetchers/search.js
CHANGED
|
@@ -76,8 +76,15 @@ function searchViaDdgApi(query) {
|
|
|
76
76
|
if (data.RelatedTopics) {
|
|
77
77
|
for (const topic of data.RelatedTopics) {
|
|
78
78
|
if (topic.Text && topic.FirstURL) {
|
|
79
|
+
// Skip DDG category pages (e.g. duckduckgo.com/c/SpaceX_people)
|
|
80
|
+
if (topic.FirstURL.includes('duckduckgo.com/c/')) continue;
|
|
81
|
+
const title = topic.Text.substring(0, 80);
|
|
82
|
+
// Skip entries where title and body are identical (just a category name)
|
|
83
|
+
if (title === topic.Text && topic.Text.length < 20) continue;
|
|
84
|
+
// Skip entries with body too short to be useful
|
|
85
|
+
if (topic.Text.length < 20) continue;
|
|
79
86
|
results.push({
|
|
80
|
-
title
|
|
87
|
+
title,
|
|
81
88
|
url: topic.FirstURL,
|
|
82
89
|
body: topic.Text,
|
|
83
90
|
});
|
|
@@ -86,8 +93,15 @@ function searchViaDdgApi(query) {
|
|
|
86
93
|
if (topic.Topics) {
|
|
87
94
|
for (const sub of topic.Topics) {
|
|
88
95
|
if (sub.Text && sub.FirstURL) {
|
|
96
|
+
// Skip DDG category pages
|
|
97
|
+
if (sub.FirstURL.includes('duckduckgo.com/c/')) continue;
|
|
98
|
+
const subTitle = sub.Text.substring(0, 80);
|
|
99
|
+
// Skip entries where title and body are identical
|
|
100
|
+
if (subTitle === sub.Text && sub.Text.length < 20) continue;
|
|
101
|
+
// Skip entries with body too short to be useful
|
|
102
|
+
if (sub.Text.length < 20) continue;
|
|
89
103
|
results.push({
|
|
90
|
-
title:
|
|
104
|
+
title: subTitle,
|
|
91
105
|
url: sub.FirstURL,
|
|
92
106
|
body: sub.Text,
|
|
93
107
|
});
|