Posting Status

Currently minimal_activitypub only implements standard status posting (polls and direct messages / DMs are not currently implemented).

Posting Text Only Status

The sample code below shows a very simple text only status being posted.

from minimal_activitypub.client_2_server import ActivityPub
from minimal_activitypub import Status

async def post_status(instance_api: ActivityPub, status: str) -> Status:
    posted_status = await instance_api.post_status(status=status)
    return posted_status

Posting Status That Includes Media

The sample code below shows how to post a status with images attached. Basically the images/media needs to be posted first and the media id(s) need to be included when posting the status itself.

import aiofiles
import magic
from minimal_activitypub.client_2_server import ActivityPub
from minimal_activitypub import Status
from typing import Dict, Any

async def post_status_with_image(instance_api: ActivityPub, status: str, image_path: str) -> Dict[str, Any]:
    # First determine mime-type
    mime_type = magic.from_file(image_path, mime=True)

    # Post media
    async with aiofiles.open(image_path, "rb") as image_file:
        posted_media = await instance_api.post_media(file=image_file, mime_type=mime_type)

    # Determine media id to include when posting status
    media_id = posted_media["id"]

    # Post actual status and include image
    posted_status = await instance_api.post_status(status=status, media_ids=[media_id])
    return posted_status

Advanced Posting Options

Posting with Multiple Media Files

from minimal_activitypub.client_2_server import ActivityPub
from minimal_activitypub import Visibility

async def post_with_multiple_images(instance_api: ActivityPub, status: str, image_paths: list[str]) -> dict:
    media_ids = []

    for image_path in image_paths:
        mime_type = magic.from_file(image_path, mime=True)
        async with aiofiles.open(image_path, "rb") as image_file:
            posted_media = await instance_api.post_media(file=image_file, mime_type=mime_type)
            media_ids.append(posted_media["id"])

    # Post status with all media
    posted_status = await instance_api.post_status(
        status=status,
        media_ids=media_ids,
        visibility=Visibility.PUBLIC
    )
    return posted_status

Posting Sensitive Content with Spoiler Text

from minimal_activitypub.client_2_server import ActivityPub

async def post_sensitive_content(instance_api: ActivityPub, status: str, spoiler_text: str, image_path: str) -> dict:
    mime_type = magic.from_file(image_path, mime=True)

    async with aiofiles.open(image_path, "rb") as image_file:
        posted_media = await instance_api.post_media(
            file=image_file,
            mime_type=mime_type,
            description="Image description for accessibility"
        )

    posted_status = await instance_api.post_status(
        status=status,
        media_ids=[posted_media["id"]],
        sensitive=True,
        spoiler_text=spoiler_text
    )
    return posted_status

Scheduling a Post

from datetime import datetime, timedelta
from pytz import UTC
from minimal_activitypub.client_2_server import ActivityPub

async def schedule_post(instance_api: ActivityPub, status: str, hours_from_now: int = 24) -> dict:
    # Schedule post for 24 hours from now
    scheduled_time = datetime.now(UTC) + timedelta(hours=hours_from_now)

    posted_status = await instance_api.post_status(
        status=status,
        scheduled_at=scheduled_time
    )
    return posted_status

Method Signatures

ActivityPub.post_status(status: str, visibility: Visibility = Visibility.PUBLIC, media_ids: list[str] | None = None, sensitive: bool = False, spoiler_text: str | None = None, scheduled_at: datetime | None = None) -> dict

Posts a status to the fediverse with various options.

Parameters: - status: The text content of the status - visibility: One of public, unlisted, private, or direct (default: public) - media_ids: List of media IDs to attach to the status - sensitive: Whether the content should be marked as sensitive (default: False) - spoiler_text: Content warning text that hides the status behind a warning - scheduled_at: Datetime to schedule the post for future publication

Returns: A dictionary containing the posted status information.

ActivityPub.post_media(file: IO[bytes], mime_type: str, description: str | None = None, focus: tuple[float, float] | None = None) -> dict

Uploads a media file (image or video) to the instance.

Parameters: - file: The file object to upload (must be opened in binary mode) - mime_type: MIME type of the file (e.g., image/jpeg, image/png, video/mp4) - description: Alt text for accessibility - focus: Focal point coordinates as (x, y) tuple, each between -1.0 and 1.0

Returns: A dictionary containing the uploaded media information including the id needed for posting.

Complete Example

Here's a complete example that demonstrates the full posting workflow:

import asyncio
import aiofiles
import magic
from httpx import AsyncClient
from minimal_activitypub.client_2_server import ActivityPub
from minimal_activitypub import Visibility

async def main():
    instance_url = "https://mastodon.social"
    access_token = "your_access_token_here"

    async with AsyncClient() as client:
        # Create ActivityPub instance
        ap = ActivityPub(
            instance=instance_url,
            client=client,
            access_token=access_token
        )

        # Post a simple text status
        simple_status = await ap.post_status(
            status="Hello from minimal-activitypub! 🐘"
        )
        print(f"Posted simple status: {simple_status.get('url')}")

        # Post a status with an image
        image_path = "example.jpg"
        mime_type = magic.from_file(image_path, mime=True)

        async with aiofiles.open(image_path, "rb") as image_file:
            media = await ap.post_media(
                file=image_file,
                mime_type=mime_type,
                description="A beautiful sunset over the mountains"
            )

        status_with_image = await ap.post_status(
            status="Check out this beautiful sunset! 🌅",
            media_ids=[media["id"]],
            visibility=Visibility.UNLISTED
        )
        print(f"Posted status with image: {status_with_image.get('url')}")

if __name__ == "__main__":
    asyncio.run(main())

Notes

  • Media files must be uploaded before posting a status that includes them.
  • The maximum number of media attachments varies by instance (typically 4 for Mastodon).
  • File size limits also vary by instance configuration.
  • Scheduled posts must be at least 5 minutes in the future.
  • Always include alt text (description) for images to improve accessibility.