diff --git a/externallinks.php b/externallinks.php index 6fd9d79..778fbd0 100644 --- a/externallinks.php +++ b/externallinks.php @@ -1,296 +1,21 @@ yellow = $yellow; - $this->yellow->system->setDefault("mastodonapiInstanceUrl", "https://example.social"); - $this->yellow->system->setDefault("mastodonapiAccountId", ""); - $this->yellow->system->setDefault("mastodonapiAccessToken", ""); - $this->yellow->system->setDefault("mastodonapiUpdateInterval", "30"); - $this->yellow->system->setDefault("mastodonapiLimit", "20"); - $this->yellow->system->setDefault("mastodonapiHashtag", ""); - $this->yellow->system->setDefault("mastodonapiAuthor", "YellowUser"); - $this->yellow->system->setDefault("mastodonapiTag", "Fediverse"); - $this->yellow->system->setDefault("mastodonapiFolder", "content/2-blog/"); - $this->yellow->system->setDefault("mastodonapiMaxAge", "0"); - $this->yellow->system->setDefault("mastodonapiStatus", "public"); } - // Handle update - public function onUpdate($action) { - if ( $action=="mastodon" || $action=="daily" || $action=="install" ) { - $this->updateMastodonPosts(); + public function onExtra($name) { + $output = ""; + if ($name == "header") { + $location = $this->yellow->config->get("serverBase")."/system/plugins/externallinks/"; + $output .= "\n"; } - } - - // Handle command - public function onCommand($command, $text) { - if ( $command=="mastodonapi" ) { - echo "Mastodon Import gestartet...\n"; - $count = $this->updateMastodonPosts(); - if ( $count===false ) { - echo "Fehler: API nicht erreichbar, falsche ID oder Zugriff verweigert.\n"; - return 500; - } - echo "Erfolg: " . $count . " neue Posts verarbeitet.\n"; - return true; - } - return 0; - } - - // Handle periodic update - public function onTick() { - $interval = intval($this->yellow->system->get("mastodonapiUpdateInterval")) * 60; - $statusFile = $this->getStatusFile(); - $lastUpdate = file_exists($statusFile) ? intval(file_get_contents($statusFile)) : 0; - if ( time() - $lastUpdate >= $interval ) { - $this->updateMastodonPosts(); - } - } - - // Update Mastodon posts - public function updateMastodonPosts() { - $postsCreated = $this->processMastodonFeed(); - if ( $postsCreated!==false ) { - $statusFile = $this->getStatusFile(); - file_put_contents($statusFile, time()); - } - return $postsCreated; - } - - // Process Mastodon feed and create pages - private function processMastodonFeed() { - $instanceUrl = $this->yellow->system->get("mastodonapiInstanceUrl"); - $accountId = $this->yellow->system->get("mastodonapiAccountId"); - $accessToken = $this->yellow->system->get("mastodonapiAccessToken"); - $hashtag = ltrim($this->yellow->system->get("mastodonapiHashtag"), "#"); - $defaultAuthor = $this->yellow->system->get("mastodonapiAuthor"); - $defaultTag = $this->yellow->system->get("mastodonapiTag"); - $targetFolder = $this->yellow->system->get("mastodonapiFolder"); - $statusSetting = $this->yellow->system->get("mastodonapiStatus"); - $maxAgeHours = intval($this->yellow->system->get("mastodonapiMaxAge")); - $limit = intval($this->yellow->system->get("mastodonapiLimit")); - - if ( empty($instanceUrl) || empty($accountId) ) return false; - - $apiUrl = rtrim($instanceUrl, "/") . "/api/v1/accounts/" . $accountId . "/statuses?limit=" . $limit; - - $headers = array("User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) YellowMastodonapi/" . self::VERSION); - if ( !empty($accessToken) ) $headers[] = "Authorization: Bearer " . $accessToken; - - $ch = curl_init($apiUrl); - curl_setopt_array($ch, array( - CURLOPT_RETURNTRANSFER => true, - CURLOPT_FOLLOWLOCATION => true, - CURLOPT_TIMEOUT => 30, - CURLOPT_HTTPHEADER => $headers, - CURLOPT_SSL_VERIFYPEER => false, - CURLOPT_SSL_VERIFYHOST => 0 - )); - $json = curl_exec($ch); - $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - curl_close($ch); - - if ( $json===false || $httpCode < 200 || $httpCode >= 300 ) return false; - $posts = json_decode($json, true); - if ( !is_array($posts) ) return false; - - echo "DEBUG: " . count($posts) . " Posts von API empfangen.\n"; - - $postsCreated = 0; - foreach ( $posts as $index => $post ) { - // Snac-Verschachtelung auflösen: Wenn ein echtes 'reblog'-Objekt von jemand anderem drinsteckt - if ( !empty($post["reblog"]) && is_array($post["reblog"]) ) { - // Nur überspringen, wenn es wirklich der Post eines ANDEREN Nutzers ist, den du geteilt hast - if ( !empty($post["reblog"]["account"]["id"]) && $post["reblog"]["account"]["id"] != $accountId ) { - echo "DEBUG [$index]: Übersprungen (Echter Reblog eines fremden Beitrags)\n"; - continue; - } - // Wenn es dein eigener ist, nutzen wir die inneren Daten - $post = $post["reblog"]; - } - - $contentHtml = $post["content"] ?? ""; - $createdAt = $post["created_at"] ?? ""; - $shortText = mb_substr(strip_tags($contentHtml), 0, 40) . "..."; - - // Reblogged-Flag nur werten, wenn es kein Eigen-Boost ist - if ( (isset($post["reblogged"]) && $post["reblogged"]===true) && empty($contentHtml) ) { - echo "DEBUG [$index]: Übersprungen (Leerer Boost): $shortText\n"; - continue; - } - - // REPLY CHECK (Snac sendet leere Strings oder null) - if ( !empty($post["in_reply_to_id"]) ) { - echo "DEBUG [$index]: Übersprungen (Antwort - Reply-ID: " . $post["in_reply_to_id"] . "): $shortText\n"; - continue; - } - - // ALTER CHECK - if ( $maxAgeHours > 0 ) { - if ( strtotime($createdAt) < ( time() - ( $maxAgeHours * 3600 ) ) ) { - echo "DEBUG [$index]: Übersprungen (Zu alt - Datum: $createdAt): $shortText\n"; - continue; - } - } - - // HASHTAG CHECK - $tags = array_column($post["tags"] ?? array(), "name"); - if ( !empty($hashtag) && !in_array(strtolower($hashtag), array_map("strtolower", $tags)) ) { - echo "DEBUG [$index]: Übersprungen (Hashtag #$hashtag fehlt): $shortText\n"; - continue; - } - - $title = $this->generateTitle($contentHtml); - $titleSlug = $this->generateTitleSlug($title); - $fileName = date("Y-m-d", strtotime($createdAt)) . "-" . $titleSlug . ".md"; - $fullPath = $this->yellow->system->get("coreServerDocument") . $targetFolder . $fileName; - - // EXISTENZ CHECK - if ( file_exists($fullPath) ) { - echo "DEBUG [$index]: Übersprungen (Datei existiert bereits: $fileName): $shortText\n"; - continue; - } - - if ( !is_dir(dirname($fullPath)) ) { - mkdir(dirname($fullPath), 0777, true); - chmod(dirname($fullPath), 0777); - } - - $imageFilenames = array(); - $imageAltTexts = array(); - foreach ( $post["media_attachments"] ?? array() as $media ) { - if ( ( $media["type"] ?? "" )==="image" ) { - $filename = $this->downloadAndSaveImage($media, $this->yellow->system->get("coreServerDocument") . "media/images/"); - if ( $filename ) { - $imageFilenames[] = $filename; - $imageAltTexts[] = $media["description"] ?? ""; - } - } - } - - $authorName = !empty($post["account"]["display_name"]) ? $post["account"]["display_name"] : $defaultAuthor; - $authorUrl = !empty($post["account"]["url"]) ? $post["account"]["url"] : "#"; - - $markdownContent = $this->generateMarkdown( - $title, $titleSlug, $createdAt, $contentHtml, - $authorName, $authorUrl, - $defaultAuthor, $defaultTag, $imageFilenames, $imageAltTexts, $statusSetting - ); - - file_put_contents($fullPath, $markdownContent); - chmod($fullPath, 0666); - echo "-> ERSTELLT [$index]: $fileName ($shortText)\n"; - $postsCreated++; - } - return $postsCreated; - } - - // Download and save image - private function downloadAndSaveImage($media, $dirPath) { - $imageUrl = $media['remote_url'] - ?? $media['url'] - ?? $media['preview_url'] - ?? ($media['meta']['original']['url'] ?? null) - ?? ($media['meta']['small']['url'] ?? null); - - if (empty($imageUrl)) return false; - - if (!is_dir($dirPath)) mkdir($dirPath, 0755, true); - - $fileName = basename(parse_url($imageUrl, PHP_URL_PATH)); - - if (!strpos($fileName, '.')) { - $fileName .= ".jpg"; - } - - $fullPath = $dirPath . $fileName; - - if (file_exists($fullPath) && filesize($fullPath) > 0) return $fileName; - - $ch = curl_init($imageUrl); - $headers = ["User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) YellowMastodonAPI"]; - - $accessToken = $this->yellow->system->get("mastodonapiAccessToken"); - if (!empty($accessToken)) { - $headers[] = "Authorization: Bearer " . $accessToken; - } - - curl_setopt_array($ch, [ - CURLOPT_RETURNTRANSFER => true, - CURLOPT_FOLLOWLOCATION => true, - CURLOPT_TIMEOUT => 30, - CURLOPT_HTTPHEADER => $headers, - CURLOPT_SSL_VERIFYPEER => false, - CURLOPT_SSL_VERIFYHOST => 0 - ]); - - $imgData = curl_exec($ch); - $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - curl_close($ch); - - if ($imgData === false || $httpCode < 200 || $httpCode >= 300) { - return false; - } - - file_put_contents($fullPath, $imgData); - return $fileName; - } - - // Generate title from content - private function generateTitle($content) { - $text = trim(strip_tags($content)); - $text = preg_replace("/https?:\/\/[^\s]+|#\w+/u", "", $text); - $words = explode(" ", $text); - return trim(mb_substr(implode(" ", array_slice($words, 0, 7)), 0, 100, "UTF-8")) ?: "Mastodon Post"; - } - - // Generate slug from title - private function generateTitleSlug($title) { - $slug = mb_strtolower($title, "UTF-8"); - $slug = str_replace(array("ä","ö","ü","ß"," "), array("ae","oe","ue","ss","-"), $slug); - return trim(preg_replace("/[^a-z0-9-]+/", "", $slug), "-"); - } - - // Generate markdown content - private function generateMarkdown($title, $slug, $dateTime, $html, $origAuthor, $origUrl, $yellowAuthor, $yellowTag, $images, $alts, $status) { - $date = new DateTime($dateTime); - $pub = $date->format("Y-m-d H:i:s"); - $content = trim(html_entity_decode(strip_tags(str_replace(array("
", "

"), array("\n", "

\n"), $html)), ENT_QUOTES | ENT_HTML5, "UTF-8")); - $content = str_replace(array("\n", "#"), array("\n> ", "#"), $content); - $imageMarkdown = ""; - $featuredImage = $featuredImageAlt = ""; - foreach ( $images as $i => $filename ) { - $alt = isset($alts[$i]) ? htmlspecialchars($alts[$i], ENT_QUOTES) : ""; - $imageMarkdown .= "\n\"$alt\"/\n"; - if ( $i===0 ) { - $featuredImage = "Image: $filename\n"; - $featuredImageAlt = "ImageAlt: $alt\n"; - } - } - $authorSafe = str_replace("@", "@", $origAuthor); - return "---\n" . - "Title: $title\n" . - "TitleSlug: $slug\n" . - "Published: $pub\n" . - "Author: $yellowAuthor\n" . - "Layout: blog\n" . - "Tag: $yellowTag\n" . - "Status: $status\n" . - $featuredImage . $featuredImageAlt . - "---\n\n" . - $imageMarkdown . "\n" . - "> " . $content . "\n>\n" . - "> — $authorSafe"; - } - - // Return status file path - private function getStatusFile() { - return $this->yellow->system->get("coreWorkerDirectory") . "mastodonapi.status"; + return $output; } } \ No newline at end of file