A community library of hooks for Pure Blog.

Post Notes to Mastodon

Author: Kev Quirk |  Hook: on_post_published

Automatically syndicates posts using a notes layout to your Mastodon account when published. Images in the note are uploaded as media attachments. The status is trimmed to fit Mastodon's 500-character limit, with the permalink appended.

Setup

  1. Generate an access token in your Mastodon instance under Settings → Development → New Application
  2. Replace the placeholder values at the top of the snippet

⚠️ If your site's source code is publicly available, make sure your Mastodon token isn't.

define('MASTODON_INSTANCE', 'https://your.mastodon.instance');
define('MASTODON_TOKEN',    'your-access-token');

function mastodon_upload_image(string $filePath): ?string
{
    $ch = curl_init(MASTODON_INSTANCE . '/api/v1/media');
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . MASTODON_TOKEN],
        CURLOPT_POSTFIELDS     => ['file' => new CURLFile($filePath)],
    ]);
    $response = curl_exec($ch);
    if (!$response) {
        return null;
    }
    $data = json_decode((string) $response, true);
    return isset($data['id']) ? (string) $data['id'] : null;
}

function on_post_published(string $slug): void
{
    $post = get_post_by_slug($slug);
    if (!$post || ($post['layout'] ?? '') !== 'notes') {
        return;
    }

    $config  = load_config();
    $baseUrl = rtrim($config['base_url'] ?? get_base_url(), '/');
    $html    = render_markdown($post['content'], ['post_title' => '']);

    $mediaIds  = [];
    $urlPrefix = base_path();
    preg_match_all('/<img[^>]+src=["\']([^"\']+)["\']/', $html, $matches);
    foreach ($matches[1] as $src) {
        $urlPath = $src;
        if ($urlPrefix !== '' && str_starts_with($urlPath, $urlPrefix)) {
            $urlPath = substr($urlPath, strlen($urlPrefix));
        }
        $fsPath = PUREBLOG_BASE_PATH . '/' . ltrim($urlPath, '/');
        if (is_file($fsPath)) {
            $id = mastodon_upload_image($fsPath);
            if ($id !== null) {
                $mediaIds[] = $id;
            }
        }
    }

    $html      = preg_replace('/<\/p>\s*/i', "\n\n", $html) ?? $html;
    $html      = preg_replace('/<br\s*\/?>\s*/i', "\n", $html) ?? $html;
    $permalink = $baseUrl . '/' . $post['slug'];
    $status    = trim(strip_tags($html));

    if (strlen($status) + strlen($permalink) + 2 > 500) {
        $status = substr($status, 0, 500 - strlen($permalink) - 5) . '…';
    }
    $status .= "\n\n" . $permalink;

    $ch = curl_init(MASTODON_INSTANCE . '/api/v1/statuses');
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => [
            'Authorization: Bearer ' . MASTODON_TOKEN,
            'Content-Type: application/json',
        ],
        CURLOPT_POSTFIELDS => json_encode(array_filter([
            'status'    => $status,
            'media_ids' => $mediaIds ?: null,
        ])),
    ]);
    curl_exec($ch);
}

← Back to all hooks