LXMF bot framework for creating bots for the Reticulum Network
Project description
LXMFy
Easily create LXMF bots for the Reticulum Network with this extensible framework.
Features
- Interactive CLI
- Spam protection (rate limiting, command cooldown, warnings, banning)
- Command prefix (set to None to process all messages as commands)
- Announcements (announce in seconds, set to 0 to disable)
- Extensible Storage Backend (JSON, SQLite)
- Permission System (Role-based)
- Middleware System
- Task Scheduler (Cron-style)
- Event System
- Help on first message
- LXMF Attachments (File, Image, Audio)
- Customizable Bot Icon (via LXMF Icon Appearance field)
- Direct Delivery & Propagation Fallback
- Threading support for commands.
- Cryptographic Message Signing & Verification (LXMF)
Installation
pip install lxmfy
or pipx:
pipx install lxmfy
or uv:
uv sync
or via git
pipx install git+https://github.com/lxmfy/LXMFy.git
or temporary environment with uv:
uvx --from git+https://github.com/lxmfy/LXMFy.git lxmfy
Usage
lxmfy
or with uv:
uv run lxmfy
Create bots:
lxmfy create
Docker
Building Manually
To build the Docker image, navigate to the root of the project and run:
docker build -t lxmfy-test .
Once built, you can run the Docker image:
docker run -d \
--name lxmfy-test-bot \
-v $(pwd)/config:/bot/config \
-v $(pwd)/.reticulum:/root/.reticulum \
--restart unless-stopped \
lxmfy-test
Auto-Interface support (network host):
docker run -d \
--name lxmfy-test-bot \
--network host \
-v $(pwd)/config:/bot/config \
-v $(pwd)/.reticulum:/root/.reticulum \
--restart unless-stopped \
lxmfy-test
Building Wheels with docker/Dockerfile.Build
The docker/Dockerfile.Build is used to build the lxmfy Python package into a wheel file within a Docker image.
docker build -f docker/Dockerfile.Build -t lxmfy-wheel-builder .
This will create an image named lxmfy-wheel-builder. To extract the built wheel file from the image, you can run a container from this image and copy the dist directory:
docker run --rm -v "$(pwd)/dist_output:/output" lxmfy-wheel-builder
This command will create a dist_output directory in your current working directory and copy the built wheel file into it.
Example
from lxmfy import LXMFBot, load_cogs_from_directory
bot = LXMFBot(
name="LXMFy Test Bot", # Name of the bot that appears on the network.
announce=5400, # Announce every hour, set to 0 to disable.
announce_enabled=True, # Set to False to disable all announces (both initial and periodic)
announce_immediately=True, # Set to False to disable initial announce
admins=["your_lxmf_hash_here"], # List of admin hashes.
hot_reloading=True, # Enable hot reloading.
command_prefix="/", # Set to None to process all messages as commands.
cogs_dir="cogs", # Specify cogs directory name.
rate_limit=5, # 5 messages per minute
cooldown=5, # 5 seconds cooldown
max_warnings=3, # 3 warnings before ban
warning_timeout=300, # Warnings reset after 5 minutes
signature_verification_enabled=True, # Enable cryptographic signature verification
require_message_signatures=False, # Allow unsigned messages but log them
propagation_fallback_enabled=True, # Enable propagation fallback after direct delivery fails
propagation_node="your_propagation_node_hash_here", # Manual propagation node (optional)
autopeer_propagation=True, # Auto-discover propagation nodes (optional)
autopeer_maxdepth=4, # Max hops for auto-peering (default: 4)
enable_propagation_node=False, # Run as propagation node (default: False)
message_storage_limit_mb=500, # Storage limit in MB for propagation node (default: 500)
direct_delivery_retries=3, # Number of direct delivery attempts before falling back to propagation
)
# Dynamically load all cogs
load_cogs_from_directory(bot)
@bot.command(name="ping", description="Test if bot is responsive")
def ping(ctx):
ctx.reply("Pong!")
# Admin Only Command
@bot.command(name="echo", description="Echo a message", admin_only=True)
def echo(ctx, message: str):
ctx.reply(message)
bot.run()
Cryptographic Message Signing
LXMFy supports cryptographic signing and verification of messages for enhanced security:
bot = LXMFBot(
name="SecureBot",
signature_verification_enabled=True, # Enable signature verification
require_message_signatures=False, # Allow unsigned messages but log them
# ... other config
)
CLI Commands for Signatures
# Test signature functionality
lxmfy signatures test
# Get enable instructions
lxmfy signatures enable
# Get disable instructions
lxmfy signatures disable
Propagation Node Configuration
LXMFy supports three modes for propagation node usage:
1. Manual Configuration
Set a specific propagation node by hash:
bot = LXMFBot(
name="MyBot",
propagation_fallback_enabled=True,
propagation_node="your_propagation_node_hash_here", # Manual node configuration
direct_delivery_retries=3,
)
2. Automatic Discovery (Auto-Peering)
Let the bot automatically discover and use propagation nodes from network announces:
bot = LXMFBot(
name="MyBot",
propagation_fallback_enabled=True,
autopeer_propagation=True, # Enable automatic discovery
autopeer_maxdepth=4, # Maximum hop distance for auto-peering (default: 4)
)
The bot will listen for propagation node announces and automatically peer with suitable nodes within the configured hop depth.
3. Run as Propagation Node
Your bot can act as a propagation node itself to store and forward messages:
bot = LXMFBot(
name="MyPropagationBot",
enable_propagation_node=True, # Enable propagation node mode
message_storage_limit_mb=500, # Limit storage to 500 MB (default)
)
When running as a propagation node, the bot will store messages for offline users and forward them when the recipients come online. The message_storage_limit_mb prevents the bot from consuming unlimited disk space. Set to 0 for unlimited storage (not recommended).
Querying Propagation Status
You can check the current propagation configuration and discovered nodes:
status = bot.get_propagation_node_status()
print(f"Current outbound node: {status['current_outbound_node']}")
print(f"Discovered peers: {status['discovered_peers']}")
Dynamically Setting Propagation Node
You can change the propagation node at runtime:
bot.set_propagation_node("new_propagation_node_hash")
Managing Storage Limits
When running as a propagation node, you can query and adjust storage limits:
# Get current storage statistics
stats = bot.get_propagation_storage_stats()
print(f"Storage used: {stats['storage_size_mb']:.2f} MB")
print(f"Storage limit: {stats['storage_limit_mb']} MB")
print(f"Utilization: {stats['utilization_percent']:.1f}%")
print(f"Messages stored: {stats['message_count']}")
# Change storage limit at runtime
bot.set_message_storage_limit(megabytes=1000) # Set to 1 GB
Important Notes
- Without configuring propagation (manual, auto-peer, or running as a node), messages requiring propagation will fail
- You can combine modes: e.g., set a manual node AND enable auto-peering as backup
- When running as a propagation node, your bot can still send and receive messages normally
- Auto-peering respects the
autopeer_maxdepthsetting to avoid connecting to distant nodes
Development
- poetry or uv
- python 3.11 or higher
With poetry:
poetry install
poetry run lxmfy run echo
With uv:
uv sync
uv run lxmfy run echo
Contributing
Pull requests are welcome.
Part of Quad4
LXMFy is a Quad4 project.
License
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 Distribution
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 lxmfy-1.2.1.tar.gz.
File metadata
- Download URL: lxmfy-1.2.1.tar.gz
- Upload date:
- Size: 43.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bda6e89c0796b5c432a1ad183ea4cfc17b410fbe87bb89825426a2a454843ce4
|
|
| MD5 |
b88e8205d249f5570233c1c1d40a12b1
|
|
| BLAKE2b-256 |
72a9882e1653ba38db26ba0ff7379cd2d727c1fe7423f53baae5d2bcca74f282
|
Provenance
The following attestation bundles were made for lxmfy-1.2.1.tar.gz:
Publisher:
publish.yml on lxmfy/LXMFy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lxmfy-1.2.1.tar.gz -
Subject digest:
bda6e89c0796b5c432a1ad183ea4cfc17b410fbe87bb89825426a2a454843ce4 - Sigstore transparency entry: 732058410
- Sigstore integration time:
-
Permalink:
lxmfy/LXMFy@b2da9341fd67546dcc9476711deafa12a609ff9d -
Branch / Tag:
refs/tags/v1.2.1 - Owner: https://github.com/lxmfy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
self-hosted -
Publication workflow:
publish.yml@b2da9341fd67546dcc9476711deafa12a609ff9d -
Trigger Event:
push
-
Statement type:
File details
Details for the file lxmfy-1.2.1-py3-none-any.whl.
File metadata
- Download URL: lxmfy-1.2.1-py3-none-any.whl
- Upload date:
- Size: 51.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b182195d1f62bb21c8e56f4a6aec95b43607d0419d83cc851a6917249b1e7d0e
|
|
| MD5 |
0fa631a9e46f0bb6fa696811304e0823
|
|
| BLAKE2b-256 |
06c20016808a093caec5314700efc2a4c819be4115123fc93d2732e643105193
|
Provenance
The following attestation bundles were made for lxmfy-1.2.1-py3-none-any.whl:
Publisher:
publish.yml on lxmfy/LXMFy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lxmfy-1.2.1-py3-none-any.whl -
Subject digest:
b182195d1f62bb21c8e56f4a6aec95b43607d0419d83cc851a6917249b1e7d0e - Sigstore transparency entry: 732058411
- Sigstore integration time:
-
Permalink:
lxmfy/LXMFy@b2da9341fd67546dcc9476711deafa12a609ff9d -
Branch / Tag:
refs/tags/v1.2.1 - Owner: https://github.com/lxmfy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
self-hosted -
Publication workflow:
publish.yml@b2da9341fd67546dcc9476711deafa12a609ff9d -
Trigger Event:
push
-
Statement type: