snippets/cron/generate_indieblog_daily_rss.php

115 lines
6.4 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
// Configuration
$jsonUrl = "https://indieblog.page/export";
$rssFile = "/app/data/public/indieblogv2_feed.xml";
$cacheFile = "/app/data/cache/indieblogv2_cache.txt";
$cutoffDate = strtotime('-10 days'); // Seulement les 10 derniers jours
// Liste des mots/expressions à exclure (insensibilité à la casse activée avec `(?i)`)
$excludePatterns = [
'(?i)(azure|powershell|rimworld|Olympique|Olympic|Paris 2024|sportif|windows|twitter|freebsd|mastodon)',
'(?i)(metalcore|deathcore|death metal|black metal|death punk|deathpunk|djent|blackmetal|blackened)',
'(?i)(JO|foot|automated archives|announcing systemd)',
'(?i)(guerre|bluesky|combat|terror|craindre|fear|décliner|declin|climat|inégalité|fuite|ont fui|a fui|musk|tesla|voitures|dying|indifference|feux|abandon|chômage|scam|arnaque|fraud|craindre|inquiét|openai|victime|violence|violent|violemm|pénurie|impôt|taxes|deuil|chasse|bombes|a perdu|vide exist|fake|poursuivi|subir|pauvre|riches|politique|politic|Macron|marine le pen|michel barnier|zemmour|Matignon|tremble|aucun n\'a|décès|crise|difficile|marre|action contre|irrecevable|pollution|élimination|pourrai|tourisme|mauvaise humeur|stress|dangereux|détruit|insupportable|de droite|souffre|chaleur|cyber|attaque|extrême.droite|risque|travail|offensi|sacrif|canard pc|photos|videos|crisis|trump|nocif|negatif|négative|pollution|poids|plainte|escro|usurp|contamin|advertising|anniversary edition|en voyage|extermin|kamikaze|nationalis|mittal|est prévu pour 202|is planned for|due in 202|incendies|fires|inondation|ligue 1|le suicide|gaza|terreur|capitalism|attentat|calvaire|calvitie|la gauche|esclave|paris|ségrégation|athlète|précarité|Hezbollah|Israël|morts|frappes|marine lepen|dérives|un drame|sans-abri|protobuf|paralympique|nazi|mélenchon|ministre|Review: (?:[1-3]\.\d|[1-2]\.\d|3\.0)/5\.0|licenciement|misère|du jamais vu|il faut|Palestiniens|israél|palestine|vaccin|mpox|épidémie|violeur|rapist|pédo|criminel|poutine|le RN |réseau social x|gendarme|policier|kurde|condamnation|perquisition|incendi|gabriel attal|attaqué|justice|tués par|tué par|snapdragon|qualcomm|porn|barnier|occultes|désinformation|immigration|racism|spacex|islam|condamné|de viol|pour viol)',
// Années au début, à la fin, ou précédées de "at/in/of", ou au milieu des titres spécifiques
'(?i)(\b\d{4}\b|(?:\b(?:at|in|of)\s\d{4}\b)|(?:\b\d{4}\b\.$|\(\d{4}\)))',
'(?i)(^202\d\b|\b202\d$)', // Titre commençant ou terminant par 202x
'(?i)(\bin\s202\d|\bat\s202\d)', // "in/at 202x"
'(?i)(my\s.*\b202\d\b)', // Titres contenant "My ... 202x"
// Exclude specific character sets
'[Α-ωА-яäöüßÄÖÜẞ\u4E00-\u9FFF\u3000-\u303F\u3040-\u309F\u30A0-\u30FF\uFF00-\uFFEF]',
// Week notes et expressions relatives au nouvel an
'(?i)(week notes|weekly|weeknote|digest)',
'(?i)(new year|yearly)',
// Mentions de jours ou mois (en anglais), ou relatifs au temps
'(?i)(monday|tuesday|wednesday|thursday|friday|saturday|sunday)',
'(?i)(january|february|march|april|may|june|july|august|september|october|november|december)',
'(?i)(today|yesterday|tomorrow|this year|this week)',
// "Merry Christmas"
'(?i)(merry christmas)'
];
// Corrige les barres obliques pour qu'elles ne cassent pas `preg_match`
$excludePatterns = array_map(fn($pattern) => str_replace('/', '\/', $pattern), $excludePatterns);
// Crée une expression régulière unique pour matcher toutes les exclusions
$excludeRegex = '/' . implode('|', $excludePatterns) . '/i';
@mkdir(dirname($rssFile), 0777, true);
@mkdir(dirname($cacheFile), 0777, true);
$jsonData = @file_get_contents($jsonUrl);
if (!$jsonData) die("Error: Unable to fetch JSON from $jsonUrl\n");
$data = json_decode($jsonData, true);
if (!$data) die("Error: Invalid JSON data from $jsonUrl\n");
$includedIds = file_exists($cacheFile) ? file($cacheFile, FILE_IGNORE_NEW_LINES) : [];
// Filtrer les articles récents, non déjà inclus, et ne contenant pas de termes exclus
$newItems = array_filter($data, function ($item) use ($includedIds, $cutoffDate, $excludeRegex) {
return isset($item['published'], $item['itemid'], $item['itemtitle']) &&
!in_array($item['itemid'], $includedIds) &&
$item['published'] >= $cutoffDate &&
!preg_match($excludeRegex, $item['itemtitle']); // Exclure si le titre contient des termes interdits
});
usort($newItems, fn($a, $b) => $b['published'] <=> $a['published']);
$groupedByDay = [];
foreach ($newItems as $item) {
$day = isset($item['published']) && is_numeric($item['published'])
? date('Y-m-d', $item['published'])
: date('Y-m-d');
$groupedByDay[$day][] = $item;
}
$rss = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"></rss>');
$channel = $rss->addChild('channel');
$channel->addChild('title', 'IndieBlog Feed (Filtered, Last 10 Days)');
$channel->addChild('link', 'https://indieblog.page/');
$channel->addChild('description', 'RSS feed of articles from the last 10 days, excluding unwanted topics');
$channel->addChild('language', 'en');
$newEntries = [];
foreach ($groupedByDay as $day => $items) {
$rssItem = $channel->addChild('item');
$rssItem->addChild('title', "Links for $day");
$rssItem->addChild('link', "https://indieblog.page/$day#" . md5(json_encode($items)));
$rssItem->addChild('guid', "https://indieblog.page/$day#" . md5(json_encode($items)));
$rssItem->addChild('pubDate', date(DATE_RSS, strtotime($day)));
$description = "<ul>";
foreach ($items as $item) {
$postTitle = htmlspecialchars($item['itemtitle'] ?? 'No title', ENT_XML1);
$postUrl = htmlspecialchars($item['itemurl'] ?? '#', ENT_XML1);
$blogTitle = htmlspecialchars($item['feedtitle'] ?? 'Unknown Blog', ENT_XML1);
$blogUrl = htmlspecialchars($item['feedurl'] ?? '#', ENT_XML1);
$description .= "<li><a href=\"$postUrl\">$postTitle</a> (<a href=\"$blogUrl\">$blogTitle</a>)</li>";
$newEntries[] = $item['itemid'];
}
$description .= "</ul>";
addCData($rssItem->addChild('description'), $description);
}
if (!empty($newEntries)) {
file_put_contents($cacheFile, implode("\n", array_merge($includedIds, $newEntries)));
}
$rss->asXML($rssFile);
function addCData(SimpleXMLElement $node, $content)
{
$domNode = dom_import_simplexml($node);
$domOwner = $domNode->ownerDocument;
$domNode->appendChild($domOwner->createCDATASection($content));
}