-
-
Notifications
You must be signed in to change notification settings - Fork 1
Claude/configurable news reader g1 gdx #57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
… digests Major overhaul of AI-Pulse into a fully configurable, personalized news reader: - config.json: externalized 12 categories (AI, Cybersecurity, IoT, Windows, Mac, Linux, Tech, Entrepreneurship, Finance, Crypto, Open Source, Products) with 112 RSS sources in FR/EN, bilingual labels, and keyword mappings - aggregator.js: config-driven, franc-min language detection, article deduplication (70% Dice similarity), local Readability reader, RSS feed generation (global + per-category), email digests via Resend API - readme-viewer.html: section navigation sidebar with scroll spy, preferences panel (language, categories, keywords, article count slider), DOM-based filtering - reader.html: back button in article info bar - tracker.js: preferences manager, read history, bookmarks manager (localStorage) - All pages: portfolio moved from nav to footer, added "Proposer une source" and "S'abonner" links in footer pointing to GitHub Issue templates - Issue templates: source submission (new-source.yml) and subscription (subscribe.yml) - Workflows: add-source.yml (auto-add approved sources), manage-subscriber.yml (auto-add subscribers), update-ai-pulse.yml (API_RESEND env var) - Email digest: HTML template with personalized content per subscriber preferences https://claude.ai/code/session_0138bAjho1fWwiRZju3nJFJ3
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1a13d20ac0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@copilot Corrige le check qui n'a pas marché. |
|
@EthanThePhoenix38 I've opened a new pull request, #58, to work on those changes. Once the pull request is ready, I'll request review from you. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request introduces a major upgrade to the AI-Pulse news aggregator, transforming it from a hardcoded system to a fully configurable, multi-language platform with email subscriptions and enhanced user personalization.
Changes:
- Implemented a configuration-driven architecture using config.json with 12 content categories and multi-language support
- Added email digest functionality with subscriber management via GitHub issues and Resend API integration
- Introduced client-side preferences system with language filtering, category selection, keyword search, and read history tracking
- Added RSS feed generation for global and per-category subscriptions
Reviewed changes
Copilot reviewed 32 out of 33 changed files in this pull request and generated 31 comments.
Show a summary per file
| File | Description |
|---|---|
| config.json | New centralized configuration defining categories, feeds, language labels, and settings |
| src/aggregator.js | Major refactor: added language detection (franc-min), deduplication, RSS/email generation, and config-driven feed processing |
| templates/email-digest.html | New HTML email template for subscriber digests |
| readme-viewer.html | Enhanced with preferences panel, category filtering, language selection, and read history tracking |
| reader.html | Added back button for improved navigation |
| js/tracker.js | Extended with PrefsManager, ReadHistory, and Bookmarks managers for cross-page persistence |
| package.json | Added franc-min dependency for language detection |
| .github/workflows/manage-subscriber.yml | New workflow to automatically process subscription requests via GitHub issues |
| .github/workflows/add-source.yml | New workflow to automatically add approved RSS sources to config.json |
| .github/workflows/update-ai-pulse.yml | Updated to include API_RESEND secret for email functionality |
| .github/ISSUE_TEMPLATE/*.yml | New issue templates for subscriptions and source suggestions |
| stats.html, privacy.html, index.html, app.html, about.html | Footer updates: moved Portfolio to footer, added subscription/source suggestion links |
| feed*.xml | Generated RSS feed files for global and per-category subscriptions |
| data/subscribers.json | New empty subscribers list file |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| for (const subscriber of subscribers) { | ||
| try { | ||
| // Filter articles based on subscriber preferences | ||
| let filteredSections = []; | ||
| const subCategories = subscriber.categories || Object.keys(CATEGORIES); | ||
| const subLang = subscriber.lang || 'all'; | ||
|
|
||
| for (const cat of subCategories) { | ||
| const articles = categorizedArticles[cat]; | ||
| if (!articles || articles.length === 0) continue; | ||
|
|
||
| let filtered = articles; | ||
| if (subLang !== 'all') { | ||
| filtered = articles.filter(a => a.lang === subLang); | ||
| } | ||
| filtered = filtered.slice(0, 5); | ||
| if (filtered.length === 0) continue; | ||
|
|
||
| const catConfig = CATEGORIES[cat]; | ||
| const label = subLang === 'fr' ? catConfig.labels.fr : catConfig.labels.en; | ||
|
|
||
| const articlesHtml = filtered.map(a => | ||
| `<tr><td style="padding:10px 0;border-bottom:1px solid #1a1e47;"> | ||
| <a href="https://thephoenixagency.github.io/AI-Pulse/app.html?url=${encodeURIComponent(a.link)}&title=${encodeURIComponent(a.title)}&source=${encodeURIComponent(a.source)}" style="color:#00d9ff;text-decoration:none;font-weight:600;">${a.title}</a> | ||
| <br><span style="color:#94a3b8;font-size:13px;">${a.source} | ${a.lang.toUpperCase()}</span> | ||
| </td></tr>` | ||
| ).join(''); | ||
|
|
||
| filteredSections.push(`<tr><td style="padding:20px 0 10px;"><h2 style="color:#00d9ff;margin:0;font-size:18px;">${label}</h2></td></tr>${articlesHtml}`); | ||
| } | ||
|
|
||
| if (filteredSections.length === 0) continue; | ||
|
|
||
| const emailHtml = template | ||
| .replace('{{DATE}}', today) | ||
| .replace('{{SECTIONS}}', filteredSections.join('')) | ||
| .replace('{{EMAIL}}', subscriber.email) | ||
| .replace(/{{UNSUB_URL}}/g, `https://github.com/ThePhoenixAgency/AI-Pulse/issues/new?template=unsubscribe.yml&title=Unsubscribe:+${encodeURIComponent(subscriber.email)}`); | ||
|
|
||
| // Send via Resend | ||
| const response = await axios.post('https://api.resend.com/emails', { | ||
| from: 'AI-Pulse <noreply@resend.dev>', | ||
| to: [subscriber.email], | ||
| subject: `AI-Pulse Digest - ${today}`, | ||
| html: emailHtml | ||
| }, { | ||
| headers: { | ||
| 'Authorization': `Bearer ${apiKey}`, | ||
| 'Content-Type': 'application/json' | ||
| } | ||
| }); | ||
|
|
||
| console.error(` Email sent to ${subscriber.email}: ${response.data.id}`); | ||
| } catch (error) { | ||
| console.error(` Failed to send email to ${subscriber.email}: ${error.message}`); | ||
| } | ||
| } | ||
| } |
Copilot
AI
Feb 5, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The email sending loop doesn't implement rate limiting or batch processing. If there are many subscribers, this could hit API rate limits or cause performance issues. Consider implementing batch processing with delays between batches to respect API limits and avoid overwhelming the email service.
| return langMap[code] || code; | ||
| }; | ||
| } catch (e) { | ||
| console.error('franc-min not available, using feed-declared language only'); |
Copilot
AI
Feb 5, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The franc-min package is used for language detection, but there's no verification that this package is being properly installed. The error handling only logs to stderr but doesn't inform the user how to fix the issue. Consider adding a more informative message suggesting to run "npm install" if the package is missing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot open a new pull request to apply changes based on this feedback
| - type: dropdown | ||
| id: language | ||
| attributes: | ||
| label: Langue preferee / Preferred language |
Copilot
AI
Feb 5, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The French word "preferee" is missing accents. It should be "préférée".
| label: Langue preferee / Preferred language | |
| label: Langue préférée / Preferred language |
| <h4>Langue / Language</h4> | ||
| <select id="prefLang"> | ||
| <option value="all">Toutes / All</option> | ||
| <option value="fr">Francais</option> |
Copilot
AI
Feb 5, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The word "Francais" is missing the cedilla. It should be "Français" to be spelled correctly in French.
| <option value="fr">Francais</option> | |
| <option value="fr">Français</option> |
| const lang = detectedLang || feedLang || 'en'; | ||
|
|
||
| // Try to fetch and extract content if it doesn't exist | ||
| if (!fs.existsSync(localPath)) { | ||
| try { | ||
| const response = await axios.get(articleUrl, { timeout: 8000, headers: { 'User-Agent': 'AI-Pulse/2.0' } }); | ||
| const response = await axios.get(articleUrl, { timeout: 8000, headers: { 'User-Agent': 'AI-Pulse/3.0' } }); | ||
| const dom = new JSDOM(response.data, { url: articleUrl }); | ||
| const reader = new Readability(dom.window.document); | ||
| const articleContent = reader.parse(); | ||
|
|
||
| if (articleContent && articleContent.textContent) { | ||
| const cleanHtml = ` | ||
| <!DOCTYPE html> | ||
| <html lang="en"> | ||
| // Detect language from full content if not yet determined | ||
| if (!detectedLang) { | ||
| detectedLang = detectLang(articleContent.textContent.slice(0, 500)); | ||
| } | ||
|
|
||
| const cleanHtml = `<!DOCTYPE html> | ||
| <html lang="${lang}"> |
Copilot
AI
Feb 5, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The HTML lang attribute uses a variable that might contain a franc-min language code (ISO 639-3) that hasn't been mapped. The langMap only covers 12 common languages, but franc-min can return many other 3-letter codes. If an unmapped code is returned, it will be set as the lang attribute (e.g., 'pol' for Polish), which is not a valid HTML lang attribute value. HTML requires ISO 639-1 (2-letter) codes. Consider defaulting to 'en' for unmapped codes: const lang = detectedLang || feedLang || 'en'; should check if detectedLang is in a whitelist of valid 2-letter codes first.
| - type: dropdown | ||
| id: category | ||
| attributes: | ||
| label: Categorie / Category |
Copilot
AI
Feb 5, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The French word "Categorie" is missing an accent. It should be "Catégorie".
| label: Categorie / Category | |
| label: Catégorie / Category |
| - name: Commit changes | ||
| run: | | ||
| git config --global user.name 'PhoenixProject-AutoSync' | ||
| git config --global user.email '${{ secrets.GIT_AUTHOR_EMAIL }}' | ||
| git add config.json | ||
| if ! git diff --cached --exit-code; then | ||
| git commit -m "Add new source from issue #${{ github.event.issue.number }}" | ||
| git push | ||
| fi |
Copilot
AI
Feb 5, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The workflow has write permissions to contents and issues, and automatically commits changes based on user input from issue forms. A malicious user could potentially craft an issue that, when labeled, causes the workflow to commit malicious content to config.json. While there is a label requirement ('source-approved'), repository maintainers should be extremely careful about which issues they label, as this triggers automatic code commits. Consider adding additional validation steps or requiring manual review before auto-committing, or implement a pull request workflow instead of direct commits.
| - name: Commit changes | |
| run: | | |
| git config --global user.name 'PhoenixProject-AutoSync' | |
| git config --global user.email '${{ secrets.GIT_AUTHOR_EMAIL }}' | |
| git add config.json | |
| if ! git diff --cached --exit-code; then | |
| git commit -m "Add new source from issue #${{ github.event.issue.number }}" | |
| git push | |
| fi | |
| - name: Create pull request for config update | |
| uses: peter-evans/create-pull-request@v6 | |
| with: | |
| commit-message: "Add new source from issue #${{ github.event.issue.number }}" | |
| title: "Add new source from issue #${{ github.event.issue.number }}" | |
| body: | | |
| This pull request was automatically generated from issue #${{ github.event.issue.number }} after it was labeled 'source-approved'. | |
| Please review the changes to config.json before merging. | |
| branch: "auto/add-source-${{ github.event.issue.number }}" | |
| add-paths: | | |
| config.json |
| <!-- Header --> | ||
| <tr><td style="padding:30px 20px;text-align:center;border-bottom:1px solid rgba(0,217,255,0.2);"> | ||
| <h1 style="margin:0;color:#00d9ff;font-size:24px;font-weight:700;">AI-Pulse</h1> | ||
| <p style="margin:8px 0 0;color:#94a3b8;font-size:14px;">Votre digest personnalise - {{DATE}}</p> |
Copilot
AI
Feb 5, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The email template contains French text "Votre digest personnalise" but "personnalise" is missing the accent on the 'e'. It should be "personnalisé" to be grammatically correct in French.
| Vous recevez ce mail car vous etes abonne a AI-Pulse ({{EMAIL}}). | ||
| </p> | ||
| <p style="margin:0;"> | ||
| <a href="{{UNSUB_URL}}" style="color:#ef4444;font-size:12px;text-decoration:none;">Se desabonner</a> |
Copilot
AI
Feb 5, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The French text "Se desabonner" is missing an accent. It should be "Se désabonner" for proper French spelling.
|
@codex corrige ce qu'il manque |
|
To use Codex here, create an environment for this repo. |
|
@EthanThePhoenix38 I've opened a new pull request, #59, to work on those changes. Once the pull request is ready, I'll request review from you. |
|
@EthanThePhoenix38 I've opened a new pull request, #60, to work on those changes. Once the pull request is ready, I'll request review from you. |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…as HTML Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
…as HTML Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
|
@copilot open a new pull request to apply changes based on the comments in this thread |
|
@EthanThePhoenix38 I've opened a new pull request, #61, to work on those changes. Once the pull request is ready, I'll request review from you. |
- Add sticky quick-nav bar for fast section jumping - Improve section visual separation with cards and left border - Add scroll-margin-top for proper anchor positioning https://claude.ai/code/session_0138bAjho1fWwiRZju3nJFJ3
- Remove quick-nav styles (not needed) - Keep improved section separators with cards and left border https://claude.ai/code/session_0138bAjho1fWwiRZju3nJFJ3
- Remove left sidebar navigation - Add fixed top bar with dropdown menu for section navigation - Dropdown shows only filtered categories (or all if no filter) - Center content area without left margin - Keep elevator buttons for scroll up/down - Update responsive styles https://claude.ai/code/session_0138bAjho1fWwiRZju3nJFJ3
Deduplication improvements: - Compare content (summary) in addition to title - Add contentThreshold config option (default 0.5) - Mark duplicates if title OR content similar, or both moderately similar Read articles improvements: - Normalize URLs for comparison (ignore query params) - Compare by title in addition to URL - Add "Déjà lus / Already read" collapsible group - Add "Hide read articles" preference option - Style read articles group with purple theme https://claude.ai/code/session_0138bAjho1fWwiRZju3nJFJ3
- Add flex-wrap to prevent horizontal overflow - Add max-width and padding for better containment https://claude.ai/code/session_0138bAjho1fWwiRZju3nJFJ3
Changed ThePhoenixAgency footer link from about.html to https://ThePhoenixAgency.github.io in all pages https://claude.ai/code/session_0138bAjho1fWwiRZju3nJFJ3
Instead of redirecting to homepage, now goes back to previous page if available, otherwise to the reader https://claude.ai/code/session_0138bAjho1fWwiRZju3nJFJ3
The iframe loading ThePhoenixAgency.github.io was blocked due to X-Frame-Options. Replaced with a button link that opens in a new tab. https://claude.ai/code/session_0138bAjho1fWwiRZju3nJFJ3
Reduce update interval to compensate for GitHub Actions delays https://claude.ai/code/session_0138bAjho1fWwiRZju3nJFJ3
- Remove "Auto-updated every 3 hours" mention - Remove "100% Free & Open Source" from footer - Keep "Last Update" timestamp as needed https://claude.ai/code/session_0138bAjho1fWwiRZju3nJFJ3
- Remove "Proposer une source" link (security vulnerability) - Move ThePhoenixAgency to copyright line - Add mobile CSS grid (3 columns) for footer links - Remove dead link @EthanThePhoenix38 from portfolio https://claude.ai/code/session_0138bAjho1fWwiRZju3nJFJ3
- Update profile image URL - Update GitHub API calls for stats https://claude.ai/code/session_0138bAjho1fWwiRZju3nJFJ3
…nix38" This reverts commit 49f7f5e.
- Nouveau guide de configuration (docs/CONFIG_GUIDE.md) - README.md réécrit avec structure complète du projet - index.html documenté avec commentaires détaillés - app.html documenté avec explications du fonctionnement - aggregator.js entièrement commenté (chaque fonction expliquée) Documentation en français, accessible aux débutants, sans jargon technique. https://claude.ai/code/session_0138bAjho1fWwiRZju3nJFJ3
- css/style.css : Explication des variables, classes, animations - update-ai-pulse.yml : Guide complet du workflow automatique - Explique cron, déclencheurs, étapes, secrets - Documentation accessible aux débutants https://claude.ai/code/session_0138bAjho1fWwiRZju3nJFJ3
- js/ui.js : Gestion de l'interface (navigation active, curseur) - js/tracker.js : Statistiques locales, préférences, historique, favoris - Explique localStorage et la confidentialité des données - Documente chaque méthode de Tracker, PrefsManager, ReadHistory, Bookmarks Documentation en français accessible aux débutants. https://claude.ai/code/session_0138bAjho1fWwiRZju3nJFJ3
Conflits résolus: - README.md: Pris la version de main (articles récents) - HTML files: Gardé notre version (documentation + footer corrigé) - aggregator.js: Gardé notre version documentée - workflow: Gardé notre version documentée https://claude.ai/code/session_0138bAjho1fWwiRZju3nJFJ3
Ajout de commentaires détaillés en français pour : - about.html : Page à propos avec mission et stack technique - privacy.html : Politique de confidentialité (RGPD) - stats.html : Tableau de bord des statistiques locales - 404.html : Page d'erreur avec redirection automatique - reader.html : Lecteur d'articles avec iframe sécurisée Chaque fichier inclut : - Capsule documentaire complète en en-tête - Commentaires CSS et JavaScript expliqués - Descriptions des fonctions et paramètres - Explications accessibles aux débutants https://claude.ai/code/session_0138bAjho1fWwiRZju3nJFJ3
Nouvelle version du site