Criar bots¶
Estrutura básica¶
Um bot LXMFy mínimo envolve:
- Importar
LXMFBot. - Instanciar
LXMFBotcom a configuração pretendida. - Definir comandos ou manipuladores de eventos.
- Executar o bot com
bot.run().
from lxmfy import LXMFBot
# 1. Instanciar o bot
bot = LXMFBot(
name="SimpleBot",
command_prefix="!",
storage_path="simple_data"
)
# 2. Definir comandos
@bot.command(name="ping", description="Responds with pong")
def ping_command(ctx):
# ctx é um objeto de contexto com informação da mensagem
# ctx.sender: hash LXMF do remetente
# ctx.content: conteúdo completo da mensagem
# ctx.args: lista de argumentos após o comando
# ctx.reply(message): função para enviar uma resposta
# (também aceita argumentos nomeados como title="My Title", lxmf_fields=some_fields)
ctx.reply("Pong!")
# Para tarefas de longa duração, pode usar comandos em thread:
# 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) # Simula uma operação demorada
# ctx.reply("Long operation complete!")
# Importante: comandos em thread não devem interagir diretamente com o RNS ou o 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. Executar o bot
if __name__ == "__main__":
print(f"Starting bot: {bot.config.name}")
print(f"Bot LXMF Address: {bot.local.hash}")
bot.run()
Utilizar templates¶
O LXMFy fornece vários templates para tipos de bot comuns. Pode usar a CLI para gerar um ficheiro de bot a partir de um template.
# Criar um bot de eco
lxmfy create --template echo my_echo_bot
# Criar um bot de lembretes (usa armazenamento SQLite)
lxmfy create --template reminder my_reminder_bot
# Criar um bot de notas (usa armazenamento JSON)
lxmfy create --template note my_note_bot
# Criar um bot de teste de cogs (testa funcionalidades de carregamento de cogs)
lxmfy create --template cogtest my_cog_test_bot
# Criar um bot de sala RRC (entra em hubs e responde a @menções)
lxmfy create --template rrc my_rrc_bot
# Ou executar o template diretamente
lxmfy run rrc
Estes comandos criam um ficheiro Python (ex.: my_echo_bot.py) que
importa e executa o template escolhido. Pode depois modificar o ficheiro
gerado ou o próprio código do template (lxmfy/templates/...).
Exemplo de ficheiro gerado (my_cog_test_bot.py):
from lxmfy.templates import CogTestBot
if __name__ == "__main__":
bot = CogTestBot() # Cria uma instância do template CogTestBot
# Pode, opcionalmente, alterar o nome predefinido:
# bot.bot.name = "My Cog Test Bot"
bot.run()
Configuração do bot¶
Ao criar uma instância de LXMFBot, pode passar vários argumentos
nomeados para configurar o seu comportamento. Consulte a secção
BotConfig na Referência da API ou o Guia de
início rápido para uma lista de opções comuns.
from lxmfy import LXMFBot
bot = LXMFBot(
name="ConfiguredBot",
announce=3600, # Anunciar a cada hora
admins={"your_admin_hash_here"}, # Definir utilizador(es) administrador(es)
command_prefix="$", # Usar '$' como prefixo
storage_type="sqlite", # Usar base de dados SQLite
storage_path="data/my_bot_data.db", # Especificar o caminho do ficheiro da BD
rate_limit=10, # Permitir 10 mensagens / minuto
cooldown=30, # Cooldown de 30 segundos
permissions_enabled=True # Ativar permissões por papéis
)
if __name__ == "__main__":
# Também pode modificar a configuração após a instanciação
# Nota: algumas opções devem ser definidas durante a inicialização
bot.config.max_warnings = 5
bot.spam_protection.config.max_warnings = 5 # Atualizar também o protetor de spam
bot.run()
Definir um ícone para o bot (campo LXMF)¶
Pode dar ao bot um ícone personalizado que aparece em clientes LXMF
compatíveis. Usa o LXMF.FIELD_ICON_APPEARANCE e pode ser definido ao
enviar mensagens.
Primeiro, garanta que tem os imports necessários:
Depois, pode definir e usar o ícone:
# Na classe do bot ou na configuração
icon_data = IconAppearance(
icon_name="robot_2", # Escolha entre os Material Symbols
fg_color=b'\x00\xFF\x00', # Verde
bg_color=b'\x33\x33\x33' # Cinzento escuro
)
self.bot_icon_field = pack_icon_appearance_field(icon_data)
# Ao enviar uma mensagem ou responder:
ctx.reply("Message from your bot!", lxmf_fields=self.bot_icon_field)
# ou
# bot.send(destination, "Another message", lxmf_fields=self.bot_icon_field)
Este self.bot_icon_field pode ser pré-calculado e reutilizado em todas
as mensagens enviadas pelo bot.
Comandos estruturados via campos LXMF¶
Além de comandos de texto, o LXMFy suporta comandos enviados em campos
de mensagem LXMF usando FIELD_COMMANDS (0x09). É útil para fluxos
estruturados de pedido/resposta entre clientes LXMF e bots.
Quando uma mensagem contém FIELD_COMMANDS, o bot extrai o nome do
comando e os argumentos, encaminha-os pelo mesmo registo de comandos dos
comandos de texto e inclui automaticamente FIELD_RESULTS (0x0A) na
resposta.
Receção de comandos estruturados
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>")
O objeto ctx nos callbacks de comandos por campos inclui:
ctx.fields: o dicionário de campos LXMF em bruto da mensagem recebidactx.request_id: orequest_iddoFIELD_COMMANDSrecebido (se existir)
Envio de um comando estruturado a partir de um cliente LXMF
import LXMF
from lxmfy import FIELD_COMMANDS
lxm = LXMF.LXMessage(
destination,
source,
b"", # o conteúdo pode ser vazio em comandos apenas por campos
desired_method=LXMF.LXMessage.DIRECT,
)
lxm.fields[FIELD_COMMANDS] = {
"command": "add",
"args": ["3", "5"],
"request_id": "req-42", # opcional, para correlação
}
router.handle_outbound(lxm)
Desativar comandos por campos
Se quiser que o bot ignore FIELD_COMMANDS e processe apenas comandos
de texto, defina:
Utilizar cogs (extensões)¶
Os cogs permitem organizar comandos e listeners de eventos em ficheiros separados (módulos), mantendo o ficheiro principal do bot mais limpo.
- Crie um diretório
cogs(ou o que definiu emcogs_dirnoBotConfig). - Crie ficheiros Python dentro do diretório
cogs(ex.:utility.py). - Defina uma classe que herda de
lxmfy.Cog(opcional, mas boa prática) ou que é apenas uma classe normal. - Defina comandos como métodos da classe usando o decorador
@Command. - Crie uma função
setup(bot)no ficheiro do cog, que o LXMFy chama para registar o cog.
Exemplo (cogs/utility.py):
from lxmfy import Command
from lxmfy.commands import Cog # Importar Cog se herdar
import time
class UtilityCog: # Ou class UtilityCog(Cog):
def __init__(self, bot):
self.bot = bot
self.start_time = time.time()
@Command(name="uptime", description="Shows bot uptime")
# Nota: métodos em cogs recebem frequentemente 'self' e '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) # Simula uma operação demorada
ctx.reply("Long cog task completed!")
# Esta função é necessária para o cog ser carregado
def setup(bot):
cog_instance = UtilityCog(bot)
bot.add_cog(cog_instance) # Regista a instância do cog no bot
Ficheiro principal do bot (my_bot.py):
from lxmfy import LXMFBot
bot = LXMFBot(
name="CogBot",
cogs_enabled=True, # Garanta que os cogs estão ativos (predefinição)
cogs_dir="cogs" # Apontar para o diretório
)
if __name__ == "__main__":
# Os cogs são carregados automaticamente durante a inicialização do LXMFBot
# se cogs_enabled for True.
bot.run()
Quando o bot arranca, encontra automaticamente o utility.py, chama a
sua função setup, que cria uma instância de UtilityCog e a regista
com bot.add_cog(). Os comandos definidos no cog (uptime, info)
ficam então disponíveis.
Cogs de scripts externos (suporte multilinguagem)¶
Também pode escrever extensões do bot noutras linguagens (ex.: Bash, Ruby, Perl, Go, C) usando cogs de scripts externos.
- Crie um script executável no diretório
cogs. - Adicione um shebang no topo do script (ex.:
#!/bin/bash). - Garanta que o script é executável (
chmod +x your_script).
Quando o bot arranca, regista automaticamente qualquer ficheiro
executável no diretório cogs (que não termine em .py) como comando
do bot.
Protocolo de argumentos:
$1: hash LXMF do remetente.$2: conteúdo completo da mensagem.$3,$4, ...: argumentos individuais do comando.
Variáveis de ambiente:
LXMFY_SENDER: o hash de identidade do remetente.LXMFY_CONTENT: o conteúdo completo da mensagem.LXMFY_HAS_ADMIN:trueoufalseconforme o estatuto de administrador do remetente.
Exemplo de cog em Bash (cogs/greet.sh):
Quando um utilizador envia /greet hello, o bot executa este script e
responde com o seu stdout: Hello from Bash! You sent: /greet hello.
Classificação local de intents NLP¶
O LXMFy inclui um classificador local de intents. Faz corresponder o texto da mensagem a frases de exemplo etiquetadas quando o texto não corresponde exatamente a um prefixo de comando.
- Ative o NLP na configuração do bot:
nlp_enabled=True. - Defina intents com o decorador
@bot.intent.
@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.")
A correspondência usa vetores TF-IDF e semelhança por cosseno. Todo o cálculo corre no anfitrião do bot. Nenhum texto é enviado para uma API externa.
Exportação e importação do modelo
Exporte e importe um modelo treinado para que bots maiores evitem o retreino a cada arranque:
# Exportar o modelo
model_data = bot.nlp.export_model()
# Guardar model_data num ficheiro ou base de dados
# Mais tarde, importar de volta
bot.nlp.import_model(model_data)
Suporte de RNS Links¶
Os bots podem estabelecer e responder a RNS Links diretos para tráfego com estado, streaming ou de maior largura de banda do que pacotes LXMF individuais.
- Ative o suporte de links na configuração:
link_support_enabled=True. - Peça um link:
bot.request_link(destination_hash). Também pode especificar um nome de aplicação e aspects personalizados:bot.request_link(dest, callback, "my_app", "aspect1"). - Trate links recebidos: registe um callback com
bot.on_link(handler).
def handle_link(link):
print(f"Link established with {RNS.hexrep(link.destination.hash)}")
# Pode agora usar o link para comunicação RNS direta
bot.on_link(handle_link)
Segurança e sandboxing:
- Timeouts: os cogs externos têm um timeout predefinido (30s) para
evitar bloqueios. Configurável via
external_cogs_timeout. - Threads: todos os cogs externos correm em threads separadas e não bloqueiam o bot.
- Sandbox do processo do bot (apenas Linux): quando
landlock_enabled=True(predefinição) e o kernel suporta Landlock LSM (5.13+), o bot aplica uma sandbox de sistema de ficheiros ao próprio processo após o arranque. Os caminhos com escrita limitam-se ao armazenamento, configuração, cogs, configuração Reticulum e diretórios temporários. Use a variável de ambienteLXMFY_LANDLOCK=0para desativar ouLXMFY_LANDLOCK=1para forçar a tentativa. - Sandbox dos cogs externos (apenas Linux): quando
external_cogs_sandbox_enabled=True(predefinição), os cogs de script executáveis correm num ambiente restrito. Definaexternal_cogs_sandbox_typepara um de: auto(predefinição): prefere Landlock quando suportado, senãobubblewrap(bwrap), senãofirejaillandlock: sandbox só com Landlock viapreexec_fn(regras mais restritas que a sandbox do processo do bot)bwrap: sandbox bubblewrap com bind só de leiturafirejail: perfil privado firejail sem redenone: sem sandbox de subprocesso- Estado: chame
bot.get_landlock_status()para inspecionar o suporte do kernel, se o Landlock foi pedido e se a sandbox do processo do bot está ativa.
Tratar mensagens¶
O LXMFy oferece várias formas de tratar mensagens recebidas em diferentes fases do processamento.
Manipulador da primeira mensagem¶
Trate a primeira mensagem de cada novo utilizador (útil para mensagens de boas-vindas):
from lxmfy import LXMFBot
bot = LXMFBot(
name="WelcomeBot",
first_message_enabled=True # Tem de ser True (predefinição)
)
@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 # Devolva True para parar o processamento desta mensagem
if __name__ == "__main__":
bot.run()
Manipulador geral de mensagens¶
Trate todas as mensagens recebidas antes do processamento de comandos:
from lxmfy import LXMFBot
bot = LXMFBot(name="EchoBot")
@bot.on_message()
def echo_non_commands(sender, message):
content = message.content.decode("utf-8").strip()
# Verificar se é um comando - se for, deixar o manipulador de comandos tratar
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 # Deixar o manipulador de comandos processar
# Não é um comando, fazer eco de volta
bot.send(sender, f"You said: {content}")
return False # Devolver False para continuar o processamento (embora nenhum comando corresponda)
@bot.command(name="hello", description="Say hello")
def hello_command(ctx):
ctx.reply("Hello! This is a command response.")
if __name__ == "__main__":
bot.run()
Ordem de processamento dos manipuladores de mensagens:
- Manipulador da primeira mensagem (se
first_message_enabled=Truee for a primeira mensagem deste remetente) - Manipuladores gerais de mensagens (registados com
@bot.on_message()) - Processamento de comandos (se a mensagem corresponder a um comando registado)
Os manipuladores podem devolver True para parar o processamento ou
False para continuar para a fase seguinte.
Tratar eventos¶
Pode registar manipuladores para vários eventos do bot usando o
decorador @bot.events.on().
from lxmfy import LXMFBot
from lxmfy.events import EventPriority # Opcional para prioridade
bot = LXMFBot(name="EventBot")
@bot.events.on("message_received")
def log_message(event):
# O objeto de evento contém detalhes
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}")
# Pode cancelar o processamento do evento (ex.: parar o tratamento da mensagem)
# if sender == "some_blocked_hash":
# event.cancel()
@bot.events.on("command_executed", priority=EventPriority.LOW)
def log_command(event):
# Exemplo: event.data pode conter {'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}")
# Também pode definir eventos personalizados
@bot.command(name="special")
def special_command(ctx):
ctx.reply("Doing something special!")
# Emitir um evento personalizado
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()
Consulte lxmfy/events.py para mais detalhes sobre a estrutura Event
e as prioridades.
Armazenamento¶
O LXMFy fornece backends de armazenamento JSON, SQLite e em memória.
- JSON: simples, legível por humanos. Bom para conjuntos de dados
pequenos. Configure com
storage_type="json"estorage_path="your_data_dir". - SQLite: mais eficiente para conjuntos de dados maiores ou escritas
frequentes. Configure com
storage_type="sqlite"estorage_path="your_db_file.db". - Memory: armazenamento inteiramente em RAM. O estado perde-se no
encerramento. Configure com
storage_type="memory".
Pode aceder à interface de armazenamento via bot.storage:
# Guardar dados
bot.storage.set("user_prefs:" + ctx.sender, {"theme": "dark"})
# Obter dados (com valor predefinido)
prefs = bot.storage.get("user_prefs:" + ctx.sender, {})
theme = prefs.get("theme", "light")
# Verificar se os dados existem
if bot.storage.exists("some_key"):
print("Key exists!")
# Apagar dados
bot.storage.delete("old_data_key")
# Procurar chaves com um prefixo (útil para listar dados de utilizadores)
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}")
Consulte lxmfy/storage.py e a referência da API para mais detalhes.
Permissões¶
O LXMFy inclui um sistema opcional de permissões por papéis. Ative-o com
permissions_enabled=True durante a inicialização do LXMFBot.
- Papéis: defina papéis com permissões específicas (ex.:
DefaultPerms.MANAGE_USERS). - Permissões: flags granulares definidas em
DefaultPerms(ex.:USE_COMMANDS,BYPASS_SPAM). - Atribuição: atribua papéis a hashes de utilizadores.
Consulte lxmfy/permissions.py, a referência da API e eventuais cogs de
exemplo (se existirem) para detalhes de utilização.
Verificação de assinaturas¶
O LXMFy fornece configuração para a assinatura e verificação criptográfica de mensagens integrada no LXMF. Todas as mensagens LXMF são automaticamente assinadas pela stack LXMF/RNS - o LXMFy apenas permite aplicar políticas de verificação de assinaturas.
Configuração:
Ative a verificação de assinaturas na configuração do bot:
bot = LXMFBot(
name="SecureBot",
signature_verification_enabled=True, # Ativar a verificação de assinaturas
require_message_signatures=False # Definir como True para rejeitar mensagens não assinadas
)
Como funciona:
O LXMF trata automaticamente de todas as operações criptográficas:
- Mensagens enviadas: o LXMF assina automaticamente todas as mensagens usando a identidade RNS do remetente durante o empacotamento da mensagem.
- Mensagens recebidas: o LXMF valida automaticamente as assinaturas usando a identidade RNS do remetente e fornece os resultados da validação.
- Papel do LXMFy: o LXMFy verifica os resultados da validação do
LXMF e aplica a sua política:
- Se
signature_verification_enabled=False: todas as mensagens são aceites (predefinição) - Se
signature_verification_enabled=Trueerequire_message_signatures=False: as mensagens são aceites, mas assinaturas ausentes/inválidas são registadas - Se
signature_verification_enabled=Trueerequire_message_signatures=True: mensagens não assinadas ou inválidas são rejeitadas
- Se
- Integração com permissões: utilizadores com a permissão
BYPASS_SPAMpodem ignorar os requisitos de verificação de assinaturas.
Gestão via CLI:
Pode gerir as definições de verificação de assinaturas usando a CLI:
# Testar a verificação de assinaturas
lxmfy signatures test
# Ativar a verificação de assinaturas
lxmfy signatures enable
# Desativar a verificação de assinaturas
lxmfy signatures disable
Detalhes técnicos:
O LXMF usa assinaturas Ed25519 fornecidas pelo sistema criptográfico
RNS. Cada mensagem LXMF inclui a assinatura do remetente, validada
contra a sua identidade RNS conhecida. O LXMFy apenas lê as propriedades
message.signature_validated e message.unverified_reason do LXMF para
aplicar a política de segurança do bot.
Entrega de mensagens¶
Utilizar nós de propagação¶
Envie mensagens através de nós de propagação LXMF específicos:
from lxmfy import LXMFBot
bot = LXMFBot(name="PropagationBot")
@bot.command(name="send", description="Send via propagation node")
def send_command(ctx):
# Definir um nó de propagação específico uma vez (nível de configuração)
bot.set_propagation_node("<propagation_node_hash_here>")
# Enviar usando a estratégia de entrega configurada
bot.send(
ctx.sender,
"This message will use direct delivery with propagation fallback as configured"
)
Use um nó de propagação quando o destino está offline ou a entrega direta continua a falhar no caminho atual.
Configurar repetições¶
Configure tentativas automáticas de repetição para entregas falhadas via configuração do bot:
from lxmfy import LXMFBot
bot = LXMFBot(name="ReliableBot")
bot = LXMFBot(
name="ReliableBot",
direct_delivery_retries=5, # Repetir a entrega direta até 5 vezes
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):
# O direct_delivery_retries predefinido é 3
bot.send(ctx.sender, "This message uses default retry settings")
O sistema de repetição:
- Regista automaticamente as tentativas de entrega por destino
- Repete entregas diretas falhadas até
direct_delivery_retries - Repõe o contador de repetições numa entrega bem-sucedida
- Regista tentativas e falhas de repetição para depuração
Reticulum Relay Chat (RRC)¶
Os bots LXMFy podem entrar em hubs RRC como clientes normais através de RNS Links usando envelopes CBOR. É compatível com hubs estilo NomadNet e rrcd (incluindo o MeshChatX quando aloja ou entra no mesmo hub).
A configuração Reticulum importa¶
O bot tem de usar a mesma rede Reticulum do hub. O MeshChatX usa
tipicamente ~/.reticulum com interfaces backbone ou TCP. O diretório
config/ local do projeto usa frequentemente um nome de instância
isolado e apenas AutoInterface, por isso os anúncios do hub nunca chegam
e aparece Hub identity unknown.
Prefira uma destas opções:
- Defina
reticulum_config_dirpara a configuração do seu utilizador (normalmente~/.reticulum) - Ou exporte
LXMFY_RETICULUM_CONFIG_DIR=~/.reticulum - Mantenha o MeshChatX ou o
rnsdem execução para a instância partilhada estar ativa antes do bot arrancar
O template rrc usa ~/.reticulum por predefinição quando esse
diretório existe.
Início rápido com o template¶
Predefinições:
- Hub:
664fc0e8d2e448658e37bb3f34e6c88f - Sala:
#general - Configuração Reticulum:
~/.reticulum(ouLXMFY_RETICULUM_CONFIG_DIR)
Deve ver registos de ligação ao hub, welcome, auto-join e `RRC joined
general`.¶
Bot RRC programático¶
from lxmfy import LXMFBot, RRCMessage
bot = LXMFBot(
name="RoomBot",
reticulum_config_dir="~/.reticulum",
rrc_enabled=True,
rrc_hubs=["your_rrc_hub_destination_hash"],
rrc_rooms=["general"],
rrc_nick="RoomBot",
rrc_auto_reconnect=True,
rrc_persist_sessions=True,
)
@bot.on_rrc
def on_rrc(event, client, payload):
if event == "welcome":
bot.logger.info("Welcomed by hub")
return
if event != "msg" or not isinstance(payload, RRCMessage):
return
if payload.mention and payload.room:
client.send_message(
payload.room,
f"Heard you, {payload.nick}",
)
bot.run()
Ou ligue em tempo de execução:
bot.connect_rrc("hub_destination_hash", rooms=["general"])
bot.rrc.send_message("general", "hello room")
bot.rrc.send_action("general", "waves")
bot.disconnect_rrc()
Comportamento da sessão¶
- HELLO / WELCOME, JOIN / PART, MSG / NOTICE / ACTION, PING / PONG, ERROR, RESOURCE_ENVELOPE
- Reconexão automática com reentrada nas salas após WELCOME
- Limite de hubs e aplicação de rate limit no lado do cliente
- Persistência de sessão entre reinícios (
rrc_persist_sessions, ativa por predefinição) - A persistência da fila LXMF de saída é separada
(
message_persistence_enabled)