fn_b/app/Services/ArticleRecommendations.php

67 lines
3.4 KiB
PHP
Raw Permalink 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
namespace App\Services;
use App\Models\Articles;
final class ArticleRecommendations
{
public const SLOT = '<!-- fna-airport-recommendations -->';
public static function providers(): array
{
if (! config('airport_recommendations.enabled')) return [];
$providers = array_filter(config('airport_recommendations.providers', []), fn ($p) =>
($p['enabled'] ?? false) && filter_var($p['url'] ?? '', FILTER_VALIDATE_URL)
&& in_array(parse_url($p['url'], PHP_URL_SCHEME), ['https', 'http'], true));
uasort($providers, fn ($a, $b) => ($a['order'] ?? 0) <=> ($b['order'] ?? 0));
return $providers;
}
/** Presentation only: article storage, API receipts and subscription bytes stay intact. */
public static function prepare(Articles $article): array
{
$html = $article->sContent ?? '';
$category = $article->oCategory;
$slug = $category?->sSlug;
$scope = in_array($slug, ['freenode', 'shadowrocketids', 'advanced', 'client'], true)
|| $category?->oParent?->sSlug === 'client';
$enabled = $scope && self::providers() !== [];
if (! $enabled) return ['body' => $html, 'enabled' => false, 'inline' => false];
$inline = false;
if (in_array($slug, ['freenode', 'shadowrocketids'], true)) {
// Match the known legacy heading and its own Fuyun link, bounded by the next heading.
if (preg_match('~(?:<p\b[^>]*>\s*)?<h[2-6]\b[^>]*>\s*机场推荐\s*[-—-::]*\s*福云\s*[::]?\s*</h[2-6]>~u', $html, $head, PREG_OFFSET_CAPTURE)) {
$start = $head[0][1];$from = $start + strlen($head[0][0]);
$tail = substr($html, $from);
if (preg_match('~<h[1-6]\b~i', $tail, $next, PREG_OFFSET_CAPTURE)) $tail = substr($tail, 0, $next[0][1]);
preg_match_all('~<a\b[^>]*>.*?</a>~si', $tail, $links, PREG_OFFSET_CAPTURE);
foreach ($links[0] as [$link, $offset]) {
$text = html_entity_decode(strip_tags($link), ENT_QUOTES | ENT_HTML5, 'UTF-8');
if (! str_contains($text, '机场官网') || ! str_contains($text, '福云')) continue;
$end = $from + $offset + strlen($link);
if (preg_match('~\A(?:\s|</(?:b|strong|p)>)*~i', substr($html, $end), $closing)) $end += strlen($closing[0]);
$html = substr($html, 0, $start).self::SLOT.substr($html, $end);
$inline = true;
break;
}
}
}
if ($slug === 'advanced') {
$anchor = '#'.config('airport_recommendations.anchor');
if (! str_contains($html, $anchor)) {
$jump = '<p class="airport-recommendation-jump">如果需要更稳定的线路,可以从文末的<a href="'.e($anchor).'">机场推荐</a>中选择。</p>';
$inserted = false;
$html = preg_replace_callback('~<p\b[^>]*>.*?</p>~si', function ($m) use (&$inserted, $jump) {
if ($inserted || ! preg_match('/免费节点|订阅|稳定|机场|线路/u', strip_tags($m[0]))) return $m[0];
$inserted = true;
return $m[0].$jump;
}, $html);
if (! $inserted) $html .= $jump;
}
}
return ['body' => $html, 'enabled' => true, 'inline' => $inline];
}
}