diff --git a/NIKKENewsBridge.php b/NIKKENewsBridge.php new file mode 100644 index 0000000..9c7ed6d --- /dev/null +++ b/NIKKENewsBridge.php @@ -0,0 +1,232 @@ + array( + 'name' => 'Category', + 'type' => 'list', + 'values' => array( + 'All' => 'all', + 'Important' => 'important', + 'News' => 'news', + 'Notice' => 'notice' + ), + 'required' => true, + 'defaultValue' => 'all' + ), + 'lang' => array( + 'name' => 'Language', + 'type' => 'list', + 'values' => array( + 'English' => 'en', + 'Deutsch' => 'de', + 'Français' => 'fr', + '日本語' => 'ja', + '한국어' => 'ko', + 'ไทย' => 'th' + ), + 'required' => true, + 'defaultValue' => 'en' + ), + 'limit' => array( + 'name' => 'Limit', + 'type' => 'number', + 'required' => false, + 'defaultValue' => 10 + ) + ) + ); + + private $apiUrl = 'https://na-community.playerinfinite.com/api/gpts.information_feeds_svr.InformationFeedsSvr/GetContentByLabel'; + private $apiUrlDetail = 'https://na-community.playerinfinite.com/api/gpts.information_feeds_svr.InformationFeedsSvr/GetContentInfoById'; + private $pageUrl = self::URI; + + public function getURI() + { + return $this->pageUrl; + } + + public function collectData() + { + $category = $this->getInput('category'); + $lang = $this->getInput('lang'); + $limit = $this->getInput('limit') ?? 10; + + // Map category to label IDs + $labelConfig = $this->getLabelConfig($category); + + $allItems = []; + + // If "all" is selected, fetch from multiple labels + if ($category === 'all') { + $seenIds = []; + foreach ($labelConfig as $config) { + $items = $this->fetchNews($config['primary'], $config['secondary'], $lang, $limit); + foreach ($items as $item) { + // Use URI as unique identifier to prevent duplicates + if (!isset($seenIds[$item['uri']])) { + $seenIds[$item['uri']] = true; + $allItems[] = $item; + } + } + } + // Sort by timestamp and limit + usort($allItems, function ($a, $b) { + return $b['timestamp'] <=> $a['timestamp']; + }); + $allItems = array_slice($allItems, 0, $limit); + } else { + $config = $labelConfig[0]; + $allItems = $this->fetchNews($config['primary'], $config['secondary'], $lang, $limit); + } + + $this->items = $allItems; + } + + private function getLabelConfig($category) + { + switch ($category) { + case 'important': + return [['primary' => 308, 'secondary' => 494]]; + case 'news': + return [['primary' => 309, 'secondary' => 496]]; + case 'notice': + return [['primary' => 309, 'secondary' => 892]]; + case 'all': + return [ + ['primary' => 308, 'secondary' => 494], // Important + ['primary' => 309, 'secondary' => 496], // News + ['primary' => 309, 'secondary' => 892] // Notice + ]; + default: + returnClientError('Invalid category: ' . $category); + } + } + + private function fetchNews($primaryLabelId, $secondaryLabelId, $lang, $limit) + { + $requestBody = json_encode([ + 'language' => [$lang], + 'gameid' => '16', + 'offset' => 0, + 'get_num' => $limit, + 'ext_info_type_list' => [0, 1, 2], + 'secondary_label_id' => $secondaryLabelId, + 'primary_label_id' => $primaryLabelId + ]); + + $headers = [ + 'Content-Type: application/json;charset=utf-8', + 'x-areaid: na', + 'x-gameid: 16', + 'x-language: ' . $lang, + 'x-source: pc_web' + ]; + + $opts = [ + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_POSTFIELDS => $requestBody, + CURLOPT_HTTPHEADER => $headers + ]; + + $json = getContents($this->apiUrl, $headers, $opts) + or returnServerError('Could not fetch NIKKE news.'); + + $data = json_decode($json, true); + + if (!isset($data['data']['info_content'])) { + returnServerError('Invalid response structure.'); + } + + $items = []; + foreach ($data['data']['info_content'] as $element) { + $contentId = $element['content_id'] ?? ''; + $fatherContentId = $element['father_content_id'] ?? $contentId; + $title = $element['title'] ?? 'Untitled'; + $timestamp = isset($element['pub_timestamp']) ? (int)$element['pub_timestamp'] : time(); + $picUrls = $element['pic_urls'] ?? []; + + // Fetch full content + $fullContent = $this->fetchFullContent($fatherContentId, $lang); + + // Prepend image to content if available + if (!empty($picUrls)) { + $imageUrl = $picUrls[0]; + $imageHtml = '

' . htmlspecialchars($title) . '

'; + $fullContent = $imageHtml . $fullContent; + } + + // Construct article URI + $uri = 'https://nikke-en.com/newsdetail.html?content_id=' . $contentId; + + $item = [ + 'uri' => $uri, + 'title' => $title, + 'timestamp' => $timestamp, + 'content' => $fullContent, + 'author' => 'NIKKE Team' + ]; + + // Add image as enclosure + if (!empty($picUrls)) { + $item['enclosures'] = [$picUrls[0]]; + } + + $items[] = $item; + } + + return $items; + } + + private function fetchFullContent($fatherContentId, $lang) + { + $requestBody = json_encode([ + 'father_content_id' => $fatherContentId + ]); + + $headers = [ + 'Content-Type: application/json;charset=utf-8', + 'x-areaid: na', + 'x-gameid: 16', + 'x-language: ' . $lang, + 'x-source: pc_web' + ]; + + $opts = [ + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_POSTFIELDS => $requestBody, + CURLOPT_HTTPHEADER => $headers + ]; + + $json = getContents($this->apiUrlDetail, $headers, $opts); + + if (!$json) { + // Fallback to empty content if detail fetch fails + return ''; + } + + $data = json_decode($json, true); + + if (!isset($data['data']['content'])) { + return ''; + } + + // Return the HTML content + return $data['data']['content']; + } +} diff --git a/README.md b/README.md index 2c1484a..fe1fd01 100644 --- a/README.md +++ b/README.md @@ -205,6 +205,18 @@ Diese Sammlung enthält verschiedene Bridge-Implementierungen für RSS-Bridge, u - **Parameter**: - **Ort**: Wähle einen Ort aus einer Region (z.B. Bad Kissingen, Schweinfurt, Würzburg usw.) +### [NIKKE News Bridge](https://bridge.ponywave.de/#bridge-NIKKENewsBridge) (Von Akamaru) +- **Beschreibung**: Zeigt die neuesten Nachrichten von GODDESS OF VICTORY: NIKKE +- **Parameter**: + - **Category**: All, Important, News, Notice + - **Language**: English, Deutsch, Français, 日本語, 한국어, ไทย + - **Limit** (optional): Maximale Anzahl an News-Items (Standard: 10) +- **Hinweise**: + - Vollständiger HTML-Content für jeden Artikel + - Cover-Bilder werden im Content und als Enclosure angezeigt + - Automatische Deduplizierung bei "All"-Kategorie + - Autor ist auf "NIKKE Team" gesetzt + ### [Pixiv User Illustrations Bridge](https://bridge.ponywave.de/#bridge-PixivUserIllustsBridge) (Von Akamaru) - **Beschreibung**: Zeigt die neuesten Illustrationen eines Pixiv-Nutzers - **Parameter**: