18 Commits

Author SHA1 Message Date
Alcea 4d1afd6e72 Add files via upload
Read all profileinfo + ava from profileinfo.json
2025-07-20 15:35:22 +02:00
Alcea a2856a6d33 Add files via upload
Router Revamp to serve tsgs and emoji in tags
----

if (preg_match('/^\/' . $username . '\/status\/([a-z0-9\-]+)$/', $uri, $m)) {
    $postId = $m[1];
    $post   = null; $date = ''; $hash = '';
    foreach ($data as $entry)
        foreach ($entry as $d => $c)
            if ("$d-" . substr(md5($c['value']), 0, 8) === $postId) {
                $post = $c; $date = $d; $hash = substr(md5($c['value']), 0, 8); break 2;
            }
    if (!$post) {
        http_response_code(404);
        echo "Not found";
        exit;
    }

    // Construct noteId
    $noteId = "$baseUrl/$username/status/{$date}-$hash";

    // Extract content
    $content = $post['value'];

    // Prepare tags (hashtags, mentions, emoji tags)
    $tags = [];
    $emojiTags = [];
    $hashtags = [];
    $mentions = [];

    // Extract hashtags: #hashtag
    preg_match_all('/#(\w+)/', $content, $hashtags);
    foreach ($hashtags[1] as $hashtag) {
        $tags[] = [
            "type" => "Hashtag",
            "name" => "#$hashtag",
            "href" => "$baseUrl/tags/$hashtag"
        ];
    }

    // Extract mentions: @username
    preg_match_all('/@(\w+)/', $content, $mentions);
    foreach ($mentions[1] as $mention) {
        // Not directly used in tags, but can be added if needed
    }

    // Extract emoji tags: :emoji:
    preg_match_all('/:([\w\+\-]+):/', $content, $emojiTags);
    foreach ($emojiTags[1] as $emoji) {
        // Assuming emoji images are stored on a path like "/z_files/emojis/{emoji}.gif"
        $tags[] = [
            "type" => "Emoji",
            "name" => ":$emoji:",
            "icon" => [
                "type" => "Image",
                "mediaType" => "image/gif",
                "url" => "$baseUrl/z_files/emojis/$emoji.gif"
            ]
        ];
    }

    // Determine if client accepts JSON
    $accept = $_SERVER['HTTP_ACCEPT'] ?? '';
    if (str_contains($accept, 'application/activity+json') || str_contains($accept, 'application/ld+json')) {
        header('Content-Type: application/activity+json');
        echo json_encode([
            '@context'     => [
                'https://www.w3.org/ns/activitystreams',
                [
                    'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers',
                    'toot' => 'http://joinmastodon.org/ns#',
                    'featured' => [
                        '@id' => 'toot:featured',
                        '@type' => '@id'
                    ]
                ]
            ],
            'id'           => $noteId,
            'type'         => 'Note',
            'published'    => date(DATE_ATOM, strtotime($date)),
            'attributedTo' => "$baseUrl/$username",
            'to'           => ['https://www.w3.org/ns/activitystreams#Public'],
            'content'      => $content,
            'contentMap'   => [
                'und' => $content,
                'html' => nl2br($content) // Convert newlines to <br> for HTML
            ],
            'tag'          => $tags,
        ], JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
        exit;
    }

    // Otherwise, return an HTML redirect after 1 second
    header('Content-Type: text/html');
    $redirectUrl = "https://alceawis.com#" . $noteId;
    echo <<<HTML
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Redirecting…</title>
    <meta http-equiv="refresh" content="1; url=$redirectUrl">
</head>
<body>
    <p>Redirecting to <a href="$redirectUrl">$redirectUrl</a>…</p>
</body>
</html>
HTML;
    exit;
}


if ($uri === "/$username/followers" || $uri === "/$username/followers/") {
    header('Content-Type: application/activity+json');
    header('Vary: Accept');
    $followers = file_exists(__DIR__ . '/followers.json') ? json_decode(file_get_contents(__DIR__ . '/followers.json'), true) : [];
    echo json_encode(['@context'=>'https://www.w3.org/ns/activitystreams','id'=>"$baseUrl/$username/followers",'type'=>'OrderedCollection','totalItems'=>count($followers),'orderedItems'=>$followers], JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
    exit;
}
2025-07-20 12:23:17 +02:00
Alcea 441d6307fe Add files via upload
Pull profile info from json
(Attempted some fixes for shorthand remote server emoj rendering suchas 🤣 :RJ_RedJohn_TheMentalist: :confused_dog: 

But alas...
https://alceawis.com/alceawis/status/20250719-e29c54df
https://alceawis.com/alceawis/status/20250719-1ab45011
2025-07-19 22:32:48 +02:00
Alcea 41d0596d66 Add files via upload
Replace status redirect handler ro resirect to instsnce ststus AND serve json for 2 seconds

if (preg_match('/^\/' . $username . '\/status\/([a-z0-9\-]+)$/', $uri, $m)) {
    $postId = $m[1];
    $post   = null; $date = ''; $hash = '';
    foreach ($data as $entry)
        foreach ($entry as $d => $c)
            if ("$d-" . substr(md5($c['value']),0,8) === $postId) {
                $post = $c; $date = $d; $hash = substr(md5($c['value']),0,8); break 2;
            }
    if (!$post) {
        http_response_code(404);
        echo "Not found";
        exit;
    }

    $noteId = "$baseUrl/$username/status/{$date}-$hash";

    // Determine if client accepts JSON
    $accept = $_SERVER['HTTP_ACCEPT'] ?? '';
    if (str_contains($accept, 'application/activity+json') || str_contains($accept, 'application/ld+json')) {
        header('Content-Type: application/activity+json');
        echo json_encode([
            '@context'     => 'https://www.w3.org/ns/activitystreams',
            'id'           => $noteId,
            'type'         => 'Note',
            'published'    => date(DATE_ATOM, strtotime($date)),
            'attributedTo' => "$baseUrl/$username",
            'to'           => ['https://www.w3.org/ns/activitystreams#Public'],
            'content'      => $post['value'],
            'contentMap'   => ['und' => $post['value']],
        ], JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
        exit;
    }

    // Otherwise, return an HTML redirect after 1 second
    header('Content-Type: text/html');
    $redirectUrl = "https://alceawis.com#" . $noteId;
    echo <<<HTML
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Redirecting…</title>
    <m eta ht.  tp-equiv="refresh" content="1; url=$re. directUrl">
</head>
<body>
    <p>Redirecting to <a href="$redirectUrl">$redirectUrl</a>…</p>
</body>
</html>
HTML;
    exit;
}
2025-07-19 09:30:59 +02:00
Alcea 7226b2c6a2 Add files via upload
Sending Reply notifications works now !
--------------

/* ---------- build outbox ---------- */


$lastNoteId = null;

foreach ($data as $entry) {
    foreach ($entry as $date => $content) {
        $hash = substr(md5($content['value']), 0, 8);
        $text = formatEmojis($content['value']);

        $inReplyTo = null;
        $mentionTag = null;
        $mentionAcct = null;

        if (preg_match('/💬(https?:\/\/[^\s💬]+)💬/', $text, $match)) {
            $inReplyTo = $match[1];
            $lines = explode("\n", $text);
            array_shift($lines);
            $text = implode("\n", $lines);
        }

        // Extract hashtags
        $hashtags = array_filter(array_map('trim', explode(',', $content['hashtags'] ?? '')));

        // Handle @mention if replying to someone
        if ($inReplyTo && preg_match('~https?://([^/]+)/@([a-zA-Z0-9_]+)~', $inReplyTo, $matches)) {
            $mentionDomain = $matches[1];
            $mentionUser = $matches[2];
            $mentionAcct = "$mentionUser@$mentionDomain";
            $mentionUrl = "https://$mentionDomain/@$mentionUser";
            $mentionText = "@$mentionAcct ";

            if (strpos($text, $mentionText) !== 0) {
                $text = $mentionText . $text;
            }

            // Create Mention tag object
            $mentionTag = [
                'type' => 'Mention',
                'href' => $mentionUrl,
                'name' => "@$mentionAcct"
            ];
        }

        // Format quotes and links
        $quotedText = formatQuotes($text);
        $htmlText = preg_replace(
            '~(https?://[^\s<]+)~i',
            '<a href="$1" target="_blank" rel="nofollow noopener noreferrer">$1</a>',
            $quotedText
        );

        // Convert hashtags to tag links
        $htmlText = preg_replace_callback('/#([\w-]+)/', function($matches) use ($domain) {
            $tag = $matches[1];
            $url = "https://$domain/tags/" . urlencode($tag);
            return "<a href=\"$url\" rel=\"tag nofollow noopener noreferrer\">#" . htmlspecialchars($tag) . "</a>";
        }, $htmlText);

        $htmlText = nl2br($htmlText);

        // Build hashtag tag objects
        $tags = array_map(function($tag) use ($domain) {
            return [
                'type' => 'Hashtag',
                'name' => "#$tag",
                'href' => "https://$domain/tags/$tag"
            ];
        }, $hashtags);

        // Extract emoji shortcodes and create tag objects
        preg_match_all('/:([a-zA-Z0-9_]+):/', $content['value'], $emojiMatches);
        foreach ($emojiMatches[1] as $shortcode) {
            $tags[] = [
                'type' => 'Emoji',
                'name' => ":$shortcode:",
                'icon' => [
                    'type' => 'Image',
                    'mediaType' => 'image/gif',
                    'url' => "https://$domain/z_files/emojis/$shortcode.gif"
                ]
            ];
        }

        // Add mention tag if applicable
        if ($mentionTag) {
            $tags[] = $mentionTag;
        }

        // Compose Note ID
        $noteId = "$baseUrl/$username/status/{$date}-$hash";

        // Build the Note object
        $note = [
            'id' => $noteId,
            'type' => 'Note',
            'published' => date(DATE_ATOM, strtotime($date)),
            'attributedTo' => "$baseUrl/$username",
            'to' => ['https://www.w3.org/ns/activitystreams#Public'],
            'content' => $htmlText,
            'contentMap' => [
                'und' => $text,
                'html' => $htmlText,
            ],
            'tag' => $tags,
        ];

        // Add inReplyTo if exists
        if ($inReplyTo) {
            $note['inReplyTo'] = $inReplyTo;
        }

        $outboxItems[] = $note;

        // Send activity only if not already pushed
        if (!in_array($noteId, $pushed)) {
            sendCreateActivity($note);
            $pushed[] = $noteId;
            file_put_contents($pushedFile, json_encode($pushed, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
        }
    }
}

// Log last pushed note
if (!empty($pushed)) {
    $lastPushedUrl = end($pushed);

    foreach ($outboxItems as $item) {
        if ($item['id'] === $lastPushedUrl) {
            $noteText = $item['contentMap']['und'] ?? '[No text]';
            $inReplyTo = $item['inReplyTo'] ?? 'None';

            if (!file_exists('outbox.log')) {
                file_put_contents('outbox.log', '');
            }

            $logEntry = sprintf(
                "[%s] New post created\nStatus URL: %s\nReply To: %s\nContent:\n%s\n\n",
                date('Y-m-d H:i:s'),
                $lastPushedUrl,
                $inReplyTo,
                $noteText
            );

            //file_put_contents('outbox.log', $logEntry, FILE_APPEND | LOCK_EX);
            break;
        }
    }
}
2025-07-19 00:02:23 +02:00
Alcea d6fea241fa Add files via upload
Reimplemented emoji array tags *phew*

foreach ($data as $entry) {
    foreach ($entry as $date => $content) {
        $hash = substr(md5($content['value']), 0, 8);
        $text = formatEmojis($content['value']);

        $inReplyTo = null;
        if (preg_match('/💬(https?:\/\/[^\s💬]+)💬/', $text, $match)) {
            $inReplyTo = $match[1];
            $lines = explode("\n", $text);
            array_shift($lines);
            $text = implode("\n", $lines);
        }

        $hashtags = array_filter(array_map('trim', explode(',', $content['hashtags'] ?? '')));
        //$escapedText = htmlspecialchars($text);
        //$quotedText = formatQuotes($escapedText);
        $quotedText = formatQuotes($text);
        $htmlText = preg_replace(
            '~(https?://[^\s<]+)~i',
            '<a href="$1" target="_blank" rel="nofollow noopener noreferrer">$1</a>',
            $quotedText
        );
        $htmlText = preg_replace_callback('/#([\w-]+)/', function($matches) use ($domain) {
            $tag = $matches[1];
            $url = "https://$domain/tags/" . urlencode($tag);
            return "<a href=\"$url\" rel=\"tag nofollow noopener noreferrer\">#" . htmlspecialchars($tag) . "</a>";
        }, $htmlText);
        $htmlText = nl2br($htmlText);
        $tags = array_map(function($tag) use ($domain) {
            return [
                'type' => 'Hashtag',
                'name' => "#$tag",
                'href' => "https://$domain/tags/$tag"
            ];
        }, $hashtags);

        preg_match_all('/:([a-zA-Z0-9_]+):/', $content['value'], $emojiMatches);
        $emojiTags = [];
        foreach ($emojiMatches[1] as $shortcode) {
            $emojiTags[] = [
                'type' => 'Emoji',
                'name' => ":$shortcode:",
                'icon' => [
                    'type' => 'Image',
                    'mediaType' => 'image/gif',
                    'url' => "https://$domain/z_files/emojis/$shortcode.gif"
                ]
            ];
        }

        $tags = array_merge($tags, $emojiTags);

        $noteId = "$baseUrl/$username/status/{$date}-$hash";

        $note = [
            'id' => $noteId,
            'type' => 'Note',
            'published' => date(DATE_ATOM, strtotime($date)),
            'attributedTo' => "$baseUrl/$username",
            'to' => ['https://www.w3.org/ns/activitystreams#Public'],
            'content' => $htmlText,
            'contentMap' => [
                'und' => $text,
                'html' => $htmlText,
            ],
            'tag' => $tags,
        ];

        if ($inReplyTo) {
            $note['inReplyTo'] = $inReplyTo;
        }

        $outboxItems[] = $note;

        if (!in_array($noteId, $pushed)) {
            sendCreateActivity($note);
            $pushed[] = $noteId;
            file_put_contents($pushedFile, json_encode($pushed, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
        }
    }
}
2025-07-18 04:19:10 +02:00
Alcea 91b153b721 Add files via upload
Allow replying if a 💬url💬 is in text body (to it)

/* ---------- build outbox ---------- */
foreach ($data as $entry) {
    foreach ($entry as $date => $content) {
        $hash      = substr(md5($content['value']), 0, 8);
        $text      = formatEmojis($content['value']);

        // 💬 inReplyTo detection
        $inReplyTo = null;
        if (preg_match('/💬\s*(https?:\/\/[^\s💬]+)\s*💬/', $text, $match)) {
            $inReplyTo = trim($match[1]);
            $text = trim(str_replace($match[0], '', $text)); // optionally remove 💬URL💬 from post
        }

        $hashtags  = array_filter(array_map('trim', explode(',', $content['hashtags'] ?? '')));
        $quoted    = formatQuotes($text);
        $htmlText  = preg_replace('~(https?://[^\s<]+)~i', '<a href="$1" target="_blank" rel="nofollow noopener noreferrer">$1</a>', $quoted);
        $htmlText  = preg_replace_callback('/#([\w-]+)/', fn($m) => "<a href=\"https://$GLOBALS[domain]/tags/" . urlencode($m[1]) . "\" rel=\"tag nofollow noopener noreferrer\">#" . htmlspecialchars($m[1]) . "</a>", $htmlText);
        $htmlText  = nl2br($htmlText);

        $tags = array_map(fn($tag) => ['type'=>'Hashtag','name'=>"#$tag",'href'=>"https://$GLOBALS[domain]/tags/$tag"], $hashtags);

        preg_match_all('/:([a-zA-Z0-9_]+):/', $content['value'], $emo);
        foreach ($emo[1] as $sc)
            $tags[] = ['type'=>'Emoji','name'=>":$sc:",'icon'=>['type'=>'Image','mediaType'=>'image/gif','url'=>"https://$GLOBALS[domain]/z_files/emojis/$sc.gif"]];

        $noteId = "$baseUrl/$username/status/{$date}-$hash";

        $note = [
            'id'           => $noteId,
            'url'          => $noteId,
            'type'         => 'Note',
            'published'    => date(DATE_ATOM, strtotime($date)),
            'attributedTo' => "$baseUrl/$username",
            'to'           => ['https://www.w3.org/ns/activitystreams#Public'],
            'content'      => $htmlText,
            'contentMap'   => ['und' => $text,'html' => $htmlText],
            'tag'          => $tags,
            'locked'       => false,
            'bot'          => false,
            'discoverable' => true,
            'group'        => false,
            'manuallyApprovesFollowers' => false,
        ];

        if ($inReplyTo) {
            $note['inReplyTo'] = $inReplyTo;
        }

        $outboxItems[] = $note;

        if (!in_array($noteId, $pushed)) {
            sendCreateActivity($note);
            $pushed[] = $noteId;
        }
    }
}
file_put_contents($pushedFile, json_encode($pushed, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
2025-07-16 11:29:07 +02:00
Alcea 89050347a4 Add files via upload
Force discoverability in posts
    'locked'        => false,
    'bot'           => false,
    'discoverable'  => true,
    'group'         => false,
2025-07-15 03:21:16 +02:00
Alcea 53dc0d5107 Add files via upload
Allow direct mentions
2025-07-13 18:31:20 +02:00
Alcea 3ba1c057a5 Add files via upload 2025-07-13 09:57:16 +02:00
Alcea 636ac2c446 Add files via upload 2025-07-12 22:58:20 +02:00
Alcea ca37cb4966 Add files via upload
Added interaction logging and a rudimentary notification system
2025-07-12 14:48:29 +02:00
Alcea bfceb1d47c Add files via upload
Added htmlspecialchars fix in quotes
2025-07-12 10:49:31 +02:00
Alcea 2174c47f4f Add files via upload
Quote Posts added
2025-07-12 07:16:33 +02:00
Alcea 473fed886a Add files via upload
Added PostGET for individual statuses via hash
2025-07-11 21:05:57 +02:00
Alcea 8c4f72b50f Add files via upload 2025-07-11 15:45:02 +02:00
alceawisteria c45a404e3a Upload files to "/"
Can follow user remotely now
2025-07-09 23:05:20 +02:00
alceawisteria a973aaeedf Upload files to "/" 2025-07-08 20:15:51 +02:00