Создание ботов

Базовая структура

Минимальный бот LXMFy включает в себя:

  1. Импорт LXMFBot.

  2. Создание экземпляра LXMFBot с желаемой конфигурацией.

  3. Определение команд или обработчиков событий.

  4. Запуск бота с помощью bot.run().

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()

Использование шаблонов

LXMFy предоставляет несколько шаблонов для распространенных типов ботов. Вы можете использовать CLI для создания файла бота на основе шаблона.

# 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 meme bot (fetches memes from an API)
lxmfy create --template meme my_meme_bot

Выполнение этих команд создает файл Python (например, my_echo_bot.py), который импортирует и запускает выбранный шаблон. Затем вы можете изменить сгенерированный файл или сам код шаблона (lxmfy/templates/...).

Пример сгенерированного файла (:code:`my_meme_bot.py`):

from lxmfy.templates import MemeBot

if __name__ == "__main__":
    bot = MemeBot() # Creates an instance of the MemeBot template
    # You can optionally override the default name:
    # bot.bot.name = "My Awesome Meme Bot"
    bot.run()

Конфигурация бота

При создании экземпляра LXMFBot вы можете передать различные именованные аргументы для настройки его поведения. Список распространенных опций см. в разделе BotConfig в Справочнике API или в Руководстве по быстрому запуску.

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()

Установка иконки бота (поле LXMF)

Вы можете присвоить своему боту пользовательскую иконку, которая будет отображаться в совместимых клиентах LXMF. Для этого используется LXMF.FIELD_ICON_APPEARANCE, и ее можно установить при отправке сообщений.

Сначала убедитесь, что у вас есть необходимые импорты:

from lxmfy import IconAppearance, pack_icon_appearance_field

Затем вы можете определить и использовать иконку:

# 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)

Это поле self.bot_icon_field можно предварительно рассчитать и использовать повторно для всех сообщений, отправляемых ботом.

Использование модулей (расширений)

Модули позволяют вам организовывать команды и прослушиватели событий в отдельные файлы (модули), сохраняя основной файл вашего бота в чистоте.

  1. Создайте каталог :code:`cogs` (или любое другое имя, которое вы указали для cogs_dir в BotConfig).

  2. Создайте файлы Python внутри каталога cogs (например, utility.py).

  3. Определите класс, который наследуется от lxmfy.Cog (необязательно, но рекомендуется) или является просто стандартным классом.

  4. Определите команды как методы внутри класса с помощью декоратора @Command.

  5. Создайте функцию :code:`setup(bot)` в файле модуля, которую LXMFy вызовет для регистрации модуля.

Пример (:code:`cogs/utility.py`):

from lxmfy import Command, 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

Основной файл бота (:code:`my_bot.py`):

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()

Когда бот запустится, он автоматически найдет utility.py, вызовет его функцию setup, которая создаст экземпляр UtilityCog и зарегистрирует его с помощью bot.add_cog(). После этого станут доступны команды, определенные в модуле (uptime, info).

Handling Messages

LXMFy предоставляет несколько способов обработки входящих сообщений на разных этапах.

Обработчик первого сообщения

Обработка первого сообщения от каждого нового пользователя (полезно для приветственных сообщений):

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()

Общий обработчик сообщений

Обработка всех входящих сообщений перед обработкой команд:

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()

Порядок обработки обработчиков сообщений:

  1. Обработчик первого сообщения (если first_message_enabled=True и это первое сообщение от отправителя)

  2. Общие обработчики сообщений (зарегистрированные с помощью @bot.on_message())

  3. Обработка команд (если сообщение соответствует зарегистрированной команде)

Обработчики могут возвращать True для прекращения дальнейшей обработки или False для перехода к следующему этапу.

Обработка событий

Вы можете регистрировать обработчики для различных событий бота с помощью декоратора @bot.events.on().

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()

См. lxmfy/events.py для получения дополнительной информации о структуре и приоритетах Event.

Хранилище

LXMFy предоставляет бэкенды для хранения данных в форматах JSON и SQLite.

  • JSON: Простой, человекочитаемый формат. Подходит для небольших наборов данных. Настраивается с помощью storage_type="json" и storage_path="your_data_dir".

  • SQLite: Более эффективен для больших наборов данных или частых записей. Настраивается с помощью storage_type="sqlite" и storage_path="your_db_file.db".

Вы можете получить доступ к интерфейсу хранилища через bot.storage:

# 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}")

См. lxmfy/storage.py и справочник по API для получения дополнительной информации.

Разрешения

LXMFy включает дополнительную систему разрешений на основе ролей. Включите ее с помощью permissions_enabled=True во время инициализации LXMFBot.

  • Роли: Определите роли с определенными разрешениями (например, DefaultPerms.MANAGE_USERS).

  • Разрешения: Детальные флаги, определенные в DefaultPerms (например, USE_COMMANDS, BYPASS_SPAM).

  • Назначение: Назначьте роли хэшам пользователей.

Подробности использования см. в lxmfy/permissions.py, справочнике по API и, возможно, в примерах модулей (если они есть).

Проверка подписи

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:

Включите проверку подписи в конфигурации вашего бота:

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

Как это работает:

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: LXMFy проверяет результаты проверки LXMF и применяет вашу политику:

    • Если signature_verification_enabled=False: все сообщения принимаются (по умолчанию)

    • Если signature_verification_enabled=True и require_message_signatures=False: сообщения принимаются, но неподписанные/недействительные подписи регистрируются

    • Если signature_verification_enabled=True и require_message_signatures=True: неподписанные или недействительные сообщения отклоняются

  4. Permission Integration: Users with BYPASS_SPAM permission can bypass signature verification requirements.

Управление через CLI:

Вы можете управлять настройками проверки подписи с помощью CLI:

# Test signature verification
lxmfy signatures test

# Enable signature verification
lxmfy signatures enable

# Disable signature verification
lxmfy signatures disable

Технические детали:

LXMF использует подписи Ed25519, предоставляемые криптографической системой RNS. Каждое сообщение LXMF включает подпись отправителя, которая проверяется по его известному идентификатору RNS. LXMFy просто считывает свойства LXMF message.signature_validated и message.unverified_reason для применения политики безопасности вашего бота.

Расширенная доставка сообщений

LXMFy поддерживает расширенные параметры доставки сообщений для повышения надежности.

Использование узлов распространения

Отправляйте сообщения через определенные узлы распространения LXMF:

from lxmfy import LXMFBot

bot = LXMFBot(name="PropagationBot")

@bot.command(name="send", description="Send via propagation node")
def send_command(ctx):
    # Send through a specific propagation node
    bot.send(
        ctx.sender,
        "This message was routed through a propagation node",
        propagation_node="<propagation_node_hash_here>"
    )

Узлы распространения полезны, когда прямая доставка невозможна или когда вы хотите обеспечить доставку сообщений через ячеистую сеть Reticulum.

Настройка повторных попыток

Настройте автоматические повторные попытки для неудачных доставок сообщений:

from lxmfy import LXMFBot

bot = LXMFBot(name="ReliableBot")

@bot.command(name="important", description="Send important message with retries")
def important_command(ctx):
    # Send with custom retry count
    bot.send(
        ctx.sender,
        "This is an important message that will retry up to 5 times on failure",
        max_retries=5
    )

@bot.command(name="normal", description="Send with default retries")
def normal_command(ctx):
    # Default max_retries is 3
    bot.send(ctx.sender, "This message uses default retry settings")

Система повторных попыток:

  • Автоматически отслеживает попытки доставки для каждого получателя

  • Повторяет неудачные доставки до указанного max_retries

  • Сбрасывает счетчик повторных попыток при успешной доставке

  • Регистрирует попытки повтора и сбои для отладки