First steps

amoorion posts once to many platforms, reads them and answers from one place. Everything it can do is a method; the CLI, the HTTP API and the MCP server are only ways to call them. This notebook calls them from Python, on dryrun, a platform that sends nothing and only remembers.

from amoorion import Amoorion

# A sandbox for this whole notebook: secrets only from variables, the inbox and
# the dataset in a temporary folder. Nothing here can touch your real setup.
Amoorion.init()
PosixPath('/tmp/amoorion-m1v4ncht')

Which platforms are installed, and what each can do:

from amoorion.methods import Plugins

# Methods can also be used directly, without an `Amoorion`.
for plugin in Plugins()():
    print(f"{plugin['name']:16} {', '.join(plugin['capabilities'])}")
blogger          post
bluesky          delete, post, read, reply
discord          delete, post, read, reply
discord-webhook  delete, post
dryrun           delete, post, read, reply
email            post, read, reply
facebook         delete, post, read, reply
mastodon         delete, post, read, reply
matrix           delete, post, read, reply
ntfy             post, read
telegram         delete, post, read, reply
tumblr           delete, post, read
x                delete, post, read, reply

Publish one post. to names the platforms; each gives its own result, and one failing doesn’t stop the others:

from amoorion.methods import Publish

# One platform doesn't exist: the other still gets its post.
results = Publish()(
    title="Hello",
    text="first post from amoorion",
    tags=["amoorion"],
    to=[
        "dryrun",
        "nowhere",
    ],
)

for result in results:
    print(
        result["platform"],
        "ok" if result["ok"] else f"failed: {result['error']}",
    )
dryrun ok
nowhere failed: no plugin named 'nowhere'

Read it back, and answer it by its id:

from amoorion.methods import Read, Reply

# The newest message.
[latest] = Read()(
    platform="dryrun",
    limit=1,
)

print(latest["text"])

# An answer to it, by its id.
answer = Reply()(
    platform="dryrun",
    to=latest["id"],
    text="and a reply",
)

print(
    "reply to",
    answer["reply_to"],
)
Hello

first post from amoorion
reply to 1

What read fetched is kept in the inbox, across platforms (a SQLite file, AMOORION_INBOX in .env):

from amoorion.methods import InboxMethod

# All that was read so far.
for message in InboxMethod()():
    print(
        message["platform"],
        "|",
        message["text"].replace(
            "\n",
            " / ",
        ),
    )
dryrun | Hello /  / first post from amoorion

The same from the command line:

uv run amoorion publish --text "hello" --to dryrun
uv run amoorion --json read --platform dryrun
uv run amoorion inbox

The CLI’s main takes the same arguments:

from amoorion.cli import main

# The same through the command line.
exit_code = main(
    [
        "--json",
        "publish",
        "--text",
        "from the CLI",
        "--to",
        "dryrun",
    ]
)
[
  {
    "platform": "dryrun",
    "ok": true,
    "message": {
      "platform": "dryrun",
      "text": "from the CLI",
      "id": "3",
      "url": null,
      "author": null,
      "reply_to": null,
      "created_at": "2026-10-31T07:55:15.551149Z"
    },
    "error": null
  }
]