From 1182bcf50577d6da601a956a76a1eaa7d51f1094 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 18 Feb 2026 13:52:46 +0000 Subject: [PATCH 1/3] feat: Add Archive.ph and paywall bypass services for better article extraction --- src/aggregator.js | 68 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/src/aggregator.js b/src/aggregator.js index 68647f5a5..3a4f600ff 100644 --- a/src/aggregator.js +++ b/src/aggregator.js @@ -260,6 +260,14 @@ function addUTMParams(url, category = 'general') { if (mediumHosts.includes(hostname)) { url = `https://freedium.cloud/${url}`; } + + // Liste des domaines avec paywalls stricts + const paywalledHosts = ['ft.com', 'wsj.com', 'economist.com', 'bloomberg.com', 'investing.com']; + + // Ajouter Archive.ph en query parameter pour fallback + if (paywalledHosts.some(host => hostname.includes(host))) { + // On ne change pas l'URL ici, on l'utilisera comme fallback + } } catch (e) { // Erreur de parsing URL, on continue sans modification } @@ -1408,9 +1416,42 @@ async function processArticle(article, sourceName, tags, category, feedLang) { } const lang = detectedLang || feedLang || 'en'; + // Essayer d'utiliser Archive.ph ou autres services de bypass + const tryPaywallBypass = async (url) => { + const bypassServices = [ + { + name: 'archive.ph', + transform: (u) => `https://archive.ph/?url=${encodeURIComponent(u)}` + }, + { + name: 'scribe.rip', + transform: (u) => u.includes('medium.com') ? u.replace('medium.com', 'scribe.rip') : null + }, + { + name: 'web.archive.org', + transform: (u) => `https://web.archive.org/web/*/${u}` + } + ]; + + for (const service of bypassServices) { + const bypassUrl = service.transform(url); + if (!bypassUrl) continue; + try { + const response = await axios.get(bypassUrl, { + timeout: 5000, + headers: { 'User-Agent': 'AI-Pulse/3.0' } + }); + return { success: true, html: response.data, service: service.name }; + } catch (e) { + // Continuer vers le service suivant + } + } + return { success: false }; + }; + const writeFallbackLocalArticle = () => { const safeTitle = sanitizeText(article.title) || 'Untitled'; - const safeSummary = smartTruncate(cleanupNoiseText(rawSummary || ''), 1200) || 'Summary unavailable for this article.'; + const safeSummary = smartTruncate(cleanupNoiseText(rawSummary || ''), 1200) || (rawSummary ? sanitizeText(rawSummary) : 'Summary unavailable for this article.'); const fallbackHtml = ` @@ -1518,11 +1559,26 @@ async function processArticle(article, sourceName, tags, category, feedLang) { if (articleContent && articleContent.textContent) { if (isPaywallText(articleContent.textContent)) { - writeFallbackLocalArticle(); - } else { - if (isLikelyBoilerplateExtraction(articleContent.textContent)) { - writeFallbackLocalArticle(); + // Essayer les services de bypass avant de renoncer + const bypassResult = await tryPaywallBypass(resolvedArticleUrl); + if (bypassResult.success) { + const bypassDom = createSafeDom(bypassResult.html, resolvedArticleUrl); + const bypassReader = new Readability(bypassDom.window.document); + const bypassContent = bypassReader.parse(); + if (bypassContent && bypassContent.textContent && !isPaywallText(bypassContent.textContent) && bypassContent.textContent.length > 200) { + articleContent = bypassContent; + // Continuer le traitement normal avec le contenu bypassed + } else { + writeFallbackLocalArticle(); + } + } else { + writeFallbackLocalArticle(); + } } else { + // Contenu normal sans paywall + if (isLikelyBoilerplateExtraction(articleContent.textContent)) { + writeFallbackLocalArticle(); + } else { if (!computedSummary || computedSummary.trim().length < 20) { computedSummary = trimPromotionalTailText(cleanupNoiseText(sanitizeText(articleContent.textContent.slice(0, 1400)))); } @@ -1633,10 +1689,10 @@ async function processArticle(article, sourceName, tags, category, feedLang) { // Sauvegarder le fichier HTML localement fs.writeFileSync(localPath, cleanHtml); + } } } } - } } catch (e) { if (!shouldSuppressExtractionLog(resolvedArticleUrl, e)) { console.error(` Could not extract content for: ${articleUrl}`); From 7673d63c2e2fbdc1337440a5f70d4d9832f5ea23 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 18 Feb 2026 18:57:37 +0000 Subject: [PATCH 2/3] fix: Improve security branch sync in dependabot workflow - Create security branch from main if it doesn't exist - Reset security to main if more than 50 commits behind - Handle merge conflicts by resetting to main first - Use force-with-lease for safer push https://claude.ai/code/session_0138bAjho1fWwiRZju3nJFJ3 --- .github/workflows/dependabot-secure-flow.yaml | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/.github/workflows/dependabot-secure-flow.yaml b/.github/workflows/dependabot-secure-flow.yaml index 4438faf82..90ad1cb77 100755 --- a/.github/workflows/dependabot-secure-flow.yaml +++ b/.github/workflows/dependabot-secure-flow.yaml @@ -43,12 +43,25 @@ jobs: with: fetch-depth: 0 - - name: Ensure security branch exists + - name: Ensure security branch exists and is up to date run: | - git fetch origin security 2>/dev/null || git switch --create security - git push origin security || true + git fetch origin main + if git fetch origin security 2>/dev/null; then + # Security branch exists, check if it's behind main + git checkout security + BEHIND=$(git rev-list --count HEAD..origin/main) + if [ "$BEHIND" -gt 50 ]; then + echo "Security branch is $BEHIND commits behind main. Resetting to main..." + git reset --hard origin/main + fi + else + # Security branch doesn't exist, create from main + echo "Creating security branch from main..." + git checkout -b security origin/main + fi + git push origin security --force-with-lease || git push origin security || true - - name: Merge dependabot changes to dependencies branch + - name: Merge dependabot changes to security branch run: | git config --global user.name 'github-actions[bot]' git config --global user.email 'github-actions[bot]@users.noreply.github.com' @@ -56,9 +69,16 @@ jobs: # Fetch the PR branch git fetch origin ${{ github.head_ref }}:${{ github.head_ref }} || true - # Switch to security and merge - git switch security - git merge origin/${{ github.head_ref }} --no-edit || true + # Switch to security branch + git checkout security || git checkout -b security origin/main + + # Try to merge, abort if conflicts + if ! git merge origin/${{ github.head_ref }} --no-edit; then + echo "Merge conflict detected, aborting and retrying with main as base..." + git merge --abort || true + git reset --hard origin/main + git merge origin/${{ github.head_ref }} --no-edit || true + fi # Push to security git push origin security From fe0fac21d47fc6378344b76f2c281fa9a877ab73 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 18 Feb 2026 19:41:08 +0000 Subject: [PATCH 3/3] fix: Remove extra buttons and harmonize elevator arrows in reader.html MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Removed "Retour", "Accueil" and "Page précédente" buttons - Elevator arrows now match the style from readme-viewer.html: - Round buttons (border-radius: 50%) - Cyan color (#00d9ff) - Same hover effects - Position at bottom-right https://claude.ai/code/session_0138bAjho1fWwiRZju3nJFJ3 --- reader.html | 125 +++++++++------------------------------------------- 1 file changed, 22 insertions(+), 103 deletions(-) diff --git a/reader.html b/reader.html index 17c51c657..42e4b7f8e 100644 --- a/reader.html +++ b/reader.html @@ -117,81 +117,42 @@ } #view-article .iframe-container { - padding-top: 48px; + padding-top: 0; } - .article-topbar { - position: absolute; - top: 8px; - left: 10px; - right: 10px; - z-index: 2147483647; - display: flex; - gap: 8px; - align-items: center; - pointer-events: auto; - } - - .article-topbar-btn { - border: 1px solid rgba(255, 255, 255, 0.2); - background: rgba(15, 23, 42, 0.84); - color: #fff; - font-size: 12px; - font-weight: 600; - border-radius: 10px; - padding: 8px 12px; - cursor: pointer; - } - - .article-topbar-btn:hover { - background: rgba(15, 23, 42, 0.95); - } - - .back-to-previous { - position: absolute; - top: 12px; - left: 12px; - z-index: 40; - background: rgba(15, 23, 42, 0.78); - color: #fff; - border: 1px solid rgba(255, 255, 255, 0.18); - padding: 8px 12px; - border-radius: 10px; - font-size: 13px; - cursor: pointer; - backdrop-filter: blur(4px); - } - - .back-to-previous:hover { - background: rgba(15, 23, 42, 0.9); - } .reader-elevator { position: fixed; - right: 12px; - top: 58px; - bottom: auto; + right: 20px; + bottom: 20px; z-index: 2147483646; display: flex; flex-direction: column; - gap: 8px; + gap: 10px; } .reader-elevator-btn { - width: 36px; - height: 36px; - border: 1px solid rgba(255, 255, 255, 0.22); - border-radius: 10px; - background: rgba(15, 23, 42, 0.82); - color: #fff; + width: 40px; + height: 40px; + background: rgba(255, 255, 255, 0.05); + border: 1px solid rgba(0, 217, 255, 0.2); + border-radius: 50%; + color: #00d9ff; + display: flex; + align-items: center; + justify-content: center; cursor: pointer; - font-size: 16px; - line-height: 1; - backdrop-filter: blur(4px); + backdrop-filter: blur(5px); + transition: all 0.3s ease; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); + font-size: 1.2rem; } .reader-elevator-btn:hover { - background: rgba(15, 23, 42, 0.95); + background: #00d9ff; + color: white; + transform: scale(1.1); + box-shadow: 0 0 15px rgba(0, 217, 255, 0.5); } .article-mobile-footer { @@ -332,16 +293,9 @@ padding: 12px 16px; } - .back-to-previous { - top: 10px; - font-size: 12px; - padding: 7px 10px; - } - .reader-elevator { right: 10px; - top: 56px; - bottom: auto; + bottom: 10px; } .article-mobile-footer { @@ -393,11 +347,6 @@
-
- - -
-

Chargement de l'article...

@@ -875,36 +824,6 @@

Impossible de charger l'article

}); } - // Back to previous page in navigation history (fallback to list). - const backToPreviousBtn = document.getElementById('backToPreviousBtn'); - const articleBackBtn = document.getElementById('articleBackBtn'); - const articleHomeBtn = document.getElementById('articleHomeBtn'); - - function navigateBackOrHome() { - if (window.history.length > 1) { - window.history.back(); - return; - } - const fallbackUrl = new URL(window.location.href); - fallbackUrl.search = ''; - fallbackUrl.hash = ''; - window.location.assign(fallbackUrl.toString()); - } - - if (backToPreviousBtn) { - backToPreviousBtn.addEventListener('click', navigateBackOrHome); - } - if (articleBackBtn) { - articleBackBtn.addEventListener('click', navigateBackOrHome); - } - if (articleHomeBtn) { - articleHomeBtn.addEventListener('click', () => { - const fallbackUrl = new URL(window.location.href); - fallbackUrl.search = ''; - fallbackUrl.hash = ''; - window.location.assign(fallbackUrl.toString()); - }); - } function scrollArticleFrame(direction) { const iframe = document.getElementById('articleFrame');