mirror of
https://git.quad4.io/RNS-Things/MeshChatX.git
synced 2026-08-14 16:49:58 +00:00
645 lines
23 KiB
ReStructuredText
645 lines
23 KiB
ReStructuredText
Creating Bots
|
|
=============
|
|
|
|
Basic Structure
|
|
---------------
|
|
|
|
A minimal LXMFy bot involves:
|
|
|
|
1. Importing :code:`LXMFBot`.
|
|
2. Instantiating :code:`LXMFBot` with desired configuration.
|
|
3. Defining commands or event handlers.
|
|
4. Running the bot using :code:`bot.run()`.
|
|
|
|
.. code-block:: python
|
|
|
|
from lxmfy import LXMFBot
|
|
|
|
# 1. Instantiate the bot
|
|
bot = LXMFBot(
|
|
name="SimpleBot",
|
|
command_prefix="!",
|
|
storage_path="simple_data"
|
|
)
|
|
|
|
# 2. Define commands
|
|
@bot.command(name="ping", description="Responds with pong")
|
|
def ping_command(ctx):
|
|
# ctx is a context object containing message info
|
|
# ctx.sender: Sender's LXMF hash
|
|
# ctx.content: Full message content
|
|
# ctx.args: List of arguments after the command
|
|
# ctx.reply(message): Function to send a reply
|
|
# (can also take keyword arguments like title="My Title", lxmf_fields=some_fields)
|
|
ctx.reply("Pong!")
|
|
|
|
# For long-running tasks, you can use threaded commands:
|
|
# import time
|
|
# @bot.command(name="long_op", description="Performs a long operation in a separate thread", threaded=True)
|
|
# def long_op_command(ctx):
|
|
# ctx.reply("Starting long operation...")
|
|
# time.sleep(10) # Simulate a long-running operation
|
|
# ctx.reply("Long operation complete!")
|
|
# Important: Threaded commands should not directly interact with RNS or lxmfy.transport.py.
|
|
|
|
@bot.command(name="greet", description="Greets the user")
|
|
def greet_command(ctx):
|
|
if ctx.args:
|
|
name = " ".join(ctx.args)
|
|
ctx.reply(f"Hello, {name}!")
|
|
else:
|
|
ctx.reply("Hello there! Tell me your name: !greet <your_name>")
|
|
|
|
# 3. Run the bot
|
|
if __name__ == "__main__":
|
|
print(f"Starting bot: {bot.config.name}")
|
|
print(f"Bot LXMF Address: {bot.local.hash}")
|
|
bot.run()
|
|
|
|
Using Templates
|
|
---------------
|
|
|
|
LXMFy provides several templates for common bot types. You can use the CLI to generate a bot file based on a template.
|
|
|
|
.. code-block:: bash
|
|
|
|
# Create an echo bot
|
|
lxmfy create --template echo my_echo_bot
|
|
|
|
# Create a reminder bot (uses SQLite storage)
|
|
lxmfy create --template reminder my_reminder_bot
|
|
|
|
# Create a note-taking bot (uses JSON storage)
|
|
lxmfy create --template note my_note_bot
|
|
|
|
# Create a cog test bot (tests cog loading features)
|
|
lxmfy create --template cogtest my_cog_test_bot
|
|
|
|
Running these commands creates a Python file (e.g., :code:`my_echo_bot.py`) that imports and runs the chosen template. You can then modify the generated file or the template code itself (:code:`lxmfy/templates/...`).
|
|
|
|
**Example generated file (:code:`my_cog_test_bot.py`):**
|
|
|
|
.. code-block:: python
|
|
|
|
from lxmfy.templates import CogTestBot
|
|
|
|
if __name__ == "__main__":
|
|
bot = CogTestBot() # Creates an instance of the CogTestBot template
|
|
# You can optionally override the default name:
|
|
# bot.bot.name = "My Cog Test Bot"
|
|
bot.run()
|
|
|
|
Bot Configuration
|
|
-----------------
|
|
|
|
When creating an :code:`LXMFBot` instance, you can pass various keyword arguments to configure its behavior. See the :code:`BotConfig` section in the `API Reference <api-reference.html>`_ or the `Quick Start Guide <quick-start.html>`_ for a list of common options.
|
|
|
|
.. code-block:: python
|
|
|
|
from lxmfy import LXMFBot
|
|
|
|
bot = LXMFBot(
|
|
name="ConfiguredBot",
|
|
announce=3600, # Announce every hour
|
|
admins={"your_admin_hash_here"}, # Set admin user(s)
|
|
command_prefix="$", # Use '$' as prefix
|
|
storage_type="sqlite", # Use SQLite database
|
|
storage_path="data/my_bot_data.db", # Specify DB file path
|
|
rate_limit=10, # Allow 10 messages / minute
|
|
cooldown=30, # Cooldown of 30 seconds
|
|
permissions_enabled=True # Enable role-based permissions
|
|
)
|
|
|
|
if __name__ == "__main__":
|
|
# You can also modify config after instantiation
|
|
# Note: some settings are best set during init
|
|
bot.config.max_warnings = 5
|
|
bot.spam_protection.config.max_warnings = 5 # Update spam protector too
|
|
|
|
bot.run()
|
|
|
|
Setting a Bot Icon (LXMF Field)
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
You can give your bot a custom icon that appears in compatible LXMF clients. This uses the :code:`LXMF.FIELD_ICON_APPEARANCE` and can be set when sending messages.
|
|
|
|
First, ensure you have the necessary imports:
|
|
|
|
.. code-block:: python
|
|
|
|
from lxmfy import IconAppearance, pack_icon_appearance_field
|
|
|
|
Then, you can define and use the icon:
|
|
|
|
.. code-block:: python
|
|
|
|
# In your bot class or setup
|
|
icon_data = IconAppearance(
|
|
icon_name="robot_2", # Choose from Material Symbols
|
|
fg_color=b'\x00\xFF\x00', # Green
|
|
bg_color=b'\x33\x33\x33' # Dark Grey
|
|
)
|
|
self.bot_icon_field = pack_icon_appearance_field(icon_data)
|
|
|
|
# When sending a message or replying:
|
|
ctx.reply("Message from your bot!", lxmf_fields=self.bot_icon_field)
|
|
# or
|
|
# bot.send(destination, "Another message", lxmf_fields=self.bot_icon_field)
|
|
|
|
This :code:`self.bot_icon_field` can be pre-calculated and reused for all messages sent by the bot.
|
|
|
|
Structured Commands via LXMF Fields
|
|
-----------------------------------
|
|
|
|
In addition to text-based commands, LXMFy supports commands sent through LXMF message fields using ``FIELD_COMMANDS`` (``0x09``). This is useful for structured request/response workflows between LXMF clients and bots.
|
|
|
|
When a message contains ``FIELD_COMMANDS``, the bot extracts the command name and arguments, routes them through the same command registry as text commands, and automatically includes ``FIELD_RESULTS`` (``0x0A``) in the reply.
|
|
|
|
**Receiving structured commands**
|
|
|
|
.. code-block:: python
|
|
|
|
from lxmfy import LXMFBot
|
|
|
|
bot = LXMFBot(name="FieldBot")
|
|
|
|
@bot.command(name="add", description="Add two numbers")
|
|
def add_command(ctx):
|
|
if len(ctx.args) >= 2:
|
|
try:
|
|
result = float(ctx.args[0]) + float(ctx.args[1])
|
|
ctx.reply(str(result))
|
|
except ValueError:
|
|
ctx.reply("Invalid numbers")
|
|
else:
|
|
ctx.reply("Usage: add <a> <b>")
|
|
|
|
The ``ctx`` object in field command callbacks includes:
|
|
|
|
- :code:`ctx.fields` — the raw LXMF fields dict from the incoming message
|
|
- :code:`ctx.request_id` — the ``request_id`` from the incoming ``FIELD_COMMANDS`` (if any)
|
|
|
|
**Sending a structured command from an LXMF client**
|
|
|
|
.. code-block:: python
|
|
|
|
import LXMF
|
|
from lxmfy import FIELD_COMMANDS
|
|
|
|
lxm = LXMF.LXMessage(
|
|
destination,
|
|
source,
|
|
b"", # content can be empty for field-only commands
|
|
desired_method=LXMF.LXMessage.DIRECT,
|
|
)
|
|
lxm.fields[FIELD_COMMANDS] = {
|
|
"command": "add",
|
|
"args": ["3", "5"],
|
|
"request_id": "req-42", # optional, for correlation
|
|
}
|
|
router.handle_outbound(lxm)
|
|
|
|
**Disabling field commands**
|
|
|
|
If you want the bot to ignore ``FIELD_COMMANDS`` and only process text commands, set:
|
|
|
|
.. code-block:: python
|
|
|
|
bot = LXMFBot(
|
|
name="TextOnlyBot",
|
|
lxmf_commands_enabled=False,
|
|
)
|
|
|
|
Using Cogs (Extensions)
|
|
-----------------------
|
|
|
|
Cogs allow you to organize your commands and event listeners into separate files (modules), keeping your main bot file cleaner.
|
|
|
|
1. **Create a :code:`cogs` directory** (or whatever you set :code:`cogs_dir` to in :code:`BotConfig`).
|
|
2. **Create Python files** inside the :code:`cogs` directory (e.g., :code:`utility.py`).
|
|
3. **Define a class** that inherits from :code:`lxmfy.Cog` (optional but good practice) or is just a standard class.
|
|
4. **Define commands** as methods within the class using the :code:`@Command` decorator.
|
|
5. **Create a :code:`setup(bot)` function** in the cog file, which LXMFy will call to register the cog.
|
|
|
|
**Example (:code:`cogs/utility.py`):**
|
|
|
|
.. code-block:: python
|
|
|
|
from lxmfy import Command
|
|
from lxmfy.commands import Cog # Import Cog if inheriting
|
|
import time
|
|
|
|
class UtilityCog: # Or class UtilityCog(Cog):
|
|
def __init__(self, bot):
|
|
self.bot = bot
|
|
self.start_time = time.time()
|
|
|
|
@Command(name="uptime", description="Shows bot uptime")
|
|
# Note: Methods in cogs often take 'self' and 'ctx'
|
|
def uptime_command(self, ctx):
|
|
uptime_seconds = time.time() - self.start_time
|
|
ctx.reply(f"Bot has been running for {uptime_seconds:.2f} seconds.")
|
|
|
|
@Command(name="info", description="Shows bot info")
|
|
def info_command(self, ctx):
|
|
info = (
|
|
f"Bot Name: {self.bot.config.name}\n"
|
|
f"Owner(s): {', '.join(self.bot.config.admins) or 'None'}\n"
|
|
f"Prefix: {self.bot.config.command_prefix}"
|
|
)
|
|
ctx.reply(info)
|
|
|
|
@Command(name="threaded_cog_task", description="Performs a long task in a cog thread", threaded=True)
|
|
def threaded_cog_task(self, ctx):
|
|
ctx.reply("Starting a long cog task... this will run in a separate thread.")
|
|
time.sleep(7) # Simulate a long-running operation
|
|
ctx.reply("Long cog task completed!")
|
|
|
|
# This function is required for the cog to be loaded
|
|
def setup(bot):
|
|
cog_instance = UtilityCog(bot)
|
|
bot.add_cog(cog_instance) # Register the cog instance with the bot
|
|
|
|
**Main Bot File (:code:`my_bot.py`):**
|
|
|
|
.. code-block:: python
|
|
|
|
from lxmfy import LXMFBot
|
|
|
|
bot = LXMFBot(
|
|
name="CogBot",
|
|
cogs_enabled=True, # Make sure cogs are enabled (default)
|
|
cogs_dir="cogs" # Point to the directory
|
|
)
|
|
|
|
if __name__ == "__main__":
|
|
# Cogs are loaded automatically during LXMFBot initialization
|
|
# if cogs_enabled is True.
|
|
bot.run()
|
|
|
|
When the bot starts, it will automatically find :code:`utility.py`, call its :code:`setup` function, which creates an instance of :code:`UtilityCog` and registers it using :code:`bot.add_cog()`. The commands defined in the cog (:code:`uptime`, :code:`info`) will then be available.
|
|
|
|
External Script Cogs (Multi-Language Support)
|
|
---------------------------------------------
|
|
|
|
You can also write bot extensions in languages other than Python (e.g., Bash, Ruby, Perl, Go, C) using External Script Cogs.
|
|
|
|
1. **Create an executable script** in your :code:`cogs` directory.
|
|
2. **Add a shebang** at the top of the script (e.g., :code:`#!/bin/bash`).
|
|
3. **Ensure the script is executable** (:code:`chmod +x your_script`).
|
|
|
|
When the bot starts, it will automatically register any executable file in the :code:`cogs` directory (that doesn't end in :code:`.py`) as a bot command.
|
|
|
|
**Argument Protocol:**
|
|
|
|
- :code:`$1`: Sender's LXMF hash.
|
|
- :code:`$2`: Full message content.
|
|
- :code:`$3`, :code:`$4`, ...: Individual command arguments.
|
|
|
|
**Environment Variables:**
|
|
|
|
- :code:`LXMFY_SENDER`: The sender's identity hash.
|
|
- :code:`LXMFY_CONTENT`: The full message content.
|
|
- :code:`LXMFY_HAS_ADMIN`: :code:`true` or :code:`false` depending on the sender's admin status.
|
|
|
|
**Example Bash Cog (:code:`cogs/greet.sh`):**
|
|
|
|
.. code-block:: bash
|
|
|
|
#!/bin/bash
|
|
echo "Hello from Bash! You sent: $2"
|
|
|
|
When a user sends :code:`/greet hello`, the bot will execute this script and reply with its stdout: :code:`Hello from Bash! You sent: /greet hello`.
|
|
|
|
Sovereign NLP (Local Intent Classification)
|
|
-------------------------------------------
|
|
|
|
LXMFy includes a built-in, lightweight NLP engine for intent classification. This allows your bot to understand the "intent" of a message even if it doesn't match a command exactly.
|
|
|
|
1. **Enable NLP** in your bot configuration: :code:`nlp_enabled=True`.
|
|
2. **Define intents** using the :code:`@bot.intent` decorator.
|
|
|
|
.. code-block:: python
|
|
|
|
@bot.intent("help", examples=["how do I use this?", "show me commands", "help me please"])
|
|
def help_intent(msg):
|
|
msg.reply("I can help! Try typing /help to see a list of commands.")
|
|
|
|
The NLP engine uses mathematical vector similarity (TF-IDF and Cosine Similarity) to match incoming text against your example phrases. This processing happens entirely locally on your machine, ensuring full privacy.
|
|
|
|
**Persistence and Extensibility:**
|
|
|
|
For larger bots, you can export and import the trained intent model to avoid retraining on every startup:
|
|
|
|
.. code-block:: python
|
|
|
|
# Export the model
|
|
model_data = bot.nlp.export_model()
|
|
# Save model_data to a file or database
|
|
|
|
# Later, import it back
|
|
bot.nlp.import_model(model_data)
|
|
|
|
RNS Link Support
|
|
----------------
|
|
|
|
Bots can now establish and respond to direct RNS Links. This is useful for stateful, streaming, or high-bandwidth communication that goes beyond simple message packets.
|
|
|
|
1. **Enable Link Support** in configuration: :code:`link_support_enabled=True`.
|
|
2. **Request a link**: :code:`bot.request_link(destination_hash)`. You can also specify a custom app name and aspects: :code:`bot.request_link(dest, callback, "my_app", "aspect1")`.
|
|
3. **Handle incoming links**: Register a callback with :code:`bot.on_link(handler)`.
|
|
|
|
.. code-block:: python
|
|
|
|
def handle_link(link):
|
|
print(f"Link established with {RNS.hexrep(link.destination.hash)}")
|
|
# You can now use the link for direct RNS communication
|
|
|
|
bot.on_link(handle_link)
|
|
|
|
**Safety & Sandboxing:**
|
|
|
|
- **Timeouts:** External cogs have a default timeout (30s) to prevent hanging. This is configurable via :code:`external_cogs_timeout`.
|
|
- **Threading:** All external cogs run in separate threads and do not block the bot.
|
|
- **Sandboxing (Linux only):** If :code:`bubblewrap` (:code:`bwrap`) or :code:`firejail` is installed, the bot can automatically run scripts in a restricted, read-only sandbox. This is enabled by default via :code:`external_cogs_sandbox_enabled`.
|
|
|
|
Handling Messages
|
|
-----------------
|
|
|
|
LXMFy provides several ways to handle incoming messages at different stages of processing.
|
|
|
|
First Message Handler
|
|
^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Handle the first message from each new user (useful for welcome messages):
|
|
|
|
.. code-block:: python
|
|
|
|
from lxmfy import LXMFBot
|
|
|
|
bot = LXMFBot(
|
|
name="WelcomeBot",
|
|
first_message_enabled=True # Must be True (default)
|
|
)
|
|
|
|
@bot.on_first_message()
|
|
def welcome_new_user(sender, message):
|
|
content = message.content.decode("utf-8")
|
|
bot.send(
|
|
sender,
|
|
f"Welcome to the bot! You said: {content}\n\n"
|
|
"Type /help to see available commands."
|
|
)
|
|
return True # Return True to stop further processing of this message
|
|
|
|
if __name__ == "__main__":
|
|
bot.run()
|
|
|
|
General Message Handler
|
|
^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Handle all incoming messages before command processing:
|
|
|
|
.. code-block:: python
|
|
|
|
from lxmfy import LXMFBot
|
|
|
|
bot = LXMFBot(name="EchoBot")
|
|
|
|
@bot.on_message()
|
|
def echo_non_commands(sender, message):
|
|
content = message.content.decode("utf-8").strip()
|
|
|
|
# Check if this is a command - if so, let command handler deal with it
|
|
if content.startswith(bot.config.command_prefix):
|
|
command_name = content.split()[0][len(bot.config.command_prefix):]
|
|
if command_name in bot.commands:
|
|
return False # Let command handler process it
|
|
|
|
# Not a command, echo it back
|
|
bot.send(sender, f"You said: {content}")
|
|
return False # Return False to continue processing (though no commands will match)
|
|
|
|
@bot.command(name="hello", description="Say hello")
|
|
def hello_command(ctx):
|
|
ctx.reply("Hello! This is a command response.")
|
|
|
|
if __name__ == "__main__":
|
|
bot.run()
|
|
|
|
Message Handler Processing Order:
|
|
|
|
1. **First Message Handler** (if :code:`first_message_enabled=True` and this is first message from sender)
|
|
2. **General Message Handlers** (registered with :code:`@bot.on_message()`)
|
|
3. **Command Processing** (if message matches a registered command)
|
|
|
|
Handlers can return :code:`True` to stop further processing or :code:`False` to continue to the next stage.
|
|
|
|
Handling Events
|
|
---------------
|
|
|
|
You can register handlers for various bot events using the :code:`@bot.events.on()` decorator.
|
|
|
|
.. code-block:: python
|
|
|
|
from lxmfy import LXMFBot
|
|
from lxmfy.events import EventPriority # Optional for priority
|
|
|
|
bot = LXMFBot(name="EventBot")
|
|
|
|
@bot.events.on("message_received")
|
|
def log_message(event):
|
|
# Event object contains details
|
|
sender = event.data.get("sender")
|
|
message_content = event.data.get("message").content.decode('utf-8', errors='ignore')
|
|
print(f"Received message from {sender}: {message_content}")
|
|
|
|
# You can cancel event processing (e.g., stop message handling)
|
|
# if sender == "some_blocked_hash":
|
|
# event.cancel()
|
|
|
|
@bot.events.on("command_executed", priority=EventPriority.LOW)
|
|
def log_command(event):
|
|
# Example: event.data might contain {'command_name': 'ping', 'sender': '...', ...}
|
|
command_name = event.data.get('command_name', 'unknown')
|
|
sender = event.data.get('sender', 'unknown')
|
|
print(f"Command '{command_name}' executed by {sender}")
|
|
|
|
# You can define custom events too
|
|
@bot.command(name="special")
|
|
def special_command(ctx):
|
|
ctx.reply("Doing something special!")
|
|
# Dispatch a custom event
|
|
bot.events.dispatch(Event("special_action_taken", data={"user": ctx.sender}))
|
|
|
|
@bot.events.on("special_action_taken")
|
|
def handle_special(event):
|
|
user = event.data.get("user")
|
|
print(f"Special action was taken by user: {user}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
bot.run()
|
|
|
|
See :code:`lxmfy/events.py` for more details on the :code:`Event` structure and priorities.
|
|
|
|
Storage
|
|
-------
|
|
|
|
LXMFy provides JSON, SQLite, and In-Memory storage backends.
|
|
|
|
* **JSON:** Simple, human-readable. Good for small datasets. Configure with :code:`storage_type="json"` and :code:`storage_path="your_data_dir"`.
|
|
* **SQLite:** More efficient for larger datasets or frequent writes. Configure with :code:`storage_type="sqlite"` and :code:`storage_path="your_db_file.db"`.
|
|
* **Memory:** Entirely in-RAM storage. State is lost on shutdown. Configure with :code:`storage_type="memory"`.
|
|
|
|
You can access the storage interface via :code:`bot.storage`:
|
|
|
|
.. code-block:: python
|
|
|
|
# Save data
|
|
bot.storage.set("user_prefs:" + ctx.sender, {"theme": "dark"})
|
|
|
|
# Get data (with a default value)
|
|
prefs = bot.storage.get("user_prefs:" + ctx.sender, {})
|
|
theme = prefs.get("theme", "light")
|
|
|
|
# Check if data exists
|
|
if bot.storage.exists("some_key"):
|
|
print("Key exists!")
|
|
|
|
# Delete data
|
|
bot.storage.delete("old_data_key")
|
|
|
|
# Scan for keys with a prefix (useful for listing user data)
|
|
user_keys = bot.storage.scan("user_prefs:")
|
|
for key in user_keys:
|
|
user_data = bot.storage.get(key)
|
|
print(f"Data for {key}: {user_data}")
|
|
|
|
See :code:`lxmfy/storage.py` and the API reference for more details.
|
|
|
|
Permissions
|
|
-----------
|
|
|
|
LXMFy includes an optional role-based permission system. Enable it with :code:`permissions_enabled=True` during :code:`LXMFBot` initialization.
|
|
|
|
* **Roles:** Define roles with specific permissions (e.g., :code:`DefaultPerms.MANAGE_USERS`).
|
|
* **Permissions:** Granular flags defined in :code:`DefaultPerms` (e.g., :code:`USE_COMMANDS`, :code:`BYPASS_SPAM`).
|
|
* **Assignment:** Assign roles to user hashes.
|
|
|
|
See :code:`lxmfy/permissions.py`, the API reference, and potentially example cogs (if any are created) for usage details.
|
|
|
|
Signature Verification
|
|
----------------------
|
|
|
|
LXMFy provides configuration for LXMF's built-in cryptographic message signing and verification. All LXMF messages are automatically signed by the LXMF/RNS stack - LXMFy simply allows you to enforce signature verification policies.
|
|
|
|
**Configuration:**
|
|
|
|
Enable signature verification in your bot configuration:
|
|
|
|
.. code-block:: python
|
|
|
|
bot = LXMFBot(
|
|
name="SecureBot",
|
|
signature_verification_enabled=True, # Enable signature checking
|
|
require_message_signatures=False # Set to True to reject unsigned messages
|
|
)
|
|
|
|
**How It Works:**
|
|
|
|
LXMF automatically handles all cryptographic operations:
|
|
|
|
1. **Outgoing Messages:** LXMF automatically signs all messages using the sender's RNS identity during message packing.
|
|
|
|
2. **Incoming Messages:** LXMF automatically validates signatures using the sender's RNS identity and provides validation results.
|
|
|
|
3. **LXMFy's Role:** LXMFy checks LXMF's validation results and enforces your policy:
|
|
|
|
- If :code:`signature_verification_enabled=False`: All messages are accepted (default)
|
|
- If :code:`signature_verification_enabled=True` and :code:`require_message_signatures=False`: Messages are accepted but unsigned/invalid signatures are logged
|
|
- If :code:`signature_verification_enabled=True` and :code:`require_message_signatures=True`: Unsigned or invalid messages are rejected
|
|
|
|
4. **Permission Integration:** Users with :code:`BYPASS_SPAM` permission can bypass signature verification requirements.
|
|
|
|
**CLI Management:**
|
|
|
|
You can manage signature verification settings using the CLI:
|
|
|
|
.. code-block:: bash
|
|
|
|
# Test signature verification
|
|
lxmfy signatures test
|
|
|
|
# Enable signature verification
|
|
lxmfy signatures enable
|
|
|
|
# Disable signature verification
|
|
lxmfy signatures disable
|
|
|
|
**Technical Details:**
|
|
|
|
LXMF uses Ed25519 signatures provided by the RNS cryptography system. Every LXMF message includes the sender's signature, which is validated against their known RNS identity. LXMFy simply reads LXMF's :code:`message.signature_validated` property and :code:`message.unverified_reason` to enforce your bot's security policy.
|
|
|
|
Advanced Message Delivery
|
|
--------------------------
|
|
|
|
LXMFy supports advanced message delivery options for improved reliability.
|
|
|
|
Using Propagation Nodes
|
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Send messages through specific LXMF propagation nodes:
|
|
|
|
.. code-block:: python
|
|
|
|
from lxmfy import LXMFBot
|
|
|
|
bot = LXMFBot(name="PropagationBot")
|
|
|
|
@bot.command(name="send", description="Send via propagation node")
|
|
def send_command(ctx):
|
|
# Set a specific propagation node once (config-level)
|
|
bot.set_propagation_node("<propagation_node_hash_here>")
|
|
|
|
# Send using configured delivery strategy
|
|
bot.send(
|
|
ctx.sender,
|
|
"This message will use direct delivery with propagation fallback as configured"
|
|
)
|
|
|
|
Propagation nodes are useful when direct delivery is not possible or when you want to ensure message delivery through the Reticulum mesh network.
|
|
|
|
Configuring Retries
|
|
^^^^^^^^^^^^^^^^^^^
|
|
|
|
Configure automatic retry attempts for failed message deliveries via bot config:
|
|
|
|
.. code-block:: python
|
|
|
|
from lxmfy import LXMFBot
|
|
|
|
bot = LXMFBot(name="ReliableBot")
|
|
|
|
bot = LXMFBot(
|
|
name="ReliableBot",
|
|
direct_delivery_retries=5, # Retry direct delivery up to 5 times
|
|
propagation_fallback_enabled=True
|
|
)
|
|
|
|
@bot.command(name="important", description="Send important message with retries")
|
|
def important_command(ctx):
|
|
bot.send(ctx.sender, "This is an important message")
|
|
|
|
@bot.command(name="normal", description="Send with default retries")
|
|
def normal_command(ctx):
|
|
# Default direct_delivery_retries is 3
|
|
bot.send(ctx.sender, "This message uses default retry settings")
|
|
|
|
The retry system:
|
|
|
|
- Automatically tracks delivery attempts per destination
|
|
- Retries failed direct deliveries up to :code:`direct_delivery_retries`
|
|
- Resets the retry counter on successful delivery
|
|
- Logs retry attempts and failures for debugging
|