Signal messaging library for Python using Presage (Rust)
Project description
ReplyFast
Signal messaging library for Python using Presage (Rust).
Installation
uv pip install replyfast
# With optional dependencies
uv pip install replyfast[scheduler] # For cron scheduling
uv pip install replyfast[qrcode] # For QR code display
uv pip install replyfast[all] # Everything
Contents
Quick Start
from replyfast import SignalClient
# Create client
client = SignalClient("./data")
# Link as secondary device (first time only)
def on_url(url):
print(f"Scan this QR code with Signal app: {url}")
client.link_device_sync("MyDevice", on_url)
# Send a message
client.send_message_sync("recipient-uuid", "Hello!")
# Or find contact by phone/name first
contact = client.find_contact_by_phone_sync("+1234567890")
if contact:
client.send_message_sync(contact.uuid, "Hello!")
# Receive messages
def on_message(msg):
if msg.is_queue_empty:
print("Initial sync complete")
return True
print(f"From: {msg.sender}, Body: {msg.body}")
return True # Return True to continue, False to stop
client.receive_messages_sync(on_message)
API Reference
SignalClient
| Method | Description |
|---|---|
is_registered_sync() |
Check if device is linked |
link_device_sync(name, callback) |
Link as secondary device |
send_message_sync(uuid, message) |
Send message to contact |
send_group_message_sync(group_id, message) |
Send message to group |
receive_messages_sync(callback) |
Receive messages with callback |
get_contacts_sync() |
List synced contacts |
get_groups_sync() |
List groups |
find_contact_by_phone_sync(phone) |
Find contact by phone number |
find_contacts_by_name_sync(name) |
Find contacts by name |
whoami_sync() |
Get account info |
Message
| Field | Type | Description |
|---|---|---|
sender |
str |
Sender's UUID |
body |
str | None |
Message text |
timestamp |
int |
Unix timestamp (ms) |
group_id |
str | None |
Group ID if group message |
is_read_receipt |
bool |
Is read receipt |
is_typing_indicator |
bool |
Is typing indicator |
is_queue_empty |
bool |
Initial sync complete marker |
Signal Handling
The library handles SIGINT (Ctrl+C) cleanly. When you press Ctrl+C during receive_messages_sync(), it will:
- Stop the receive loop gracefully
- Raise
KeyboardInterruptin Python - Allow cleanup code to run
try:
client.receive_messages_sync(on_message)
except KeyboardInterrupt:
pass # Clean exit
print("Stopped")
Scheduler
Send messages at scheduled times using cron syntax.
Scheduler with Message Receiving
When running a bot that both receives messages and runs scheduled tasks, start the scheduler after the initial sync is complete (indicated by is_queue_empty):
import time
from replyfast import SignalClient, Scheduler
client = SignalClient("./data")
scheduler = Scheduler()
# Define scheduled task
def periodic_task():
client.send_message_sync("recipient-uuid", "Scheduled message!")
# Register jobs before starting
scheduler.register(
"*/5 * * * *", # Every 5 minutes
periodic_task,
name="periodic-task"
)
# Message handler that starts scheduler after sync
def on_message(msg):
# Start scheduler once initial sync is complete
if msg.is_queue_empty:
print("Ready to receive messages")
scheduler.start() # Start scheduler in background
return True
if msg.body:
print(f"From: {msg.sender}: {msg.body}")
# Handle commands here
return True
# Run with reconnection loop for long-running bots
reconnect_delay = 5
while True:
try:
client.receive_messages_sync(on_message)
break # Normal exit
except KeyboardInterrupt:
break # Ctrl+C
except Exception as e:
print(f"Connection error: {e}")
print(f"Reconnecting in {reconnect_delay}s...")
time.sleep(reconnect_delay)
reconnect_delay = min(reconnect_delay * 2, 300) # Max 5 min
scheduler.stop()
print("Stopped")
Scheduler Only (No Message Receiving)
from replyfast import SignalClient, Scheduler
client = SignalClient("./data")
scheduler = Scheduler()
def send_greeting():
contact = client.find_contact_by_phone_sync("+1234567890")
if contact:
client.send_message_sync(contact.uuid, "Good morning!")
scheduler.register(
"0 9 * * *", # cron: minute hour day month weekday
send_greeting,
name="morning-greeting"
)
# Run scheduler (blocking)
scheduler.run()
# Or run in background
scheduler.start()
# ... do other things ...
scheduler.stop()
Cron Expression Format
┌───────────── minute (0-59)
│ ┌───────────── hour (0-23)
│ │ ┌───────────── day of month (1-31)
│ │ │ ┌───────────── month (1-12)
│ │ │ │ ┌───────────── day of week (0-6, 0=Sunday)
│ │ │ │ │
* * * * *
Examples:
*/5 * * * *- Every 5 minutes0 9 * * *- Every day at 9:00 AM0 9 * * 1-5- Weekdays at 9:00 AM30 */2 * * *- Every 2 hours at minute 30
Decorator Syntax
from replyfast import schedule, get_scheduler
@schedule("0 9 * * *")
def daily_task():
print("Runs every day at 9 AM")
get_scheduler().start()
Examples
The examples/ directory contains complete working examples:
example.py
Basic usage showing how to list contacts, groups, and receive messages with proper signal handling:
python examples/example.py
Demonstrates:
- Checking registration status
- Getting account info with
whoami_sync() - Listing contacts and groups
- Receiving messages with callback
- Handling typing indicators and read receipts
- Clean Ctrl+C shutdown
demo_bot.py
A bot that sends system stats on a schedule while also responding to commands:
DEMO_RECIPIENT=<uuid> python examples/demo_bot.py
Demonstrates:
- Scheduler with message receiving (starts after
is_queue_empty) - Multiple scheduled jobs (
df -hevery 5 min,free -mevery hour) - Interactive commands (ping, df, free, help)
- Reconnection loop with exponential backoff
- Proper cleanup on shutdown
Requirements
- Python 3.10+
- Rust toolchain (for building from source)
License
AGPL-3.0-or-later
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file replyfast-0.3.0.tar.gz.
File metadata
- Download URL: replyfast-0.3.0.tar.gz
- Upload date:
- Size: 68.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dc2bef046756f2711ed013f7247a5bf133733d90f60578365bf4c1eb99852850
|
|
| MD5 |
3552760be26daff5f8963591142970f7
|
|
| BLAKE2b-256 |
e4503d9ed340eac9e9f5276fe277d9528adf3816f18e6c4e3046640054d76686
|
Provenance
The following attestation bundles were made for replyfast-0.3.0.tar.gz:
Publisher:
release.yml on kushaldas/replyfast
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
replyfast-0.3.0.tar.gz -
Subject digest:
dc2bef046756f2711ed013f7247a5bf133733d90f60578365bf4c1eb99852850 - Sigstore transparency entry: 855180450
- Sigstore integration time:
-
Permalink:
kushaldas/replyfast@01faaf7f36f25014cf70a169fdd99af735dc3d5a -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/kushaldas
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@01faaf7f36f25014cf70a169fdd99af735dc3d5a -
Trigger Event:
push
-
Statement type:
File details
Details for the file replyfast-0.3.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: replyfast-0.3.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 8.9 MB
- Tags: CPython 3.10+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ddc02172fe0d907acdb48d5f18ba32ca70d5d04f6ecaf3db3f274279f76cec5b
|
|
| MD5 |
63b4773dd282fd549e3e019ed60b2dee
|
|
| BLAKE2b-256 |
b3daedc8ebb7538bb8bbaf3804c2ea04de04c69be62893767db7a10bba6c2385
|
Provenance
The following attestation bundles were made for replyfast-0.3.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
release.yml on kushaldas/replyfast
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
replyfast-0.3.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
ddc02172fe0d907acdb48d5f18ba32ca70d5d04f6ecaf3db3f274279f76cec5b - Sigstore transparency entry: 855180473
- Sigstore integration time:
-
Permalink:
kushaldas/replyfast@01faaf7f36f25014cf70a169fdd99af735dc3d5a -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/kushaldas
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@01faaf7f36f25014cf70a169fdd99af735dc3d5a -
Trigger Event:
push
-
Statement type:
File details
Details for the file replyfast-0.3.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: replyfast-0.3.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 8.9 MB
- Tags: CPython 3.10+, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4d4e4d2d82ea670830d57cf7e7253ca4c1541f9ccbbfbb904e365fcc8e0898e3
|
|
| MD5 |
181981213369633eb31d01d9b76395fe
|
|
| BLAKE2b-256 |
a471e8dc8eb35b5e3bd76e06c017b0e82091324e01e9a82b17b6fb1c66dadd55
|
Provenance
The following attestation bundles were made for replyfast-0.3.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:
Publisher:
release.yml on kushaldas/replyfast
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
replyfast-0.3.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -
Subject digest:
4d4e4d2d82ea670830d57cf7e7253ca4c1541f9ccbbfbb904e365fcc8e0898e3 - Sigstore transparency entry: 855180466
- Sigstore integration time:
-
Permalink:
kushaldas/replyfast@01faaf7f36f25014cf70a169fdd99af735dc3d5a -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/kushaldas
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@01faaf7f36f25014cf70a169fdd99af735dc3d5a -
Trigger Event:
push
-
Statement type:
File details
Details for the file replyfast-0.3.0-cp310-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: replyfast-0.3.0-cp310-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 7.2 MB
- Tags: CPython 3.10+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cd647323463bae6581ed1678f72ce384ca03b71ac2a9cbb52f28bc9f7d32079c
|
|
| MD5 |
defa753b5b5a074e33fb5ecfcbb640b2
|
|
| BLAKE2b-256 |
4a146f43669e6c0429028f69930600596ff8d68f9df7ba43576a6627b23460da
|
Provenance
The following attestation bundles were made for replyfast-0.3.0-cp310-abi3-macosx_11_0_arm64.whl:
Publisher:
release.yml on kushaldas/replyfast
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
replyfast-0.3.0-cp310-abi3-macosx_11_0_arm64.whl -
Subject digest:
cd647323463bae6581ed1678f72ce384ca03b71ac2a9cbb52f28bc9f7d32079c - Sigstore transparency entry: 855180459
- Sigstore integration time:
-
Permalink:
kushaldas/replyfast@01faaf7f36f25014cf70a169fdd99af735dc3d5a -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/kushaldas
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@01faaf7f36f25014cf70a169fdd99af735dc3d5a -
Trigger Event:
push
-
Statement type:
File details
Details for the file replyfast-0.3.0-cp310-abi3-macosx_10_12_x86_64.whl.
File metadata
- Download URL: replyfast-0.3.0-cp310-abi3-macosx_10_12_x86_64.whl
- Upload date:
- Size: 7.4 MB
- Tags: CPython 3.10+, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d827c05e12864c319c60b43b7259f7b22ae9ac616c9c65c0c292359370951c47
|
|
| MD5 |
32d6411f50a7403e80fb17be09ca9059
|
|
| BLAKE2b-256 |
fe76de87ebfbb750a9eb2d73f782d4189a11beab8d4d6fc129b05a2ee9ab679f
|
Provenance
The following attestation bundles were made for replyfast-0.3.0-cp310-abi3-macosx_10_12_x86_64.whl:
Publisher:
release.yml on kushaldas/replyfast
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
replyfast-0.3.0-cp310-abi3-macosx_10_12_x86_64.whl -
Subject digest:
d827c05e12864c319c60b43b7259f7b22ae9ac616c9c65c0c292359370951c47 - Sigstore transparency entry: 855180452
- Sigstore integration time:
-
Permalink:
kushaldas/replyfast@01faaf7f36f25014cf70a169fdd99af735dc3d5a -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/kushaldas
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@01faaf7f36f25014cf70a169fdd99af735dc3d5a -
Trigger Event:
push
-
Statement type: