From 3a9e39822868c1765966c1994afc6c5d405887fd Mon Sep 17 00:00:00 2001 From: xnand-dot-xyz Date: Thu, 14 Aug 2025 13:28:33 +0100 Subject: [PATCH] [ModrinthBridge] Add bridge (#4651) * [ModrinthBridge] Add bridge Support for querying updates to projects on https://modrinth.com May need modification, and I'm alright with the maintainer name being changed or cleared if actual maintenance is expected * Added declare and fixed linting errors * Skip parsing lists if null, and trim trailing space --- bridges/ModrinthBridge.php | 126 +++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 bridges/ModrinthBridge.php diff --git a/bridges/ModrinthBridge.php b/bridges/ModrinthBridge.php new file mode 100644 index 00000000..b8f42bcc --- /dev/null +++ b/bridges/ModrinthBridge.php @@ -0,0 +1,126 @@ + [ + 'name' => 'Name', + 'required' => true, + 'title' => 'The project name as seen in the URL bar', + 'exampleValue' => 'sodium' + ], + 'category' => [ + 'name' => 'Category', + 'type' => 'list', + 'values' => [ + 'Mod' => 'mod', + 'Resource Pack' => 'resourcepack', + 'Data Pack' => 'datapack', + 'Shader' => 'shader', + 'Modpack' => 'modpack', + 'Plugin' => 'plugin' + ], + 'defaultValue' => 'mod' + ], + 'loaders' => [ + 'name' => 'Loaders', + 'title' => 'List of mod loaders, separated by commas', + 'exampleValue' => 'neoforge, fabric' + ], + 'game_versions' => [ + 'name' => 'Game versions', + 'title' => 'List of game versions, separated by commas', + 'exampleValue' => '1.19.1, 1.19.2' + ], + 'featured' => [ + 'name' => 'Featured', + 'type' => 'list', + 'values' => [ + 'Unset' => '', + 'True' => 'true', + 'False' => 'false' + ], + 'title' => "Whether to filter for featured or non-featured\nUnset means no filter", + 'defaultValue', '' + ] + ]]; + + + public function getURI() + { + $name = $this->getInput('name'); + $category = $this->getInput('category'); + $uri = self::URI . $category . '/' . $name . '/versions'; + if (empty($name)) { + $uri = parent::getURI(); + } + return $uri; + } + + public function getName() + { + $name = $this->getInput('name'); + if (empty($name)) { + $name = parent::getName(); + } + return $name; + } + + public function collectData() + { + $apiUrl = 'https://api.modrinth.com/v2/project'; + $projectName = $this->getInput('name'); + $url = "{$apiUrl}/${projectName}/version"; + + $queryTable = [ + 'loaders' => $this->parseInputList($this->getInput('loaders')), + 'game_versions' => $this->parseInputList($this->getInput('game_versions')), + 'featured' => ($this->getInput('featured')) ? : null + ]; + + $query = http_build_query($queryTable); + if ($query) { + $url .= '?' . $query; + } + + // They expect a descriptive user agent and may block connections without one + // Change as appropriate + // https://docs.modrinth.com/api/#user-agents + $header = [ 'User-Agent: rss-bridge plugin https://github.com/RSS-Bridge/rss-bridge' ]; + $data = json_decode(getContents($url, $header)); + + foreach ($data as $entry) { + $item = []; + + $item['uri'] = self::URI . $this->getInput('category') . '/' . $this->getInput('name') . '/version/' . $entry->version_number; + $item['title'] = $entry->name; + $item['timestamp'] = $entry->date_published; + // Not setting the author as this would take a second request to match the author's user ID + $item['author'] = 'Modrinth'; + $item['content'] = markdownToHtml($entry->changelog); + $item['categories'] = array_merge($entry->loaders, $entry->game_versions); + $item['uid'] = $entry->id; + + $this->items[] = $item; + } + } + + // Converts lists like `foo, bar, baz` to `["foo", "bar", "baz"]` + protected function parseInputList($input): ?string + { + if (empty($input)) { + return null; + } + $items = array_filter(array_map('trim', explode(',', $input))); + return $items ? json_encode($items) : null; // return nothing if string is empty + } +}