Compare commits
173 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3097ed4420 | |||
| 814ac745a5 | |||
| 6f237257d1 | |||
| b8cb368c27 | |||
|
|
7825565b3d | ||
|
|
519a4ba9b0 | ||
|
|
cbb190d51e | ||
|
|
c31766341a | ||
|
|
b7507290b6 | ||
|
|
adb5b28713 | ||
|
|
c395abeef3 | ||
|
|
adfa390b9d | ||
|
|
ea065984d8 | ||
|
|
6996fd4ac6 | ||
|
|
faef38bec7 | ||
|
|
111b5c4f90 | ||
|
|
d290e887ff | ||
|
|
1059cb92c6 | ||
|
|
8117b30499 | ||
|
|
8d65e7aebc | ||
|
|
8b625ba7f0 | ||
|
|
1afcf3e9b8 | ||
|
|
1e461693ef | ||
|
|
baaa18d817 | ||
|
|
6ad7d5a6ea | ||
|
|
ffd25e65cc | ||
|
|
1a04be8868 | ||
|
|
009217a5dc | ||
|
|
4143d8705b | ||
|
|
c861d784a6 | ||
|
|
0608c80c0e | ||
|
|
9fb9eb6167 | ||
|
|
2809f64abf | ||
|
|
c6e5093fdb | ||
|
|
ac5e899371 | ||
|
|
3ed242c0e9 | ||
|
|
2a611e144a | ||
|
|
15f3da1b62 | ||
|
|
128570ddc1 | ||
|
|
6b320c34bb | ||
|
|
101315a5ff | ||
|
|
6c6232534e | ||
|
|
fe6e16c7d0 | ||
|
|
26228b6661 | ||
|
|
ab8b730955 | ||
|
|
0a0a8200f3 | ||
|
|
9456c5b930 | ||
|
|
39f96c85a4 | ||
|
|
c587ecc66a | ||
|
|
19dc93bb49 | ||
|
|
5bed76ad36 | ||
|
|
1cee502f03 | ||
|
|
62a84080df | ||
|
|
d4742b355f | ||
|
|
44835d596b | ||
|
|
23540a1f76 | ||
|
|
f7b2fa7378 | ||
|
|
8a94d55c34 | ||
|
|
851bf57c38 | ||
|
|
c1ae73e365 | ||
|
|
174cdb580f | ||
|
|
835e44d998 | ||
|
|
47511c4ff9 | ||
|
|
2dacddf74f | ||
|
|
0de22660b0 | ||
|
|
fd7aa866f3 | ||
|
|
5fc2f5cc11 | ||
|
|
9268d40c90 | ||
|
|
b4e6fbe3f4 | ||
|
|
7048e854f9 | ||
|
|
0e3e0dbac3 | ||
|
|
cc82e6ef79 | ||
|
|
55b41aad48 | ||
|
|
dd8a402bb0 | ||
|
|
a187a962ef | ||
|
|
bfeb41fdf1 | ||
|
|
f605b3f8be | ||
|
|
fcb54d5fc5 | ||
|
|
7597d345d2 | ||
|
|
b735decdab | ||
|
|
9d3555803f | ||
|
|
5ace0f372d | ||
|
|
80bd916b85 | ||
|
|
f6189a2fde | ||
|
|
47b8a4c5f9 | ||
|
|
4d00db6e22 | ||
|
|
98103549ef | ||
|
|
a70e70ec5b | ||
|
|
b28dfb2d1f | ||
|
|
31d51c131a | ||
|
|
56be39a22f | ||
|
|
fed0a8040f | ||
|
|
0d1a881b1e | ||
|
|
d1fc6613d3 | ||
|
|
00c5c17e22 | ||
|
|
fb09357905 | ||
|
|
13c26b2731 | ||
|
|
4e893e82aa | ||
|
|
c7818ec83e | ||
|
|
db6a20325f | ||
|
|
ad39532601 | ||
|
|
073b1849a1 | ||
|
|
a8550fc0b6 | ||
|
|
51c9d72aea | ||
|
|
f59641319c | ||
|
|
b804f6cf6b | ||
|
|
0920fe7314 | ||
|
|
d1b0c828f1 | ||
|
|
1c5b9f99e8 | ||
|
|
29d4197835 | ||
|
|
33aa39353c | ||
|
|
d6813dbdf6 | ||
|
|
2107b09c21 | ||
|
|
016433e9a7 | ||
|
|
9b4171dc7c | ||
|
|
f90294de8f | ||
|
|
05f5902857 | ||
|
|
d11cf173b2 | ||
|
|
7e290cd0ad | ||
|
|
3a987c266b | ||
|
|
167522e9de | ||
|
|
784d92ad40 | ||
|
|
67c36ee72b | ||
|
|
5b48e5649f | ||
|
|
bee65004f4 | ||
|
|
234af208d5 | ||
|
|
aaddc3ed55 | ||
|
|
0816a76b39 | ||
|
|
c043183855 | ||
|
|
fada60ed46 | ||
|
|
60cee94861 | ||
|
|
ef65e3451f | ||
|
|
950864df64 | ||
|
|
cf294020e8 | ||
|
|
3b902a118b | ||
|
|
24a3bb7e87 | ||
|
|
5d0f649354 | ||
|
|
c21fd9e4b9 | ||
|
|
925f996db3 | ||
|
|
315da16328 | ||
|
|
50ceea433d | ||
|
|
5420ec8f50 | ||
|
|
b2711fe7eb | ||
|
|
8bcb974cbe | ||
|
|
3c849c1b6c | ||
|
|
e93bb20246 | ||
|
|
6b2042bbfa | ||
|
|
9fb48527eb | ||
|
|
d461d34048 | ||
|
|
12284b5d14 | ||
|
|
c59e878388 | ||
|
|
6107808050 | ||
|
|
e0562cce9f | ||
|
|
cdddcde33e | ||
|
|
04e6355d6a | ||
|
|
dd5a9904fa | ||
|
|
04748c4518 | ||
|
|
2508804b5e | ||
|
|
50b8a42e1e | ||
|
|
da19dd8648 | ||
|
|
b751a8f997 | ||
|
|
cb0b8f1d1e | ||
|
|
4752460dd7 | ||
|
|
3053604203 | ||
|
|
0de1853edf | ||
|
|
4d526baac6 | ||
|
|
217495cfcd | ||
|
|
4eaa3edf60 | ||
|
|
0a4505986d | ||
|
|
8fb6caedf4 | ||
|
|
ced3a19f88 | ||
|
|
34e175b5c3 | ||
|
|
68f171a1ef |
10 changed files with 361 additions and 307 deletions
6
.gitattributes
vendored
Normal file
6
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
.gitattributes export-ignore
|
||||||
|
LICENSE export-ignore
|
||||||
|
help*.md export-ignore
|
||||||
|
readme*.md export-ignore
|
||||||
|
# Auto detect text files and perform LF normalization
|
||||||
|
* text=auto
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
UNLICENSE
|
||||||
|
|
||||||
This is free and unencumbered software released into the public domain.
|
This is free and unencumbered software released into the public domain.
|
||||||
|
|
||||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
|
@ -22,4 +24,4 @@ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
OTHER DEALINGS IN THE SOFTWARE.
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
For more information, please refer to <https://unlicense.org/>
|
For more information, please refer to <https://unlicense.org/>
|
||||||
and licensed under GPL version 2, unless stated otherwise.
|
and licensed under GPL version 2, unless stated otherwise.
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
# Datenstrom Yellow extension settings
|
# Datenstrom Yellow extension settings
|
||||||
|
|
||||||
Extension: MastodonAPI
|
Extension: Mastodonapi
|
||||||
Version: 0.1
|
Version: 0.9.9
|
||||||
Description: Mastodon-API Feed to Blog Post (For Mastodon & GoToSocial)
|
Description: Import your toots into your blog.
|
||||||
Developer: Simon Pipe
|
Developer: Simon Rohr
|
||||||
Tag: feature
|
Tag: feature
|
||||||
Download: https://codeberg.org/simonpipe/yellow-mastodonapi/archive/main.zip
|
DownloadUrl: https://git.thepipes.ch/simon/yellow-mastodonapi/archive/main.zip
|
||||||
Documentation: https://codeberg.org/simonpipe/yellow-mastodonapi
|
DocumentationUrl: https://git.thepipes.ch/simon/yellow-mastodonapi
|
||||||
DocumentationLanguage: en, de
|
DocumentationLanguage: en, de
|
||||||
Published: 2025-08-13 20:52:00
|
Published: 2026-07-28 15:41:00
|
||||||
Status: experimental
|
Status: experimental
|
||||||
system/workers/mastodonapi.php: mastodonapi.php, create update
|
system/workers/mastodonapi.php: mastodonapi.php, create, update
|
||||||
|
system/workers/mastodonapi.status: mastodonapi.status, create, update, careful
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,18 @@
|
||||||
# Hilfe für GoToSocial
|
# Hilfe für Snac und GoToSocial
|
||||||
|
|
||||||
## 1. Account-ID herausfinden
|
## Snac
|
||||||
|
|
||||||
|
### Token erstellen
|
||||||
|
|
||||||
|
1. Ersetze in diesem Link die Platzhalter und öffne ihn im Browser:
|
||||||
|
```
|
||||||
|
https://DEINE_INSTANZ_URL/oauth/x-snac-get-token
|
||||||
|
```
|
||||||
|
→ Anmelden, bestätigen und den **Code** kopieren.
|
||||||
|
|
||||||
|
## GoToSocial
|
||||||
|
|
||||||
|
### Account-ID herausfinden
|
||||||
1. Öffne dein Profil:
|
1. Öffne dein Profil:
|
||||||
`https://DEINE_INSTANZ_URL/@DEIN_BENUTZERNAME`
|
`https://DEINE_INSTANZ_URL/@DEIN_BENUTZERNAME`
|
||||||
2. Zeige den **Quelltext** der Seite an (Rechtsklick → *Seitenquelltext anzeigen*).
|
2. Zeige den **Quelltext** der Seite an (Rechtsklick → *Seitenquelltext anzeigen*).
|
||||||
|
|
@ -13,7 +25,7 @@
|
||||||
5. Trage die Account-ID in Yellow in der `yellow-system.ini` unter
|
5. Trage die Account-ID in Yellow in der `yellow-system.ini` unter
|
||||||
`MastodonapiAccountId` ein.
|
`MastodonapiAccountId` ein.
|
||||||
|
|
||||||
## 2. Token erstellen
|
### Token erstellen
|
||||||
|
|
||||||
1. Melde dich in deiner GoToSocial-Instanz an:
|
1. Melde dich in deiner GoToSocial-Instanz an:
|
||||||
`https://DEINE_INSTANZ_URL/settings`
|
`https://DEINE_INSTANZ_URL/settings`
|
||||||
|
|
@ -29,14 +41,14 @@
|
||||||
→ Anmelden, bestätigen und den **Code** kopieren.
|
→ Anmelden, bestätigen und den **Code** kopieren.
|
||||||
6. Ersetze in diesem `curl`-Befehl die Platzhalter:
|
6. Ersetze in diesem `curl`-Befehl die Platzhalter:
|
||||||
```bash
|
```bash
|
||||||
curl -X POST https://DEINE_INSTANZ_URL/oauth/token
|
curl -X POST https://DEINE_INSTANZ_URL/oauth/token \
|
||||||
-d "client_id=DEIN_CLIENT_ID"
|
-d "client_id=DEIN_CLIENT_ID" \
|
||||||
-d "client_secret=DEIN_CLIENT_SECRET"
|
-d "client_secret=DEIN_CLIENT_SECRET" \
|
||||||
-d "grant_type=authorization_code"
|
-d "grant_type=authorization_code" \
|
||||||
-d "code=DER_CODE_AUS_SCHRITT_5"
|
-d "code=DER_CODE_AUS_SCHRITT_5" \
|
||||||
-d "redirect_uri=urn:ietf:wg:oauth:2.0:oob"
|
-d "redirect_uri=urn:ietf:wg:oauth:2.0:oob"
|
||||||
```
|
```
|
||||||
7. Führe den Befehl im **Terminal** aus.
|
7. Führe den Befehl im **Terminal** aus.
|
||||||
→ Du erhältst dein **User-Token**.
|
→ Du erhältst dein **User-Token**.
|
||||||
8. Trage den Token in Yellow in der `yellow-system.ini` unter
|
8. Trage den Token in Yellow in der `yellow-system.ini` unter
|
||||||
`MastodonapiAccessToken` ein.
|
`MastodonapiAccessToken` ein.
|
||||||
|
|
@ -1,6 +1,18 @@
|
||||||
# Help for GoToSocial
|
# Help for Snac and GoToSocial
|
||||||
|
|
||||||
## Find your Account ID
|
## Snac
|
||||||
|
|
||||||
|
### Create a Token
|
||||||
|
|
||||||
|
1. Adjust the following link and paste it into your browser:
|
||||||
|
```
|
||||||
|
https://YOUR_INSTANCE_URL/oauth/x-snac-get-token
|
||||||
|
```
|
||||||
|
→ Log in, authorize, and copy the **code**.
|
||||||
|
|
||||||
|
## GoToSocial
|
||||||
|
|
||||||
|
### Find your Account ID
|
||||||
1. Open your profile (`https://YOUR_INSTANCE_URL/@YOUR_USERNAME`)
|
1. Open your profile (`https://YOUR_INSTANCE_URL/@YOUR_USERNAME`)
|
||||||
2. View the **page source** and search for this line in the `<head>` section:
|
2. View the **page source** and search for this line in the `<head>` section:
|
||||||
```html
|
```html
|
||||||
|
|
@ -9,7 +21,7 @@
|
||||||
3. You will find your **account ID** in this line between `fileserver` and `attachment`: `.../fileserver/ACCOUNT_ID/attachment/...`
|
3. You will find your **account ID** in this line between `fileserver` and `attachment`: `.../fileserver/ACCOUNT_ID/attachment/...`
|
||||||
4. Copy your account ID and enter it in Yellow's `yellow-system.ini` under *MastodonapiAccountId*.
|
4. Copy your account ID and enter it in Yellow's `yellow-system.ini` under *MastodonapiAccountId*.
|
||||||
|
|
||||||
## Create a Token
|
### Create a Token
|
||||||
|
|
||||||
1. Log in to your GoToSocial instance under `/settings` and click **New Application** under **Applications**.
|
1. Log in to your GoToSocial instance under `/settings` and click **New Application** under **Applications**.
|
||||||
2. Under `Application name (required)`, enter a name (e.g., *yellow-mastodonapi*). The other fields can be left blank. Click **Create**.
|
2. Under `Application name (required)`, enter a name (e.g., *yellow-mastodonapi*). The other fields can be left blank. Click **Create**.
|
||||||
|
|
@ -21,11 +33,11 @@
|
||||||
→ Log in, authorize, and copy the **code**.
|
→ Log in, authorize, and copy the **code**.
|
||||||
5. Adjust the following lines:
|
5. Adjust the following lines:
|
||||||
```bash
|
```bash
|
||||||
curl -X POST https://DEINE_INSTANZ_URL/oauth/token
|
curl -X POST https://DEINE_INSTANZ_URL/oauth/token \
|
||||||
-d "client_id=DEIN_CLIENT_ID"
|
-d "client_id=DEIN_CLIENT_ID" \
|
||||||
-d "client_secret=DEIN_CLIENT_SECRET"
|
-d "client_secret=DEIN_CLIENT_SECRET" \
|
||||||
-d "grant_type=authorization_code"
|
-d "grant_type=authorization_code" \
|
||||||
-d "code=DER_CODE_AUS_SCHRITT_5"
|
-d "code=DER_CODE_AUS_SCHRITT_4" \
|
||||||
-d "redirect_uri=urn:ietf:wg:oauth:2.0:oob"
|
-d "redirect_uri=urn:ietf:wg:oauth:2.0:oob"
|
||||||
```
|
```
|
||||||
6. Copy this code into your **terminal** and press Enter. You will now receive your **user token**, which you can enter in Yellow's `yellow-system.ini`.
|
6. Copy this code into your **terminal** and press Enter. You will now receive your **user token**, which you can enter in Yellow's `yellow-system.ini`.
|
||||||
418
mastodonapi.php
418
mastodonapi.php
|
|
@ -1,296 +1,296 @@
|
||||||
<?php
|
<?php
|
||||||
// Public Domain
|
// Mastodonapi extension, https://git.thepipes.ch/simon/yellow-mastodonapi
|
||||||
// MastodonAPI extension, https://codeberg.org/simonpipe/yellow-mastodonapi
|
|
||||||
|
|
||||||
class YellowMastodonAPI {
|
class YellowMastodonapi {
|
||||||
const VERSION = "0.1";
|
const VERSION = "0.9.9";
|
||||||
public $yellow;
|
public $yellow; // access to API
|
||||||
|
|
||||||
|
// Initialize extension
|
||||||
public function onLoad($yellow) {
|
public function onLoad($yellow) {
|
||||||
$this->yellow = $yellow;
|
$this->yellow = $yellow;
|
||||||
$this->yellow->system->setDefault("mastodonapiInstanceUrl", "https://example.social");
|
$this->yellow->system->setDefault("mastodonapiInstanceUrl", "https://example.social");
|
||||||
$this->yellow->system->setDefault("mastodonapiAccountId", "");
|
$this->yellow->system->setDefault("mastodonapiAccountId", "");
|
||||||
$this->yellow->system->setDefault("mastodonapiAccessToken", "");
|
$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("mastodonapiHashtag", "");
|
||||||
$this->yellow->system->setDefault("mastodonapiAuthor", "YellowUser");
|
$this->yellow->system->setDefault("mastodonapiAuthor", "YellowUser");
|
||||||
$this->yellow->system->setDefault("mastodonapiTag", "Fediverse");
|
$this->yellow->system->setDefault("mastodonapiTag", "Fediverse");
|
||||||
$this->yellow->system->setDefault("mastodonapiFolder", "content/1-blog/");
|
$this->yellow->system->setDefault("mastodonapiFolder", "content/2-blog/");
|
||||||
$this->yellow->system->setDefault("mastodonapiTriggerPath", "/mastodonapi-webhook-CHANGE_THIS_SECRET_KEY");
|
$this->yellow->system->setDefault("mastodonapiMaxAge", "0");
|
||||||
$this->yellow->system->setDefault("mastodonapiLimit", "20");
|
$this->yellow->system->setDefault("mastodonapiStatus", "public");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onRequest($scheme, $address, $base, $location, $fileName) {
|
// Handle update
|
||||||
$triggerPath = $this->yellow->system->get("mastodonapiTriggerPath");
|
public function onUpdate($action) {
|
||||||
if ($location == $triggerPath) {
|
if ( $action=="mastodon" || $action=="daily" || $action=="install" ) {
|
||||||
$postsCreated = $this->processMastodonFeed();
|
$this->updateMastodonPosts();
|
||||||
$this->sendResponse(
|
}
|
||||||
$postsCreated !== false
|
}
|
||||||
? "Mastodon/GoToSocial API feed processed. Created $postsCreated posts."
|
|
||||||
: "Error processing Mastodon/GoToSocial API feed.",
|
// Handle command
|
||||||
$postsCreated !== false ? 200 : 500
|
public function onCommand($command, $text) {
|
||||||
);
|
if ( $command=="mastodonapi" ) {
|
||||||
return 1;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onCommand($command, $text) {
|
// Handle periodic update
|
||||||
if (!$this->yellow) return 500;
|
public function onTick() {
|
||||||
switch ($command) {
|
$interval = intval($this->yellow->system->get("mastodonapiUpdateInterval")) * 60;
|
||||||
case "mastodonapi":
|
$statusFile = $this->getStatusFile();
|
||||||
if (!empty($text)) return 400;
|
$lastUpdate = file_exists($statusFile) ? intval(file_get_contents($statusFile)) : 0;
|
||||||
$postsCreated = $this->processMastodonFeed();
|
if ( time() - $lastUpdate >= $interval ) {
|
||||||
return ($postsCreated !== false) ? 0 : 500;
|
$this->updateMastodonPosts();
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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() {
|
private function processMastodonFeed() {
|
||||||
$instanceUrl = $this->yellow->system->get("mastodonapiInstanceUrl");
|
$instanceUrl = $this->yellow->system->get("mastodonapiInstanceUrl");
|
||||||
$accountId = $this->yellow->system->get("mastodonapiAccountId");
|
$accountId = $this->yellow->system->get("mastodonapiAccountId");
|
||||||
$accessToken = $this->yellow->system->get("mastodonapiAccessToken");
|
$accessToken = $this->yellow->system->get("mastodonapiAccessToken");
|
||||||
$hashtag = ltrim($this->yellow->system->get("mastodonapiHashtag"), '#');
|
$hashtag = ltrim($this->yellow->system->get("mastodonapiHashtag"), "#");
|
||||||
$defaultAuthor = $this->yellow->system->get("mastodonapiAuthor");
|
$defaultAuthor = $this->yellow->system->get("mastodonapiAuthor");
|
||||||
$defaultTag = $this->yellow->system->get("mastodonapiTag");
|
$defaultTag = $this->yellow->system->get("mastodonapiTag");
|
||||||
$targetFolder = $this->yellow->system->get("mastodonapiFolder");
|
$targetFolder = $this->yellow->system->get("mastodonapiFolder");
|
||||||
$limit = intval($this->yellow->system->get("mastodonapiLimit"));
|
$statusSetting = $this->yellow->system->get("mastodonapiStatus");
|
||||||
|
$maxAgeHours = intval($this->yellow->system->get("mastodonapiMaxAge"));
|
||||||
if (empty($instanceUrl) || empty($accountId)) {
|
$limit = intval($this->yellow->system->get("mastodonapiLimit"));
|
||||||
return false;
|
|
||||||
}
|
if ( empty($instanceUrl) || empty($accountId) ) return false;
|
||||||
|
|
||||||
$apiUrl = rtrim($instanceUrl, "/") . "/api/v1/accounts/" . $accountId . "/statuses?limit=" . $limit;
|
$apiUrl = rtrim($instanceUrl, "/") . "/api/v1/accounts/" . $accountId . "/statuses?limit=" . $limit;
|
||||||
|
|
||||||
$headers = ["User-Agent: YellowMastodonAPI"];
|
$headers = array("User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) YellowMastodonapi/" . self::VERSION);
|
||||||
if (!empty($accessToken)) {
|
if ( !empty($accessToken) ) $headers[] = "Authorization: Bearer " . $accessToken;
|
||||||
$headers[] = "Authorization: Bearer " . $accessToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
$ch = curl_init($apiUrl);
|
$ch = curl_init($apiUrl);
|
||||||
curl_setopt_array($ch, [
|
curl_setopt_array($ch, array(
|
||||||
CURLOPT_RETURNTRANSFER => true,
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
CURLOPT_FOLLOWLOCATION => true,
|
CURLOPT_FOLLOWLOCATION => true,
|
||||||
CURLOPT_TIMEOUT => 30,
|
CURLOPT_TIMEOUT => 30,
|
||||||
CURLOPT_HTTPHEADER => $headers,
|
CURLOPT_HTTPHEADER => $headers,
|
||||||
CURLOPT_SSL_VERIFYPEER => true,
|
CURLOPT_SSL_VERIFYPEER => false,
|
||||||
CURLOPT_SSL_VERIFYHOST => 2
|
CURLOPT_SSL_VERIFYHOST => 0
|
||||||
]);
|
));
|
||||||
$json = curl_exec($ch);
|
$json = curl_exec($ch);
|
||||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
curl_close($ch);
|
curl_close($ch);
|
||||||
|
|
||||||
if ($json === false || $httpCode < 200 || $httpCode >= 300) {
|
if ( $json===false || $httpCode < 200 || $httpCode >= 300 ) return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$posts = json_decode($json, true);
|
$posts = json_decode($json, true);
|
||||||
if (!is_array($posts)) {
|
if ( !is_array($posts) ) return false;
|
||||||
return false;
|
|
||||||
}
|
echo "DEBUG: " . count($posts) . " Posts von API empfangen.\n";
|
||||||
|
|
||||||
$config = $this->getYellowConfig();
|
|
||||||
$postsCreated = 0;
|
$postsCreated = 0;
|
||||||
|
foreach ( $posts as $index => $post ) {
|
||||||
foreach ($posts as $post) {
|
// Snac-Verschachtelung auflösen: Wenn ein echtes 'reblog'-Objekt von jemand anderem drinsteckt
|
||||||
if (!empty($post['reblog']) || (!empty($post['reblogged']) && $post['reblogged'] === true)) continue;
|
if ( !empty($post["reblog"]) && is_array($post["reblog"]) ) {
|
||||||
if (!is_null($post['in_reply_to_id'])) continue;
|
// 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 ) {
|
||||||
$contentHtml = $post['content'] ?? '';
|
echo "DEBUG [$index]: Übersprungen (Echter Reblog eines fremden Beitrags)\n";
|
||||||
$createdAt = $post['created_at'] ?? '';
|
continue;
|
||||||
$tags = array_column($post['tags'] ?? [], 'name');
|
}
|
||||||
|
// Wenn es dein eigener ist, nutzen wir die inneren Daten
|
||||||
if (!empty($hashtag) && !in_array(strtolower($hashtag), array_map('strtolower', $tags))) {
|
$post = $post["reblog"];
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$title = $this->generateTitle($contentHtml);
|
$contentHtml = $post["content"] ?? "";
|
||||||
$titleSlug = $this->generateTitleSlug($title);
|
$createdAt = $post["created_at"] ?? "";
|
||||||
$datePrefix = date('Y-m-d', strtotime($createdAt));
|
$shortText = mb_substr(strip_tags($contentHtml), 0, 40) . "...";
|
||||||
$fileName = $datePrefix . '-' . $titleSlug . '.md';
|
|
||||||
$fullPath = $config->get("coreServerDocument") . $targetFolder . $fileName;
|
// Reblogged-Flag nur werten, wenn es kein Eigen-Boost ist
|
||||||
|
if ( (isset($post["reblogged"]) && $post["reblogged"]===true) && empty($contentHtml) ) {
|
||||||
if (file_exists($fullPath)) continue;
|
echo "DEBUG [$index]: Übersprungen (Leerer Boost): $shortText\n";
|
||||||
if (!is_dir(dirname($fullPath))) mkdir(dirname($fullPath), 0755, true);
|
continue;
|
||||||
|
}
|
||||||
$authorName = $post['account']['display_name'] ?: $defaultAuthor;
|
|
||||||
$authorUrl = $post['account']['url'] ?: "#";
|
// REPLY CHECK (Snac sendet leere Strings oder null)
|
||||||
|
if ( !empty($post["in_reply_to_id"]) ) {
|
||||||
$imageFilenames = [];
|
echo "DEBUG [$index]: Übersprungen (Antwort - Reply-ID: " . $post["in_reply_to_id"] . "): $shortText\n";
|
||||||
$imageAltTexts = [];
|
continue;
|
||||||
|
}
|
||||||
foreach ($post['media_attachments'] ?? [] as $media) {
|
|
||||||
if (($media['type'] ?? '') === 'image') {
|
// ALTER CHECK
|
||||||
$filename = $this->downloadAndSaveImage($media, $config->get("coreServerDocument") . 'media/images/');
|
if ( $maxAgeHours > 0 ) {
|
||||||
if ($filename) {
|
if ( strtotime($createdAt) < ( time() - ( $maxAgeHours * 3600 ) ) ) {
|
||||||
$imageFilenames[] = $filename;
|
echo "DEBUG [$index]: Übersprungen (Zu alt - Datum: $createdAt): $shortText\n";
|
||||||
$imageAltTexts[] = $media['description'] ?? '';
|
continue;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$markdownContent = $this->generateMarkdown(
|
// HASHTAG CHECK
|
||||||
$title,
|
$tags = array_column($post["tags"] ?? array(), "name");
|
||||||
$titleSlug,
|
if ( !empty($hashtag) && !in_array(strtolower($hashtag), array_map("strtolower", $tags)) ) {
|
||||||
$createdAt,
|
echo "DEBUG [$index]: Übersprungen (Hashtag #$hashtag fehlt): $shortText\n";
|
||||||
$contentHtml,
|
continue;
|
||||||
$authorName,
|
}
|
||||||
$authorUrl,
|
|
||||||
$defaultAuthor,
|
$title = $this->generateTitle($contentHtml);
|
||||||
$defaultTag,
|
$titleSlug = $this->generateTitleSlug($title);
|
||||||
$imageFilenames,
|
$fileName = date("Y-m-d", strtotime($createdAt)) . "-" . $titleSlug . ".md";
|
||||||
$imageAltTexts
|
$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);
|
file_put_contents($fullPath, $markdownContent);
|
||||||
|
chmod($fullPath, 0666);
|
||||||
|
echo "-> ERSTELLT [$index]: $fileName ($shortText)\n";
|
||||||
$postsCreated++;
|
$postsCreated++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $postsCreated;
|
return $postsCreated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Download and save image
|
||||||
private function downloadAndSaveImage($media, $dirPath) {
|
private function downloadAndSaveImage($media, $dirPath) {
|
||||||
$imageUrl = $media['remote_url']
|
$imageUrl = $media['remote_url']
|
||||||
?? $media['url']
|
?? $media['url']
|
||||||
?? $media['preview_url']
|
?? $media['preview_url']
|
||||||
?? ($media['meta']['original']['url'] ?? null)
|
?? ($media['meta']['original']['url'] ?? null)
|
||||||
?? ($media['meta']['small']['url'] ?? null);
|
?? ($media['meta']['small']['url'] ?? null);
|
||||||
|
|
||||||
if (empty($imageUrl)) {
|
if (empty($imageUrl)) return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_dir($dirPath)) mkdir($dirPath, 0755, true);
|
if (!is_dir($dirPath)) mkdir($dirPath, 0755, true);
|
||||||
|
|
||||||
$fileName = basename(parse_url($imageUrl, PHP_URL_PATH));
|
$fileName = basename(parse_url($imageUrl, PHP_URL_PATH));
|
||||||
|
|
||||||
|
if (!strpos($fileName, '.')) {
|
||||||
|
$fileName .= ".jpg";
|
||||||
|
}
|
||||||
|
|
||||||
$fullPath = $dirPath . $fileName;
|
$fullPath = $dirPath . $fileName;
|
||||||
if (file_exists($fullPath)) return $fileName;
|
|
||||||
|
if (file_exists($fullPath) && filesize($fullPath) > 0) return $fileName;
|
||||||
|
|
||||||
$ch = curl_init($imageUrl);
|
$ch = curl_init($imageUrl);
|
||||||
$headers = ["User-Agent: YellowMastodonAPI"];
|
$headers = ["User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) YellowMastodonAPI"];
|
||||||
|
|
||||||
$accessToken = $this->yellow->system->get("mastodonapiAccessToken");
|
$accessToken = $this->yellow->system->get("mastodonapiAccessToken");
|
||||||
if (!empty($accessToken)) {
|
if (!empty($accessToken)) {
|
||||||
$headers[] = "Authorization: Bearer " . $accessToken;
|
$headers[] = "Authorization: Bearer " . $accessToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
curl_setopt_array($ch, [
|
curl_setopt_array($ch, [
|
||||||
CURLOPT_RETURNTRANSFER => true,
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
CURLOPT_FOLLOWLOCATION => true,
|
CURLOPT_FOLLOWLOCATION => true,
|
||||||
CURLOPT_TIMEOUT => 30,
|
CURLOPT_TIMEOUT => 30,
|
||||||
CURLOPT_HTTPHEADER => $headers,
|
CURLOPT_HTTPHEADER => $headers,
|
||||||
CURLOPT_SSL_VERIFYPEER => true,
|
CURLOPT_SSL_VERIFYPEER => false,
|
||||||
CURLOPT_SSL_VERIFYHOST => 2,
|
CURLOPT_SSL_VERIFYHOST => 0
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$imgData = curl_exec($ch);
|
$imgData = curl_exec($ch);
|
||||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
curl_close($ch);
|
curl_close($ch);
|
||||||
|
|
||||||
if ($imgData === false || $httpCode < 200 || $httpCode >= 300) {
|
if ($imgData === false || $httpCode < 200 || $httpCode >= 300) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
file_put_contents($fullPath, $imgData);
|
file_put_contents($fullPath, $imgData);
|
||||||
return $fileName;
|
return $fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate title from content
|
||||||
private function generateTitle($content) {
|
private function generateTitle($content) {
|
||||||
$text = strip_tags($content);
|
$text = trim(strip_tags($content));
|
||||||
$text = preg_replace('/https?:\/\/[^\s]+|#\w+/u', '', $text);
|
$text = preg_replace("/https?:\/\/[^\s]+|#\w+/u", "", $text);
|
||||||
$text = preg_replace('/[^\p{L}\p{N}\s-]/u', '', $text);
|
$words = explode(" ", $text);
|
||||||
$text = trim(preg_replace('/\s+/', ' ', $text));
|
return trim(mb_substr(implode(" ", array_slice($words, 0, 7)), 0, 100, "UTF-8")) ?: "Mastodon Post";
|
||||||
$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) {
|
private function generateTitleSlug($title) {
|
||||||
$slug = mb_strtolower($title, 'UTF-8');
|
$slug = mb_strtolower($title, "UTF-8");
|
||||||
$slug = str_replace(['ä','ö','ü','Ä','Ö','Ü','ß',' '], ['ae','oe','ue','ae','oe','ue','ss','-'], $slug);
|
$slug = str_replace(array("ä","ö","ü","ß"," "), array("ae","oe","ue","ss","-"), $slug);
|
||||||
return trim(preg_replace('/[^a-z0-9-]+/', '', $slug), '-');
|
return trim(preg_replace("/[^a-z0-9-]+/", "", $slug), "-");
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generateMarkdown($title, $slug, $dateTime, $html, $origAuthor, $origUrl, $yellowAuthor, $yellowTag, $images = [], $alts = []) {
|
// Generate markdown content
|
||||||
|
private function generateMarkdown($title, $slug, $dateTime, $html, $origAuthor, $origUrl, $yellowAuthor, $yellowTag, $images, $alts, $status) {
|
||||||
$date = new DateTime($dateTime);
|
$date = new DateTime($dateTime);
|
||||||
$formattedDate = $date->format('Y-m-d');
|
$pub = $date->format("Y-m-d H:i:s");
|
||||||
$formattedTime = $date->format('H:i:s');
|
$content = trim(html_entity_decode(strip_tags(str_replace(array("<br>", "</p>"), array("\n", "</p>\n"), $html)), ENT_QUOTES | ENT_HTML5, "UTF-8"));
|
||||||
|
$content = str_replace(array("\n", "#"), array("\n> ", "#"), $content);
|
||||||
$content = strip_tags($html);
|
$imageMarkdown = "";
|
||||||
$content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
$featuredImage = $featuredImageAlt = "";
|
||||||
|
foreach ( $images as $i => $filename ) {
|
||||||
$hashtagString = '';
|
$alt = isset($alts[$i]) ? htmlspecialchars($alts[$i], ENT_QUOTES) : "";
|
||||||
if (preg_match('/(\s*(#\w+))+$/u', $content, $matches)) {
|
$imageMarkdown .= "\n<img src=\"/media/images/$filename\" alt=\"$alt\"/>\n";
|
||||||
$content = substr($content, 0, -strlen($matches[0]));
|
if ( $i===0 ) {
|
||||||
if (preg_match_all('/#(\w+)/u', $matches[0], $tags)) {
|
$featuredImage = "Image: $filename\n";
|
||||||
$safeHashtags = array_map(function($tag) {
|
$featuredImageAlt = "ImageAlt: $alt\n";
|
||||||
return str_replace('#', '#', $tag);
|
|
||||||
}, $tags[0]);
|
|
||||||
$hashtagString = implode(' ', $safeHashtags);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$authorSafe = str_replace("@", "@", $origAuthor);
|
||||||
$content = trim(preg_replace('/\s+/', ' ', $content));
|
return "---\n" .
|
||||||
|
"Title: $title\n" .
|
||||||
$imageMarkdown = '';
|
"TitleSlug: $slug\n" .
|
||||||
foreach ($images as $i => $filename) {
|
"Published: $pub\n" .
|
||||||
$alt = isset($alts[$i]) ? htmlspecialchars($alts[$i], ENT_QUOTES) : '';
|
"Author: $yellowAuthor\n" .
|
||||||
$imageMarkdown .= "\n<img src=\"/media/images/" . $filename . "\" alt=\"" . $alt . "\"/>\n";
|
"Layout: blog\n" .
|
||||||
}
|
"Tag: $yellowTag\n" .
|
||||||
|
"Status: $status\n" .
|
||||||
$featuredImage = '';
|
$featuredImage . $featuredImageAlt .
|
||||||
$featuredImageAlt = '';
|
"---\n\n" .
|
||||||
if (!empty($images)) {
|
$imageMarkdown . "\n" .
|
||||||
$featuredImage = "Image: " . $images[0] . "\n";
|
"> " . $content . "\n>\n" .
|
||||||
if (isset($alts[0]) && !empty($alts[0])) {
|
"> — <a target=\"_blank\" href=\"$origUrl\">$authorSafe</a>";
|
||||||
$featuredImageAlt = "ImageAlt: " . htmlspecialchars($alts[0], ENT_QUOTES) . "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$htmlSafeAuthorName = str_replace('@', '@', $origAuthor);
|
|
||||||
|
|
||||||
$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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getYellowConfig() {
|
// Return status file path
|
||||||
return $this->yellow ? $this->yellow->system : null;
|
private function getStatusFile() {
|
||||||
|
return $this->yellow->system->get("coreWorkerDirectory") . "mastodonapi.status";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private function sendResponse($message, $statusCode = 200) {
|
|
||||||
if (!headers_sent()) {
|
|
||||||
header("HTTP/1.1 $statusCode");
|
|
||||||
header("Content-Type: text/html; charset=UTF-8");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Basis-URL aus der aktuellen Anfrage bestimmen
|
|
||||||
$fullUrl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http")
|
|
||||||
. "://{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}";
|
|
||||||
|
|
||||||
// Trigger-Pfad aus der Yellow-Konfiguration holen
|
|
||||||
$triggerPath = $this->yellow->system->get("mastodonapiTriggerPath");
|
|
||||||
|
|
||||||
// Ziel-URL: Alles vor dem Trigger-Pfad abschneiden
|
|
||||||
$redirectUrl = str_replace($triggerPath, "/", $fullUrl);
|
|
||||||
|
|
||||||
echo "<!DOCTYPE html><html><head>";
|
|
||||||
echo "<meta http-equiv='refresh' content='5;url={$redirectUrl}'>";
|
|
||||||
echo "<title>Mastodon/GoToSocial API</title></head><body>";
|
|
||||||
echo "<p>" . htmlspecialchars($message) . "</p>";
|
|
||||||
echo "<p>You will be redirected in 5 seconds...</p>";
|
|
||||||
echo "</body></html>";
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
1
mastodonapi.status
Normal file
1
mastodonapi.status
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
0000000000
|
||||||
52
readme-de.md
Normal file
52
readme-de.md
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
<p align="right"><a href="https://git.thepipes.ch/simon/yellow-mastodonapi/src/branch/main/readme-de.md">Deutsch</a> <a href="https://git.thepipes.ch/simon/yellow-mastodonapi/src/branch/main/readme.md">English</a></p>
|
||||||
|
|
||||||
|
# Mastodonapi 0.9.9
|
||||||
|
|
||||||
|
Importiere deine Toots in deinen Blog. Getestet mit <a href="https://comam.es/what-is-snac">Snac</a>.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
<a href="https://git.thepipes.ch/simon/yellow-mastodonapi/archive/main.zip">Lade die ZIP-Datei herunter</a> und kopiere sie in deinen system/extensions Ordner. <a target="blank_" href="https://github.com/annaesvensson/yellow-update">Erfahre mehr über Erweiterungen</a>.
|
||||||
|
|
||||||
|
## Funktionsweise
|
||||||
|
|
||||||
|
Das Plugin arbeitet vollautomatisch. Es prüft im Hintergrund regelmäßig, ob neue Beiträge vorhanden sind.
|
||||||
|
|
||||||
|
Wenn du die Seite besuchst, fragt es deine Mastodon-API ab und erstellt automatisch Blog-Beiträge aus deinen Toots. Optional kannst du einen #Hashtag angeben, um nur bestimmte Beiträge zu importieren.
|
||||||
|
|
||||||
|
Es gibt drei Möglichkeiten, den Import auszulösen:
|
||||||
|
|
||||||
|
1. **Automatisch (Lazy Cron):** Sobald ein Besucher deine Website aufruft, prüft das Plugin, ob das Update-Intervall abgelaufen ist und lädt bei Bedarf neue Toots.
|
||||||
|
2. **Über das Terminal:** Du kannst den Import manuell auf dem Server starten mit: php yellow.php mastodonapi
|
||||||
|
3. **Über den System-Cronjob:** Falls dein Host Cronjobs unterstützt, kannst du den Befehl php yellow.php mastodonapi regelmäßig ausführen lassen (z. B. alle 30 Minuten).
|
||||||
|
|
||||||
|
## Einstellungen
|
||||||
|
|
||||||
|
Alle Einstellungen befinden sich in `system/extensions/yellow-system.ini`:
|
||||||
|
|
||||||
|
`MastodonapiInstanceUrl` = deine Mastodon Instanz-URL (z. B. https://mastodon.social)
|
||||||
|
`MastodonapiAccountId` = deine Mastodon User-ID (numerisch, z. B. 1234567890)
|
||||||
|
|
||||||
|
*Finde die ID eines Snac oder Mastodon Accounts: Gib "https://DEINE_INSTANZ_URL/api/v1/accounts/lookup?acct=PROFILNAME" in deinen Browser ein.*
|
||||||
|
*Finde die ID eines GoToSocial-Accounts <a href="https://git.thepipes.ch/simon/yellow-mastodonapi/src/branch/main/help-de.md">(siehe hier)</a>*
|
||||||
|
|
||||||
|
`MastodonapiAccessToken` = optional für einen Mastodon-Account, zwingend für einen GoToSocial-Account <a href="https://git.thepipes.ch/simon/yellow-mastodonapi/src/branch/main/help-de.md">(Wie komme ich zu einem Token?)</a>
|
||||||
|
`MastodonapiUpdateInterval` = Zeitintervall zwischen automatischen Updates in Minuten (Standard: 30)
|
||||||
|
`MastodonapiLimit` = Anzahl der Toots, die pro Durchgang abgerufen werden (Standard: 20)
|
||||||
|
`MastodonapiHashtag` = leer lassen für alle Beiträge; oder mit #hashtag filtern
|
||||||
|
`MastodonapiAuthor` = Name, unter dem die Blog-Beiträge erscheinen sollen
|
||||||
|
`MastodonapiTag` = Tag für die Blog-Beiträge (Standard: Fediverse)
|
||||||
|
`MastodonapiFolder` = Pfad zum Blog-Ordner (Standard: content/2-blog/)
|
||||||
|
`MastodonapiMaxAge` = Alter der Toots in Stunden, die noch importiert werden sollen. 0 = alle verfügbaren Toots
|
||||||
|
`MastodonapiStatus` = Seitenstatus, Standardwert ist public
|
||||||
|
|
||||||
|
Die folgenden Status-Werte werden unterstützt:
|
||||||
|
|
||||||
|
`public` = Seite ist eine normale Seite
|
||||||
|
`draft` = Seite ist nicht sichtbar, Benutzer muss eingeloggt sein, erfordert die <a target="blank_" href="https://github.com/annaesvensson/yellow-draft">Draft-Erweiterung</a>
|
||||||
|
`unlisted` = Seite ist nicht sichtbar, kann aber mit dem richtigen Link aufgerufen werden
|
||||||
|
|
||||||
|
## Entwickler
|
||||||
|
|
||||||
|
Simon Rohr
|
||||||
|
*Fediverse: <a href="https://snac.thepipes.ch/simon">@simon@snac.thepipes.ch</a>*
|
||||||
42
readme-en.md
42
readme-en.md
|
|
@ -1,42 +0,0 @@
|
||||||
<p align="right"><a href="https://codeberg.org/simonpipe/yellow-mastodonapi/src/branch/main/readme.md">Deutsch</a> <a href="https://codeberg.org/simonpipe/yellow-mastodonapi/src/branch/main/readme-en.md">English</a></p>
|
|
||||||
|
|
||||||
# MastodonAPI
|
|
||||||
|
|
||||||
A plugin to import your public Toots via the Mastodon API into your Yellow Blog. The plugin works with Mastodon and with GoToSocial.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
1. Download the ZIP file
|
|
||||||
2. Copy it into the `/system/extensions` directory
|
|
||||||
3. Reload your website – the plugin is now installed
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
The plugin is triggered via a custom **webhook**.
|
|
||||||
When the webhook is called, it fetches your Toots from the Mastodon API and creates blog posts from them.
|
|
||||||
Optionally, you can specify a **#hashtag** to import only matching posts.
|
|
||||||
|
|
||||||
## Settings
|
|
||||||
|
|
||||||
All settings are in `system/extensions/yellow-system.ini`:
|
|
||||||
|
|
||||||
- **MastodonapiInstanceUrl** – your Mastodon instance URL (e.g. `https://mastodon.social`)
|
|
||||||
- **MastodonapiAccountId** - your Mastodon user ID (numeric, e.g. 1234567890)
|
|
||||||
- Find the ID of a Mastodon account: Enter "https://YOUR_INSTANCE_URL/api/v1/accounts/lookup?acct=PROFILNAME" in your browser.
|
|
||||||
- Find the ID of a GoToSocial account <a href="https://codeberg.org/simonpipe/yellow-mastodonapi/src/branch/main/help-gotoso-en.md">(see here)</a>
|
|
||||||
- **MastodonapiAccessToken**
|
|
||||||
- Optional for a Mastodon account
|
|
||||||
- Mandatory for a GoToSocial account <a href="https://codeberg.org/simonpipe/yellow-mastodonapi/src/branch/main/help-gotoso-en.md">(see here)</a>
|
|
||||||
- **MastodonapiLimit** - Default value is 20
|
|
||||||
- **MastodonapiHashtag** – leave empty to import all public posts; set to `#hashtag` to import only matching posts
|
|
||||||
- **MastodonapiAuthor** – author name to use for imported blog posts
|
|
||||||
- **MastodonapiTag** – tag to assign to imported blog posts
|
|
||||||
- **MastodonapiFolder** – path to your blog folder (e.g. `content/1-blog/`)
|
|
||||||
- **MastodonapiTriggerPath** – webhook path, e.g. `/mastodonapi-webhook-CHANGE_THIS_SECRET_KEY`
|
|
||||||
*(Be sure to change it! Anyone with this link can trigger the plugin.)*
|
|
||||||
|
|
||||||
💡 You can also call the webhook via a cron job to run the plugin automatically (e.g., every hour).
|
|
||||||
|
|
||||||
## Developer
|
|
||||||
|
|
||||||
Simon Pipe
|
|
||||||
68
readme.md
68
readme.md
|
|
@ -1,42 +1,52 @@
|
||||||
<p align="right"><a href="https://codeberg.org/simonpipe/yellow-mastodonapi/src/branch/main/readme.md">Deutsch</a> <a href="https://codeberg.org/simonpipe/yellow-mastodonapi/src/branch/main/readme-en.md">English</a></p>
|
<p align="right"><a href="https://git.thepipes.ch/simon/yellow-mastodonapi/src/branch/main/readme-de.md">Deutsch</a> <a href="https://git.thepipes.ch/simon/yellow-mastodonapi/src/branch/main/readme.md">English</a></p>
|
||||||
|
|
||||||
# MastodonAPI
|
# Mastodonapi 0.9.9
|
||||||
|
|
||||||
Ein Plug-in, um deine öffentlichen Toots via Mastodon-API in deinen Yellow Blog zu importieren. Das Plugin funktiert mit Mastodon und mit GoToSocial.
|
Import your toots into your blog. Tested with <a href="https://comam.es/what-is-snac">Snac</a>.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
1. ZIP-Datei herunterladen
|
<a href="https://git.thepipes.ch/simon/yellow-mastodonapi/archive/main.zip">Download ZIP file</a> and copy it into your `system/extensions` folder. <a target="blank_" href="https://github.com/annaesvensson/yellow-update">Learn more about extensions</a>.
|
||||||
2. In das Verzeichnis `/system/extensions` kopieren
|
|
||||||
3. Deine Website neu laden – das Plug-in ist nun installiert
|
|
||||||
|
|
||||||
## Benutzung
|
## How to
|
||||||
|
|
||||||
Das Plug-in wird über einen individuellen **Webhook** aufgerufen.
|
The plugin works fully automatically. It regularly checks in the background if there are new posts.
|
||||||
Beim Aufruf fragt es deine Mastodon-API ab und erstellt aus deinen Toots automatisch Blogbeiträge.
|
|
||||||
Optional kannst du einen **#Hashtag** angeben, um nur bestimmte Beiträge zu importieren.
|
|
||||||
|
|
||||||
## Einstellungen
|
When you visit the site, it queries your Mastodon API and automatically creates blog posts from your Toots. Optionally, you can specify a #Hashtag to import only certain posts.
|
||||||
|
|
||||||
Alle Einstellungen findest du in `system/extensions/yellow-system.ini`:
|
There are three ways to trigger the import:
|
||||||
|
|
||||||
- **MastodonapiInstanceUrl** – URL deiner Mastodon-Instanz (z. B. `https://mastodon.social`)
|
1. **Automatic (Lazy Cron):** As soon as a visitor accesses your website, the plugin checks if the update interval has expired and loads new Toots if necessary.
|
||||||
- **MastodonapiAccountId** – deine Mastodon-User-ID (numerisch, z. B. `1234567890`)
|
2. **From the terminal:** You can manually start the import on the server with: `php yellow.php mastodonapi`
|
||||||
- ID bei einem Mastodon-Account finden: Gib im Browser "https://DEINE_INSTANZ_URL/api/v1/accounts/lookup?acct=PROFILNAME" ein.
|
3. **From the system cronjob:** If your host supports cronjobs, you can have the command `php yellow.php mastodonapi` run regularly (e.g. every 30 minutes).
|
||||||
- ID bei einem GoToSocial-Account finden <a href="https://codeberg.org/simonpipe/yellow-mastodonapi/src/branch/main/help-gotoso-de.md">(siehe hier)</a>
|
|
||||||
- **MastodonapiAccessToken**
|
|
||||||
- Bei einem Mastodon-Account optional
|
|
||||||
- Bei einem GoToSocial-Account pflicht <a href="https://codeberg.org/simonpipe/yellow-mastodonapi/src/branch/main/help-gotoso-de.md">(siehe hier)</a>
|
|
||||||
- **MastodonapiLimit** - Standartwert ist 20
|
|
||||||
- **MastodonapiHashtag** – leer lassen = alle öffentlichen Beiträge importieren; mit `#hashtag` = nur Beiträge mit diesem Tag
|
|
||||||
- **MastodonapiAuthor** – Name, unter dem die Blogposts erscheinen sollen
|
|
||||||
- **MastodonapiTag** – Tag, mit dem die Blogposts im Blog versehen werden
|
|
||||||
- **MastodonapiFolder** – Pfad zum Blog-Ordner (z. B. `content/1-blog/`)
|
|
||||||
- **MastodonapiTriggerPath** – Webhook-Pfad, z. B. `/mastodonapi-webhook-CHANGE_THIS_SECRET_KEY`
|
|
||||||
- Unbedingt anpassen! Wer diesen Link kennt, kann das Plug-in starten.
|
|
||||||
|
|
||||||
💡 Du kannst den Webhook auch per Cronjob regelmäßig aufrufen (z. B. stündlich).
|
## Settings
|
||||||
|
|
||||||
## Entwickler
|
All settings are in `system/extensions/yellow-system.ini`:
|
||||||
|
|
||||||
Simon Pipe
|
`MastodonapiInstanceUrl` = your Mastodon instance URL (e.g. https://mastodon.social)
|
||||||
|
`MastodonapiAccountId` = your Mastodon user ID (numeric, e.g. 1234567890)
|
||||||
|
|
||||||
|
*Find the ID of a Snac or Mastodon account: Enter "https://YOUR_INSTANCE_URL/api/v1/accounts/lookup?acct=PROFILNAME" in your browser.*
|
||||||
|
*Find the ID of a GoToSocial account <a href="https://git.thepipes.ch/simon/yellow-mastodonapi/src/branch/main/help-en.md">(see here)</a>*
|
||||||
|
|
||||||
|
`MastodonapiAccessToken` = optional for a Mastodon account, mandatory for a GoToSocial account <a href="https://git.thepipes.ch/simon/yellow-mastodonapi/src/branch/main/help-en.md">(How do I get a token?)</a>
|
||||||
|
`MastodonapiUpdateInterval` = time interval between automatic updates in minutes (default: 30)
|
||||||
|
`MastodonapiLimit` = number of Toots retrieved per run (default: 20)
|
||||||
|
`MastodonapiHashtag` = leave blank for all posts; or filter with #hashtag
|
||||||
|
`MastodonapiAuthor` = name under which the blog posts should appear
|
||||||
|
`MastodonapiTag` = tag for the blog posts (default: Fediverse)
|
||||||
|
`MastodonapiFolder` = path to the blog folder (default: content/2-blog/)
|
||||||
|
`MastodonapiMaxAge` = age of Toots in hours that should still be imported. 0 = all available Toots
|
||||||
|
`MastodonapiStatus` = page status, default value is public
|
||||||
|
|
||||||
|
The following status values are supported:
|
||||||
|
|
||||||
|
`public` = page is a normal page
|
||||||
|
`draft` = page is not visible, user needs to log in, requires <a target="blank_" href="https://github.com/annaesvensson/yellow-draft">draft extension</a>
|
||||||
|
`unlisted` = page is not visible, but can be accessed with the correct link
|
||||||
|
|
||||||
|
## Developer
|
||||||
|
|
||||||
|
Simon Rohr
|
||||||
|
*Fediverse: <a href="https://snac.thepipes.ch/simon">@simon@snac.thepipes.ch</a>*
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue