yellow = $yellow; $this->yellow->system->setDefault("mastodonapiInstanceUrl", "https://example.social"); $this->yellow->system->setDefault("mastodonapiAccountId", ""); $this->yellow->system->setDefault("mastodonapiAccessToken", ""); $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("mastodonapiLimit", "20"); $this->yellow->system->setDefault("mastodonapiStatus", "public"); $this->yellow->system->setDefault("mastodonapiMaxAge", "0"); $this->yellow->system->setDefault("mastodonapiUpdateInterval", "30"); } // Reagiert auf System-Events und manuelle Aufrufe public function onUpdate($action) { if ($action == "mastodon" || $action == "daily" || $action == "install") { $this->updateMastodonPosts(); } } // Ermöglicht 'php yellow.php mastodonapi' im Terminal public function onCommand($command, $text) { if ($command == "mastodonapi") { echo "Mastodon Import gestartet...\n"; $count = $this->updateMastodonPosts(); if ($count === false) { echo "Fehler: API nicht erreichbar oder falsche ID.\n"; return 500; } echo "Erfolg: $count neue Posts verarbeitet.\n"; return 0; } return 0; } // Hilfsfunktion für den Pfad zur Status-Datei private function getStatusFile() { return $this->yellow->system->get("coreWorkerDirectory")."mastodonapi.status"; } // Die einzige Version dieser Funktion behalten: public function updateMastodonPosts() { $postsCreated = $this->processMastodonFeed(); if ($postsCreated !== false) { file_put_contents($this->getStatusFile(), time()); } return $postsCreated; } 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(); } } 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"); $status = $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 = ["User-Agent: YellowMastodonAPI"]; if (!empty($accessToken)) $headers[] = "Authorization: Bearer " . $accessToken; $ch = curl_init($apiUrl); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_TIMEOUT => 30, CURLOPT_HTTPHEADER => $headers, CURLOPT_SSL_VERIFYPEER => true, CURLOPT_SSL_VERIFYHOST => 2 ]); $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; $postsCreated = 0; foreach ($posts as $post) { if (!empty($post['reblog']) || (!empty($post['reblogged']) && $post['reblogged'] === true)) continue; if (!is_null($post['in_reply_to_id'])) continue; $contentHtml = $post['content'] ?? ''; $createdAt = $post['created_at'] ?? ''; if ($maxAgeHours > 0) { if (strtotime($createdAt) < (time() - ($maxAgeHours * 3600))) continue; } $tags = array_column($post['tags'] ?? [], 'name'); if (!empty($hashtag) && !in_array(strtolower($hashtag), array_map('strtolower', $tags))) 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; if (file_exists($fullPath)) continue; if (!is_dir(dirname($fullPath))) mkdir(dirname($fullPath), 0755, true); $imageFilenames = []; $imageAltTexts = []; foreach ($post['media_attachments'] ?? [] as $media) { if (($media['type'] ?? '') === 'image') { $filename = $this->downloadAndSaveImage($media, $this->yellow->system->get("coreServerDocument") . 'media/images/'); if ($filename) { $imageFilenames[] = $filename; $imageAltTexts[] = $media['description'] ?? ''; } } } $markdownContent = $this->generateMarkdown( $title, $titleSlug, $createdAt, $contentHtml, $post['account']['display_name'] ?: $defaultAuthor, $post['account']['url'] ?: "#", $defaultAuthor, $defaultTag, $imageFilenames, $imageAltTexts, $status ); file_put_contents($fullPath, $markdownContent); $postsCreated++; } return $postsCreated; } private function downloadAndSaveImage($media, $dirPath) { $imageUrl = $media['remote_url'] ?? $media['url'] ?? $media['preview_url'] ?? null; if (empty($imageUrl)) return false; if (!is_dir($dirPath)) mkdir($dirPath, 0755, true); $fileName = basename(parse_url($imageUrl, PHP_URL_PATH)); $fullPath = $dirPath . $fileName; if (file_exists($fullPath)) return $fileName; $ch = curl_init($imageUrl); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_TIMEOUT => 30, CURLOPT_USERAGENT => "YellowMastodonAPI", CURLOPT_SSL_VERIFYPEER => true, ]); $imgData = curl_exec($ch); curl_close($ch); if (!$imgData) return false; file_put_contents($fullPath, $imgData); return $fileName; } 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"; } private function generateTitleSlug($title) { $slug = mb_strtolower($title, 'UTF-8'); $slug = str_replace(['ä','ö','ü','ß',' '], ['ae','oe','ue','ss','-'], $slug); return trim(preg_replace('/[^a-z0-9-]+/', '', $slug), '-'); } 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(["
", "

"], ["\n", "

\n"], $html)), ENT_QUOTES | ENT_HTML5, 'UTF-8')); $content = str_replace("\n", "\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 << $content > > — $authorSafe MD; } }