Skip to content

Async Signal bots, production-ready by default

Build fast on signal-cli-rest-api with typed helpers, resilient ingestion, and observability baked in. Community-driven; not the official Signal app.

Resilience first
Backpressure, DLQ retries, and rate/circuit breakers keep handlers stable during bursts.
Typed context helpers
Replies, reactions, attachments, locks, and receipts all live on one ergonomic context.
Operations ready
Health + metrics servers, structured logging with PII redaction, and storage options (memory, SQLite, Redis).

Quick start

Deploy a minimal ping bot. The annotations explain each step.

import asyncio
from signal_client import SignalClient, command


@command("!ping")
async def ping(ctx):  # (1)
    await ctx.reply_text("pong")  # (2)


async def main():
    bot = SignalClient()  # (3)
    bot.register(ping)    # (4)
    await bot.start()     # (5)


if __name__ == "__main__":
    asyncio.run(main())
  1. Commands are declared with the @command decorator.
  2. Use typed helpers on ctx for replies, reactions, attachments, and receipts.
  3. SignalClient wires ingestion, queueing, and backpressure for you.
  4. Register handlers before starting the client.
  5. start() connects to signal-cli-rest-api over websocket + REST.

Run it with your environment set: poetry run python examples/ping_bot.py.

Message flow

flowchart TD
    signal[Signal message] --> rest[signal-cli-rest-api]
    rest --> ingest[signal-client websocket ingest]
    ingest --> pressure{Queue pressure?}
    pressure -- yes --> dlq[DLQ and retry]
    pressure -- no --> handler[Handler execution]
    handler --> reply[Reply / attachment / reaction]
    handler --> metrics[Metrics and logging]

Troubleshooting

  • No replies? Confirm SIGNAL_SERVICE_URL matches your websocket host and the bot number is registered.
  • Event loop closed errors? Ensure a single asyncio.run entrypoint and avoid running the script twice in the same shell session.
  • Messages slow to drain? Lower WORKER_SHARD_COUNT or raise QUEUE_SIZE in constrained environments.

Next steps