One message for four backends

In this tutorial you will build one rich message and render it for Slack, Discord, Symphony, and Telegram. No credentials or network connection are required.

Build the message

Chatom’s frontend consists of backend-independent models and formatting nodes. Build a status message with the fluent chatom.MessageBuilder:

def build_status_message():
    """Build a backend-independent status message."""
    return (
        MessageBuilder()
        .heading("Deployment status", level=2)
        .paragraph("The same message object is ready for every configured backend.")
        .bold("Environment: ")
        .text("production")
        .line_break()
        .bullet_list(["API: healthy", "Workers: healthy"])
        .table(
            [["API", "healthy"], ["Workers", "healthy"]],
            headers=["Service", "State"],
        )
        .build()
    )

The object contains structure, not platform markup. The heading, list, and table remain typed nodes until rendering.

Render for each backend

Render the same object with each backend name:

def main():
    message = build_status_message()
    for backend in ("slack", "discord", "symphony", "telegram"):
        print(f"--- {backend} ---")
        print(message.render_for(backend))

Run the complete example:

python -m chatom.examples.unified_frontend

Notice how the outputs differ:

  • Slack uses mrkdwn and a fixed-width table.

  • Discord uses Discord-flavored Markdown and a Markdown table.

  • Symphony uses MessageML elements.

  • Telegram uses its supported HTML subset and a preformatted table.

No conditional rendering logic appears in the application. chatom.FormattedMessage.render_for() selects the platform format at the boundary.

Send the rendered result

A connected backend accepts the same channel-and-content call shape:

channel = await backend.fetch_channel(name="operations")
content = message.render_for(backend.name)
await backend.send_message(channel, content)

Only backend construction and credentials vary. Message construction, channel lookup, and sending use the common frontend.