Core Components

LXMFBot

The main bot class that handles message routing, command processing, and bot lifecycle management.

from lxmfy import LXMFBot

bot = LXMFBot(
    name="MyBot",
    announce=600,
    announce_immediately=True,
    admins=set(),
    hot_reloading=False,
    rate_limit=5,
    cooldown=60,
    max_warnings=3,
    warning_timeout=300,
    command_prefix="/",
    cogs_dir="cogs",
    cogs_enabled=True,
    permissions_enabled=False,
    storage_type="json",
    storage_path="data",
    first_message_enabled=True,
    event_logging_enabled=True,
    max_logged_events=1000,
    event_middleware_enabled=True,
    announce_enabled=True,
    signature_verification_enabled=False,
    require_message_signatures=False
)

Key Methods

  • run(delay=10): Start the bot’s main loop

  • send(destination, message, title="Reply", lxmf_fields=None, propagation_node=None, max_retries=3): Send a message to a destination, optionally with custom LXMF fields, specific propagation node, and retry configuration.

  • send_with_attachment(destination, message, attachment, title="Reply"): Send a message with an attachment

  • command(name, description="No description provided", admin_only=False, threaded=False): Decorator for registering commands. Set threaded=True to run the command’s callback in a separate thread.

  • on_first_message(): Decorator for handling first messages from users

  • on_message(): Decorator for handling all messages (called before command processing)

  • validate(): Run validation checks on the bot configuration

Storage

The framework provides two storage backends:

JSONStorage

from lxmfy import JSONStorage

storage = JSONStorage("data")

SQLiteStorage

from lxmfy import SQLiteStorage

storage = SQLiteStorage("data/bot.db")

Commands

Command registration and handling:

@bot.command(name="hello", description="Says hello")
def hello(ctx):
    ctx.reply(f"Hello {ctx.sender}!")

Threaded Commands

For long-running or blocking operations that do not interact with the Reticulum Network Stack directly, you can run commands in a separate thread to keep the bot responsive.

import time

@bot.command(name="long_task", description="Performs a long-running task in a separate thread", threaded=True)
def long_task_command(ctx):
    ctx.reply("Starting a long task... please wait.")
    time.sleep(10) # This runs in a separate thread
    ctx.reply("Long task completed!")

Important: Functions marked as threaded=True must not directly interact with the Reticulum Network Stack (RNS) or any components that rely on lxmfy.transport.py, as these are generally not thread-safe. Use ctx.reply() for sending messages back to the user from within a threaded command.

Events

Event system for handling various bot events:

@bot.events.on("message_received", EventPriority.HIGHEST)
def handle_message(event):
    # Handle message event
    pass

Permissions

Permission system for controlling access to bot features:

from lxmfy import DefaultPerms

@bot.command(name="admin", description="Admin command", admin_only=True)
def admin_command(ctx):
    if ctx.is_admin:
        ctx.reply("Admin command executed")

Middleware

Middleware system for processing messages and events:

@bot.middleware.register(MiddlewareType.PRE_COMMAND)
def pre_command_middleware(ctx):
    # Process before command execution
    pass

Attachments

Support for sending files, images, and audio:

from lxmfy import Attachment, AttachmentType

attachment = Attachment(
    type=AttachmentType.IMAGE,
    name="image.jpg",
    data=image_data,
    format="jpg"
)
bot.send_with_attachment(destination, "Here's an image", attachment)

Icon Appearance (LXMF Field)

You can set a custom icon for your bot that compliant LXMF clients can display. This uses the LXMF.FIELD_ICON_APPEARANCE.

from lxmfy import IconAppearance, pack_icon_appearance_field
import LXMF # Required for LXMF.FIELD_ICON_APPEARANCE

# Define the icon appearance
icon_data = IconAppearance(
    icon_name="smart_toy",  # Name from Material Symbols
    fg_color=b'\xFF\xFF\xFF',  # White foreground (3 bytes)
    bg_color=b'\x4A\x90\xE2'   # Blue background (3 bytes)
)

# Pack it into the LXMF field format
icon_lxmf_field = pack_icon_appearance_field(icon_data)

# Send a message with this icon
bot.send(
    destination_hash_str,
    "Hello from your friendly bot!",
    title="Bot Message",
    lxmf_fields=icon_lxmf_field
)

# You can also combine it with other fields, like attachments:
# attachment_field = pack_attachment(some_attachment)
# combined_fields = {**icon_lxmf_field, **attachment_field}
# bot.send(destination, "Message with icon and attachment", lxmf_fields=combined_fields)

Scheduler

Task scheduling system:

@bot.scheduler.schedule(name="daily_task", cron_expr="0 0 * * *")
def daily_task():
    # Run daily at midnight
    pass

Signatures

LXMFy provides configuration options for LXMF’s built-in cryptographic message signing and verification:

from lxmfy import LXMFBot

bot = LXMFBot(
    name="SecureBot",
    signature_verification_enabled=True,  # Enable signature checks
    require_message_signatures=False      # Set to True to reject unsigned messages
)

Important: LXMF automatically handles all cryptographic signing and verification using RNS identities. LXMFy’s SignatureManager is a configuration layer that:

  • Controls whether to enforce signature verification

  • Determines policy for unsigned messages (accept or reject)

  • Integrates with the permission system (e.g., bypass verification for trusted users)

The actual cryptographic operations are performed by LXMF/RNS, not by LXMFy.

SignatureManager Methods

The SignatureManager is available as bot.signature_manager when signature_verification_enabled=True:

  • should_verify_message(sender): Determine if a message from the given sender should be verified

  • handle_unsigned_message(sender, message_hash): Handle messages that lack valid signatures based on policy

How LXMF Signatures Work

LXMF automatically signs all outgoing messages using the sender’s RNS identity during the pack() operation. When messages are received, LXMF validates signatures and provides:

  • message.signature_validated: Boolean indicating if the signature is valid

  • message.unverified_reason: Reason code if validation failed (e.g., SIGNATURE_INVALID, SOURCE_UNKNOWN)

LXMFy uses these built-in LXMF properties to enforce your bot’s signature policy.

Message Delivery

LXMFy provides advanced message delivery features including propagation nodes and automatic retries:

Propagation Nodes

Send messages through specific propagation nodes for improved reliability on the Reticulum network:

# Send via a specific propagation node
bot.send(
    destination_hash,
    "Message content",
    propagation_node="<propagation_node_hash>"
)

# The propagation node hash should be a valid LXMF propagation node
# on the Reticulum network

Automatic Retries

Configure automatic retry attempts for failed message deliveries:

# Send with custom retry count
bot.send(
    destination_hash,
    "Important message",
    max_retries=5  # Will retry up to 5 times on delivery failure
)

# Default max_retries is 3
# Retry logic automatically handles delivery callbacks

The retry system tracks delivery attempts per destination and automatically retries failed deliveries. Successful deliveries reset the retry counter for that destination.

Message Handlers

LXMFy provides decorators for handling different types of incoming messages:

First Message Handler

Handle the first message from each user:

@bot.on_first_message()
def welcome_user(sender, message):
    content = message.content.decode("utf-8")
    bot.send(sender, f"Welcome! You said: {content}")
    return True  # Return True to stop further processing

General Message Handler

Handle all incoming messages before command processing:

@bot.on_message()
def handle_all_messages(sender, message):
    content = message.content.decode("utf-8").strip()

    # Custom logic here
    if content.startswith("echo:"):
        bot.send(sender, content[5:])
        return True  # Stop further processing

    return False  # Continue to command processing

Message handlers are called in this order: 1. First message handler (if this is the first message from this sender) 2. General message handlers (registered with on_message()) 3. Command processing (if message starts with command prefix)

Templates

The framework includes several ready-to-use bot templates:

EchoBot

Simple echo bot that repeats messages:

from lxmfy.templates import EchoBot

bot = EchoBot()
bot.run()

MemeBot

Bot for sending random memes:

from lxmfy.templates import MemeBot

bot = MemeBot()
bot.run()

NoteBot

Note-taking bot with JSON storage:

from lxmfy.templates import NoteBot

bot = NoteBot()
bot.run()

ReminderBot

Reminder bot with SQLite storage:

from lxmfy.templates import ReminderBot

bot = ReminderBot()
bot.run()

CLI Tools

The framework provides command-line tools for bot management:

# Create a new bot
lxmfy create mybot

# Create a bot from template
lxmfy create --template echo mybot

# Run a template bot
lxmfy run echo

# Analyze bot configuration
lxmfy analyze bot.py

# Verify package signature
lxmfy verify package.whl sigstore.json

# Test signature verification with a message
lxmfy signatures test

# Enable signature verification
lxmfy signatures enable

# Disable signature verification
lxmfy signatures disable

Error Handling

The framework provides comprehensive error handling:

try:
    bot.run()
except KeyboardInterrupt:
    bot.cleanup()
except Exception as e:
    logger.error(f"Error running bot: {str(e)}")