mastodonapi.php aktualisiert
This commit is contained in:
parent
1db48837f6
commit
0fe5607616
1 changed files with 122 additions and 92 deletions
214
mastodonapi.php
214
mastodonapi.php
|
|
@ -1,40 +1,40 @@
|
|||
<?php
|
||||
// MastodonAPI extension, https://codeberg.org/simonpipe/yellow-mastodonapi
|
||||
// MastodonAPI & GoToSocial extension
|
||||
// Kompatibel mit Mastodon-Servern & GoToSocial-Instanzen
|
||||
// https://codeberg.org/simonpipe/yellow-mastodonapi (angepasst)
|
||||
|
||||
class YellowMastodonAPI {
|
||||
const VERSION = "0.4";
|
||||
const VERSION = "0.6";
|
||||
public $yellow;
|
||||
|
||||
// Initialisierung
|
||||
public function onLoad($yellow) {
|
||||
$this->yellow = $yellow;
|
||||
$this->yellow->system->setDefault("mastodonapiInstanceUrl", "https://example.social");
|
||||
$this->yellow->system->setDefault("mastodonapiAccountId", "");
|
||||
$this->yellow->system->setDefault("mastodonapiAccessToken", ""); // optional
|
||||
$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/1-blog/");
|
||||
$this->yellow->system->setDefault("mastodonapiTriggerPath", "/mastodonapi-webhook-CHANGE_THIS_SECRET_KEY");
|
||||
$this->yellow->system->setDefault("mastodonapiLimit", "20"); // neu: Anzahl Posts
|
||||
$this->yellow->system->setDefault("mastodonapiLimit", "20");
|
||||
}
|
||||
|
||||
// Webhook-Request
|
||||
public function onRequest($scheme, $address, $base, $location, $fileName) {
|
||||
$triggerPath = $this->yellow->system->get("mastodonapiTriggerPath");
|
||||
if ($location == $triggerPath) {
|
||||
$postsCreated = $this->processMastodonFeed();
|
||||
if ($postsCreated !== false) {
|
||||
$this->sendResponse("Mastodon API feed processed. Created " . $postsCreated . " posts.", 200);
|
||||
} else {
|
||||
$this->sendResponse("Error processing Mastodon API feed. Check logs for details.", 500);
|
||||
}
|
||||
$this->sendResponse(
|
||||
$postsCreated !== false
|
||||
? "Mastodon/GoToSocial API feed processed. Created $postsCreated posts."
|
||||
: "Error processing Mastodon/GoToSocial API feed. Check logs for details.",
|
||||
$postsCreated !== false ? 200 : 500
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// CLI Command
|
||||
public function onCommand($command, $text) {
|
||||
if (!$this->yellow) return 500;
|
||||
switch ($command) {
|
||||
|
|
@ -47,7 +47,6 @@ class YellowMastodonAPI {
|
|||
}
|
||||
}
|
||||
|
||||
// API-Logik
|
||||
private function processMastodonFeed() {
|
||||
$instanceUrl = $this->yellow->system->get("mastodonapiInstanceUrl");
|
||||
$accountId = $this->yellow->system->get("mastodonapiAccountId");
|
||||
|
|
@ -65,38 +64,28 @@ class YellowMastodonAPI {
|
|||
|
||||
$apiUrl = rtrim($instanceUrl, "/") . "/api/v1/accounts/" . $accountId . "/statuses?limit=" . $limit;
|
||||
|
||||
$headers = [
|
||||
"User-Agent: YellowMastodonAPI"
|
||||
];
|
||||
$headers = ["User-Agent: YellowMastodonAPI"];
|
||||
if (!empty($accessToken)) {
|
||||
$headers[] = "Authorization: Bearer " . $accessToken;
|
||||
}
|
||||
|
||||
$context = stream_context_create([
|
||||
'http' => [
|
||||
'method' => "GET",
|
||||
'header' => implode("\r\n", $headers) . "\r\n",
|
||||
'timeout' => 30
|
||||
],
|
||||
'ssl' => [
|
||||
'verify_peer' => true,
|
||||
'verify_peer_name' => true
|
||||
]
|
||||
// Daten via cURL abrufen (funktioniert bei Mastodon & GoToSocial)
|
||||
$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);
|
||||
$curlError = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
$json = @file_get_contents($apiUrl, false, $context);
|
||||
|
||||
// Debug-Ausgabe
|
||||
if (isset($http_response_header)) {
|
||||
error_log("MastodonAPI DEBUG: HTTP Response Headers:");
|
||||
foreach ($http_response_header as $header) {
|
||||
error_log(" " . $header);
|
||||
}
|
||||
}
|
||||
error_log("MastodonAPI DEBUG: Raw API Response: " . substr($json, 0, 500));
|
||||
|
||||
if ($json === false) {
|
||||
error_log("ERROR: MastodonAPI: Could not fetch API data from $apiUrl");
|
||||
if ($json === false || $httpCode < 200 || $httpCode >= 300) {
|
||||
error_log("ERROR: Could not fetch API data from $apiUrl (HTTP $httpCode, Error: $curlError)");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -110,18 +99,13 @@ class YellowMastodonAPI {
|
|||
$postsCreated = 0;
|
||||
|
||||
foreach ($posts as $post) {
|
||||
// Keine Boosts / Antworten
|
||||
if (!empty($post['reblog']) || (!empty($post['reblogged']) && $post['reblogged'] === true)) continue;
|
||||
if (!is_null($post['in_reply_to_id'])) continue;
|
||||
|
||||
// Lokaler Filter: keine Boosts, keine Antworten
|
||||
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'];
|
||||
$tags = array_column($post['tags'], 'name');
|
||||
$contentHtml = $post['content'] ?? '';
|
||||
$createdAt = $post['created_at'] ?? '';
|
||||
$tags = array_column($post['tags'] ?? [], 'name');
|
||||
|
||||
if (!empty($hashtag) && !in_array(strtolower($hashtag), array_map('strtolower', $tags))) {
|
||||
continue;
|
||||
|
|
@ -134,18 +118,17 @@ class YellowMastodonAPI {
|
|||
$fullPath = $config->get("coreServerDocument") . $targetFolder . $fileName;
|
||||
|
||||
if (file_exists($fullPath)) continue;
|
||||
|
||||
if (!is_dir(dirname($fullPath))) mkdir(dirname($fullPath), 0755, true);
|
||||
|
||||
$authorName = $post['account']['display_name'] ?: $defaultAuthor;
|
||||
$authorUrl = $post['account']['url'] ?: "#";
|
||||
|
||||
// Bilder + Alt-Text
|
||||
$imageFilenames = [];
|
||||
$imageAltTexts = [];
|
||||
foreach ($post['media_attachments'] as $media) {
|
||||
if ($media['type'] === 'image' && !empty($media['url'])) {
|
||||
$filename = $this->downloadAndSaveImage($media['url'], $config->get("coreServerDocument") . 'media/images/');
|
||||
|
||||
foreach ($post['media_attachments'] ?? [] as $media) {
|
||||
if (($media['type'] ?? '') === 'image') {
|
||||
$filename = $this->downloadAndSaveImage($media, $config->get("coreServerDocument") . 'media/images/');
|
||||
if ($filename) {
|
||||
$imageFilenames[] = $filename;
|
||||
$imageAltTexts[] = $media['description'] ?? '';
|
||||
|
|
@ -173,17 +156,54 @@ class YellowMastodonAPI {
|
|||
return $postsCreated;
|
||||
}
|
||||
|
||||
private function downloadAndSaveImage($imageUrl, $dirPath) {
|
||||
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)) {
|
||||
error_log("ERROR: No usable image URL found in media attachment: " . json_encode($media));
|
||||
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;
|
||||
$img = @file_get_contents($imageUrl);
|
||||
if ($img === false) return false;
|
||||
file_put_contents($fullPath, $img);
|
||||
|
||||
$ch = curl_init($imageUrl);
|
||||
$headers = ["User-Agent: 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 => true,
|
||||
CURLOPT_SSL_VERIFYHOST => 2,
|
||||
]);
|
||||
$imgData = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curlError = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($imgData === false || $httpCode < 200 || $httpCode >= 300) {
|
||||
error_log("ERROR: Could not download image from $imageUrl (HTTP $httpCode, Error: $curlError)");
|
||||
return false;
|
||||
}
|
||||
|
||||
file_put_contents($fullPath, $imgData);
|
||||
return $fileName;
|
||||
}
|
||||
|
||||
// Rest der Helper-Methoden wie bei dir (generateTitle, generateTitleSlug, generateMarkdown, getYellowConfig, sendResponse) ...
|
||||
|
||||
private function generateTitle($content) {
|
||||
$text = strip_tags($content);
|
||||
$text = preg_replace('/https?:\/\/[^\s]+|#\w+/u', '', $text);
|
||||
|
|
@ -207,50 +227,60 @@ class YellowMastodonAPI {
|
|||
$content = strip_tags($html);
|
||||
$content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
|
||||
// Hashtag-Block extrahieren
|
||||
$hashtagString = '';
|
||||
if (preg_match('/(\s*(#\w+))+$/u', $content, $matches)) {
|
||||
$content = substr($content, 0, -strlen($matches[0]));
|
||||
if (preg_match_all('/#(\w+)/u', $matches[0], $tags)) {
|
||||
$hashtagString = "\n> " . implode(' ', $tags[0]);
|
||||
// Ersetzt '#' durch '#' für die Ausgabe
|
||||
$safeHashtags = array_map(function($tag) {
|
||||
return str_replace('#', '#', $tag);
|
||||
}, $tags[0]);
|
||||
$hashtagString = implode(' ', $safeHashtags);
|
||||
}
|
||||
}
|
||||
|
||||
$content = trim(preg_replace('/\s+/', ' ', $content));
|
||||
$content = trim(preg_replace('/\s+/', ' ', $content));
|
||||
|
||||
$imageMarkdown = '';
|
||||
foreach ($images as $i => $filename) {
|
||||
$alt = isset($alts[$i]) ? htmlspecialchars($alts[$i], ENT_QUOTES) : '';
|
||||
$imageMarkdown .= "\n<img src=\"/media/images/" . $filename . "\" alt=\"" . $alt . "\"/>\n";
|
||||
}
|
||||
|
||||
$featuredImage = '';
|
||||
$featuredImageAlt = '';
|
||||
|
||||
if (!empty($images)) {
|
||||
$featuredImage = "Image: " . $images[0] . "\n";
|
||||
if (isset($alts[0]) && !empty($alts[0])) {
|
||||
$featuredImageAlt = "ImageAlt: " . htmlspecialchars($alts[0], ENT_QUOTES) . "\n";
|
||||
$imageMarkdown = '';
|
||||
foreach ($images as $i => $filename) {
|
||||
$alt = isset($alts[$i]) ? htmlspecialchars($alts[$i], ENT_QUOTES) : '';
|
||||
$imageMarkdown .= "\n<img src=\"/media/images/" . $filename . "\" alt=\"" . $alt . "\"/>\n";
|
||||
}
|
||||
|
||||
$featuredImage = '';
|
||||
$featuredImageAlt = '';
|
||||
|
||||
if (!empty($images)) {
|
||||
$featuredImage = "Image: " . $images[0] . "\n";
|
||||
if (isset($alts[0]) && !empty($alts[0])) {
|
||||
$featuredImageAlt = "ImageAlt: " . htmlspecialchars($alts[0], ENT_QUOTES) . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
$htmlSafeAuthorName = str_replace('@', '@', $origAuthor);
|
||||
|
||||
// Baut den Blockquote-String robust und schrittweise zusammen
|
||||
$blockquote = "> \"$content\"";
|
||||
if (!empty($hashtagString)) {
|
||||
$blockquote .= "\n>\n> " . $hashtagString;
|
||||
}
|
||||
$blockquote .= "\n>\n> — <a target=\"_blank\" href=\"$origUrl\">$htmlSafeAuthorName</a>";
|
||||
|
||||
return <<<MD
|
||||
---
|
||||
Title: $title
|
||||
TitleSlug: $slug
|
||||
Published: $formattedDate $formattedTime
|
||||
Author: $yellowAuthor
|
||||
Layout: blog
|
||||
Tag: $yellowTag
|
||||
$featuredImage$featuredImageAlt---
|
||||
$imageMarkdown
|
||||
$blockquote
|
||||
MD;
|
||||
}
|
||||
|
||||
$htmlSafeAuthorName = str_replace('@', '@', $origAuthor);
|
||||
|
||||
return <<<MD
|
||||
---
|
||||
Title: $title
|
||||
TitleSlug: $slug
|
||||
Published: $formattedDate $formattedTime
|
||||
Author: $yellowAuthor
|
||||
Layout: blog
|
||||
Tag: $yellowTag
|
||||
$featuredImage$featuredImageAlt---
|
||||
$imageMarkdown
|
||||
> "$content"$hashtagString
|
||||
>
|
||||
> — <a target="_blank" href="$origUrl">$htmlSafeAuthorName</a>
|
||||
MD;
|
||||
}
|
||||
|
||||
private function getYellowConfig() {
|
||||
return $this->yellow ? $this->yellow->system : null;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue