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.
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.
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).
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())
- Commands are declared with the
@commanddecorator. - Use typed helpers on
ctxfor replies, reactions, attachments, and receipts. SignalClientwires ingestion, queueing, and backpressure for you.- Register handlers before starting the client.
start()connects tosignal-cli-rest-apiover 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_URLmatches your websocket host and the bot number is registered. - Event loop closed errors? Ensure a single
asyncio.runentrypoint and avoid running the script twice in the same shell session. - Messages slow to drain? Lower
WORKER_SHARD_COUNTor raiseQUEUE_SIZEin constrained environments.
Next steps¶
- Read Getting started for full setup and health checks.
- Try more Examples with annotated snippets pulled from the source.
- Explore Advanced usage for middleware, locks, and observability.
- Review Operations & deployment before running in production.