Mirror of
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: prevent title repetitive words (#6)

* feat: fetch recent titles from generate script

* [autofix.ci] apply automated fixes

* feat: update events title generation

* [autofix.ci] apply automated fixes

* Update scripts/generate-digest.ts

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update src/lib/events.ts

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* fix: syntax error

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

authored by

Felix Schneider
coderabbitai[bot]
autofix-ci[bot]
and committed by
GitHub
3f4d4a52 065be8e3

+49 -6
+31 -3
scripts/generate-digest.ts
··· 1 - import { mkdir, writeFile } from "node:fs/promises"; 1 + import { mkdir, readFile, readdir, writeFile } from "node:fs/promises"; 2 2 import { join } from "node:path"; 3 3 4 4 import { ··· 35 35 return "nightly"; // Target 10pm 36 36 } 37 37 38 + async function getRecentTitles(count = 15): Promise<string[]> { 39 + try { 40 + const files = await readdir(POST_DIR); 41 + const jsonFiles = files 42 + .filter((f) => f.endsWith(".json")) 43 + .sort() 44 + .reverse() 45 + .slice(0, count); 46 + 47 + const titles: string[] = []; 48 + for (const file of jsonFiles) { 49 + try { 50 + const content = await readFile(join(POST_DIR, file), "utf-8"); 51 + const data = JSON.parse(content); 52 + if (typeof data.title === "string" && data.title.trim()) { 53 + titles.push(data.title); 54 + } 55 + } catch { 56 + console.warn(`Skipping malformed post file: ${file}`); 57 + } 58 + } 59 + return titles; 60 + } catch { 61 + return []; 62 + } 63 + } 64 + 38 65 async function run() { 39 66 console.log("\n\x1b[1m🚀 Generating Intelligent Topic Digest\x1b[0m"); 40 67 ··· 78 105 ); 79 106 80 107 try { 81 - const [gh, bs] = await Promise.all([ 108 + const [gh, bs, recentTitles] = await Promise.all([ 82 109 fetchGitHubEvents(startTime, nearestMark), 83 110 fetchBlueskyEvents(startTime, nearestMark), 111 + getRecentTitles(20), 84 112 ]); 85 113 86 114 const allEvents = [...gh, ...bs]; ··· 92 120 } 93 121 94 122 const heroTopic = pickWeightedTopic(topics); 95 - const catchyTitle = await generateCatchyTitle(heroTopic); 123 + const catchyTitle = await generateCatchyTitle(heroTopic, recentTitles); 96 124 97 125 const type = getPostType(nearestMark); 98 126 const dateStr = nearestMark.toISOString().split("T")[0];
+18 -3
src/lib/events.ts
··· 218 218 } 219 219 } 220 220 221 - export async function generateCatchyTitle(topic: Topic): Promise<string> { 221 + export async function generateCatchyTitle( 222 + topic: Topic, 223 + recentTitles: string[] = [] 224 + ): Promise<string> { 225 + const normalizedTitles = recentTitles 226 + .map((title) => title.replace(/\s+/g, " ").trim()) 227 + .filter(Boolean) 228 + .slice(0, 10) 229 + .map((title) => (title.length > 80 ? `${title.slice(0, 77)}…` : title)); 230 + 231 + const avoidList = 232 + normalizedTitles.length > 0 233 + ? `Avoid using these specific words or styles from recent headlines: ${normalizedTitles.join("; ")}` 234 + : ""; 235 + 222 236 const prompt = `You are a tech journalist for npmx. Create a very short (max 5-7 words), catchy headline for this topic. 237 + ${avoidList} 223 238 Return ONLY the text, no quotes. Topic: ${topic.title} - ${topic.summary}`; 224 239 225 240 try { ··· 228 243 { 229 244 role: "system", 230 245 content: 231 - "You provide raw text headlines without any quotation marks or wrapping characters.", 246 + "You provide raw text headlines without any quotation marks or wrapping characters. You vary your vocabulary significantly.", 232 247 }, 233 248 { role: "user", content: prompt }, 234 249 ], 235 250 model: "gpt-4o-mini", 236 - temperature: 0.7, 251 + temperature: 0.8, 237 252 max_tokens: 30, 238 253 }); 239 254