Skip to content

Claude/configurable news reader g1 gdx#57

Merged
EthanThePhoenix38 merged 24 commits intomainfrom
claude/configurable-news-reader-G1Gdx
Feb 14, 2026
Merged

Claude/configurable news reader g1 gdx#57
EthanThePhoenix38 merged 24 commits intomainfrom
claude/configurable-news-reader-G1Gdx

Conversation

@EthanThePhoenix38
Copy link
Member

Nouvelle version du site

… 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
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a 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".

@EthanThePhoenix38
Copy link
Member Author

@copilot Corrige le check qui n'a pas marché.

Copy link
Contributor

Copilot AI commented Feb 5, 2026

@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.

Copy link
Contributor

Copilot AI left a 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.

Comment on lines 401 to 458
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}`);
}
}
}
Copy link

Copilot AI Feb 5, 2026

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.

Copilot uses AI. Check for mistakes.
return langMap[code] || code;
};
} catch (e) {
console.error('franc-min not available, using feed-declared language only');
Copy link

Copilot AI Feb 5, 2026

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.

Copilot uses AI. Check for mistakes.
Copy link
Member Author

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
Copy link

Copilot AI Feb 5, 2026

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".

Suggested change
label: Langue preferee / Preferred language
label: Langue préférée / Preferred language

Copilot uses AI. Check for mistakes.
<h4>Langue / Language</h4>
<select id="prefLang">
<option value="all">Toutes / All</option>
<option value="fr">Francais</option>
Copy link

Copilot AI Feb 5, 2026

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.

Suggested change
<option value="fr">Francais</option>
<option value="fr">Français</option>

Copilot uses AI. Check for mistakes.
Comment on lines 139 to 156
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}">
Copy link

Copilot AI Feb 5, 2026

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.

Copilot uses AI. Check for mistakes.
- type: dropdown
id: category
attributes:
label: Categorie / Category
Copy link

Copilot AI Feb 5, 2026

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".

Suggested change
label: Categorie / Category
label: Catégorie / Category

Copilot uses AI. Check for mistakes.
Comment on lines +114 to +122
- 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
Copy link

Copilot AI Feb 5, 2026

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.

Suggested change
- 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

Copilot uses AI. Check for mistakes.
<!-- 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>
Copy link

Copilot AI Feb 5, 2026

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.

Copilot uses AI. Check for mistakes.
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>
Copy link

Copilot AI Feb 5, 2026

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.

Copilot uses AI. Check for mistakes.
@EthanThePhoenix38
Copy link
Member Author

@codex corrige ce qu'il manque

@chatgpt-codex-connector
Copy link

To use Codex here, create an environment for this repo.

Copy link
Contributor

Copilot AI commented Feb 5, 2026

@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.

Copy link
Contributor

Copilot AI commented Feb 5, 2026

@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.

EthanThePhoenix38 and others added 3 commits February 5, 2026 20:09
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>
@EthanThePhoenix38
Copy link
Member Author

@copilot open a new pull request to apply changes based on the comments in this thread

Copy link
Contributor

Copilot AI commented Feb 5, 2026

@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.

@EthanThePhoenix38 EthanThePhoenix38 marked this pull request as draft February 5, 2026 19:15
- 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
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
- 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
@github-actions github-actions bot added the documentation Improvements or additions to documentation label Feb 10, 2026
- 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
- 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
Ajout de commentaires détaillés en français pour :
- Variables CSS et palette de couleurs
- Section héros avec dégradé et liens sociaux
- Grille de statistiques GitHub temps réel
- Chargement dynamique des projets via API
- Protection XSS avec validation des URLs

Documentation des fonctions JavaScript :
- fetchGitHubStats() : Récupération des données utilisateur
- loadFeaturedProjects() : Affichage des projets populaires
- escapeHtml() : Protection contre les injections

https://claude.ai/code/session_0138bAjho1fWwiRZju3nJFJ3
@EthanThePhoenix38 EthanThePhoenix38 marked this pull request as ready for review February 14, 2026 11:24
@EthanThePhoenix38 EthanThePhoenix38 merged commit dfb03b6 into main Feb 14, 2026
9 checks passed
EthanThePhoenix38 added a commit that referenced this pull request Feb 14, 2026
…tibility (#61)

Addresses all actionable review feedback from PR #57 focusing on i18n
quality, input validation, error handling, and browser compatibility.

## French Localization
- Fixed missing accents throughout: `Français`, `Cybersécurité`,
`Générale`, `préférée`, `Fréquence`, `Réinitialiser`, etc.
- Updated config.json, issue templates, email templates, and UI strings

## Input Validation & Security

**Aggregator (`src/aggregator.js`)**
- Config loading with actionable error messages on missing/malformed
files
- Email validation (RFC-compliant regex) before sending
- API key format validation with sanitized error output
- Deduplication threshold type checking
- HTML lang attribute validation (ISO 639-1 codes only)

**Workflows**
- Email validation with abuse detection (max @ symbol threshold)
- RSS URL format validation
- Input sanitization for source names and tags (XSS prevention)

```javascript
// Before: silent crash on missing config
const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8'));

// After: actionable error
try {
  if (!fs.existsSync(CONFIG_PATH)) {
    console.error(`ERROR: Configuration file not found at ${CONFIG_PATH}`);
    console.error('Please ensure config.json exists in the repository root.');
    process.exit(1);
  }
  // ...
} catch (e) {
  console.error(`ERROR: Failed to load or parse config.json: ${e.message}`);
  process.exit(1);
}
```

## Browser Compatibility
- `localStorage` availability checks before all operations
- Try-catch wrappers with quota overflow recovery
- `IntersectionObserver` feature detection with graceful degradation
- `ReadHistory.markRead` safety guard against undefined

## Rate Limiting & Configuration
- Email sending rate limit (100ms delay between sends)
- Configurable sender via `EMAIL_FROM` env var with dev domain warning
- Constants extracted for magic numbers (`VALID_LANG_CODES`,
`MAX_SOURCE_NAME_LENGTH`, etc.)

## Security Scan
✅ CodeQL: 0 alerts

<!-- START COPILOT CODING AGENT TIPS -->
---

💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants