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("
", "