mirror of
https://github.com/zvx-echo6/meshai.git
synced 2026-05-21 23:24:44 +02:00
Remove dead channel/mention code — DM-only bot cleanup
MeshAI is now DM-only. Removed all unreachable channel response paths, @mention detection, ChannelsConfig, and channel TUI menu. Fixed restart mechanism with integrated watcher and SIGKILL fallback. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
32147ccaec
commit
1e033316fb
7 changed files with 81 additions and 199 deletions
|
|
@ -6,9 +6,8 @@
|
||||||
|
|
||||||
# === BOT IDENTITY ===
|
# === BOT IDENTITY ===
|
||||||
bot:
|
bot:
|
||||||
name: ai # Bot's trigger name (users say "@ai help")
|
name: ai # Bot's display name
|
||||||
owner: "" # Owner's callsign (optional)
|
owner: "" # Owner's callsign (optional)
|
||||||
respond_to_mentions: true # Respond when name is mentioned
|
|
||||||
respond_to_dms: true # Respond to direct messages
|
respond_to_dms: true # Respond to direct messages
|
||||||
filter_bbs_protocols: true # Ignore advBBS sync/notification messages
|
filter_bbs_protocols: true # Ignore advBBS sync/notification messages
|
||||||
|
|
||||||
|
|
@ -19,12 +18,6 @@ connection:
|
||||||
tcp_host: localhost # For TCP connection (meshtasticd)
|
tcp_host: localhost # For TCP connection (meshtasticd)
|
||||||
tcp_port: 4403
|
tcp_port: 4403
|
||||||
|
|
||||||
# === CHANNEL FILTERING ===
|
|
||||||
channels:
|
|
||||||
mode: all # all | whitelist
|
|
||||||
whitelist: # Only respond on these channels (if mode=whitelist)
|
|
||||||
- 0
|
|
||||||
|
|
||||||
# === RESPONSE BEHAVIOR ===
|
# === RESPONSE BEHAVIOR ===
|
||||||
response:
|
response:
|
||||||
delay_min: 2.2 # Min delay before responding (seconds)
|
delay_min: 2.2 # Min delay before responding (seconds)
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ if [ ! -f "$MESHAI_CONFIG" ]; then
|
||||||
bot:
|
bot:
|
||||||
name: ai
|
name: ai
|
||||||
owner: ""
|
owner: ""
|
||||||
respond_to_mentions: true
|
|
||||||
respond_to_dms: true
|
respond_to_dms: true
|
||||||
filter_bbs_protocols: true
|
filter_bbs_protocols: true
|
||||||
|
|
||||||
|
|
@ -25,11 +24,6 @@ connection:
|
||||||
tcp_host: localhost
|
tcp_host: localhost
|
||||||
tcp_port: 4403
|
tcp_port: 4403
|
||||||
|
|
||||||
channels:
|
|
||||||
mode: all
|
|
||||||
whitelist:
|
|
||||||
- 0
|
|
||||||
|
|
||||||
response:
|
response:
|
||||||
delay_min: 2.2
|
delay_min: 2.2
|
||||||
delay_max: 3.0
|
delay_max: 3.0
|
||||||
|
|
@ -74,38 +68,50 @@ ttyd -W -p 7682 \
|
||||||
/bin/bash -c 'while true; do python3 -m meshai --config-file "$MESHAI_CONFIG" --config; sleep 1; done' &
|
/bin/bash -c 'while true; do python3 -m meshai --config-file "$MESHAI_CONFIG" --config; sleep 1; done' &
|
||||||
|
|
||||||
# Keep ttyd running even if bot fails
|
# Keep ttyd running even if bot fails
|
||||||
trap "kill %1 2>/dev/null; kill %2 2>/dev/null" EXIT
|
trap "kill %1 2>/dev/null" EXIT
|
||||||
|
|
||||||
# Restart watcher - monitors for restart signal from config tool
|
# Kill bot gracefully with SIGKILL fallback
|
||||||
BOT_PID_FILE="/tmp/meshai_bot.pid"
|
kill_bot() {
|
||||||
(
|
local pid=$1
|
||||||
while true; do
|
if ! kill -0 "$pid" 2>/dev/null; then
|
||||||
if [ -f /tmp/meshai_restart ]; then
|
return
|
||||||
rm -f /tmp/meshai_restart
|
fi
|
||||||
echo "Restart signal received, restarting bot..."
|
kill "$pid" 2>/dev/null || true
|
||||||
# Kill bot using PID file
|
echo "Sent SIGTERM to bot (PID $pid)"
|
||||||
if [ -f "$BOT_PID_FILE" ]; then
|
# Wait up to 5 seconds for graceful shutdown
|
||||||
BOT_PID=$(cat "$BOT_PID_FILE")
|
for i in 1 2 3 4 5; do
|
||||||
if kill -0 "$BOT_PID" 2>/dev/null; then
|
kill -0 "$pid" 2>/dev/null || return
|
||||||
kill "$BOT_PID" 2>/dev/null || true
|
sleep 1
|
||||||
echo "Sent TERM to bot (PID $BOT_PID)"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
# Debounce - wait before checking for more signals
|
|
||||||
sleep 3
|
|
||||||
fi
|
|
||||||
sleep 2
|
|
||||||
done
|
done
|
||||||
) &
|
# Force kill if still alive
|
||||||
|
if kill -0 "$pid" 2>/dev/null; then
|
||||||
|
kill -9 "$pid" 2>/dev/null || true
|
||||||
|
echo "Sent SIGKILL to bot (PID $pid)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Start the bot in a loop - retry on failure
|
# Start the bot in a loop with integrated restart watcher
|
||||||
echo "Starting MeshAI..."
|
echo "Starting MeshAI..."
|
||||||
|
rm -f /tmp/meshai_restart
|
||||||
while true; do
|
while true; do
|
||||||
python -m meshai --config-file "$MESHAI_CONFIG" &
|
python -m meshai --config-file "$MESHAI_CONFIG" &
|
||||||
BOT_PID=$!
|
BOT_PID=$!
|
||||||
echo "$BOT_PID" > "$BOT_PID_FILE"
|
echo "$BOT_PID" > /tmp/meshai.pid
|
||||||
wait $BOT_PID || true
|
echo "Bot started (PID $BOT_PID)"
|
||||||
rm -f "$BOT_PID_FILE"
|
|
||||||
echo "Bot exited. Check config at http://localhost:7682. Retrying in 5s..."
|
# Poll: wait for bot to exit OR restart signal
|
||||||
sleep 5
|
while kill -0 $BOT_PID 2>/dev/null; do
|
||||||
|
if [ -f /tmp/meshai_restart ]; then
|
||||||
|
rm -f /tmp/meshai_restart
|
||||||
|
echo "Restart signal received, restarting bot..."
|
||||||
|
kill_bot $BOT_PID
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
wait $BOT_PID 2>/dev/null || true
|
||||||
|
rm -f /tmp/meshai.pid
|
||||||
|
echo "Bot exited. Restarting in 3s..."
|
||||||
|
sleep 3
|
||||||
done
|
done
|
||||||
|
|
|
||||||
|
|
@ -69,15 +69,14 @@ class Configurator:
|
||||||
disabled_count = len(self.config.commands.disabled_commands)
|
disabled_count = len(self.config.commands.disabled_commands)
|
||||||
cmd_status = f"{disabled_count} disabled" if disabled_count else "all enabled"
|
cmd_status = f"{disabled_count} disabled" if disabled_count else "all enabled"
|
||||||
|
|
||||||
table.add_row("1", "Bot Settings", f"@{self.config.bot.name}")
|
table.add_row("1", "Bot Settings", self.config.bot.name)
|
||||||
table.add_row("2", "Connection", f"{self.config.connection.type}")
|
table.add_row("2", "Connection", f"{self.config.connection.type}")
|
||||||
table.add_row("3", "LLM Backend", f"{self.config.llm.backend}/{self.config.llm.model}")
|
table.add_row("3", "LLM Backend", f"{self.config.llm.backend}/{self.config.llm.model}")
|
||||||
table.add_row("4", "Response Settings", f"{self.config.response.max_length}ch max")
|
table.add_row("4", "Response Settings", f"{self.config.response.max_length}ch max")
|
||||||
table.add_row("5", "Channels", f"{self.config.channels.mode}")
|
table.add_row("5", "History & Memory", f"{self.config.history.max_messages_per_user} msgs")
|
||||||
table.add_row("6", "History & Memory", f"{self.config.history.max_messages_per_user} msgs")
|
table.add_row("6", "Commands", cmd_status)
|
||||||
table.add_row("7", "Commands", cmd_status)
|
table.add_row("7", "Weather", f"{self.config.weather.primary}")
|
||||||
table.add_row("8", "Weather", f"{self.config.weather.primary}")
|
table.add_row("8", "Setup Wizard", "[dim]First-time setup[/dim]")
|
||||||
table.add_row("9", "Setup Wizard", "[dim]First-time setup[/dim]")
|
|
||||||
|
|
||||||
console.print(table)
|
console.print(table)
|
||||||
console.print()
|
console.print()
|
||||||
|
|
@ -86,13 +85,13 @@ class Configurator:
|
||||||
if self.modified:
|
if self.modified:
|
||||||
console.print("[yellow]* Unsaved changes[/yellow]")
|
console.print("[yellow]* Unsaved changes[/yellow]")
|
||||||
console.print()
|
console.print()
|
||||||
console.print("[white]10. Save[/white] [dim]Save config, stay in menu[/dim]")
|
console.print("[white] 9. Save[/white] [dim]Save config, stay in menu[/dim]")
|
||||||
console.print("[green]11. Save & Restart Bot[/green] [dim]Apply changes now[/dim]")
|
console.print("[green]10. Save & Restart Bot[/green] [dim]Apply changes now[/dim]")
|
||||||
console.print("[white]12. Save & Exit[/white] [dim]Save, restart bot, exit[/dim]")
|
console.print("[white]11. Save & Exit[/white] [dim]Save, restart bot, exit[/dim]")
|
||||||
console.print("[white]13. Exit without Saving[/white]")
|
console.print("[white]12. Exit without Saving[/white]")
|
||||||
console.print()
|
console.print()
|
||||||
|
|
||||||
choice = IntPrompt.ask("Select option", default=11)
|
choice = IntPrompt.ask("Select option", default=10)
|
||||||
|
|
||||||
if choice == 1:
|
if choice == 1:
|
||||||
self._bot_settings()
|
self._bot_settings()
|
||||||
|
|
@ -103,23 +102,21 @@ class Configurator:
|
||||||
elif choice == 4:
|
elif choice == 4:
|
||||||
self._response_settings()
|
self._response_settings()
|
||||||
elif choice == 5:
|
elif choice == 5:
|
||||||
self._channel_settings()
|
|
||||||
elif choice == 6:
|
|
||||||
self._history_settings()
|
self._history_settings()
|
||||||
elif choice == 7:
|
elif choice == 6:
|
||||||
self._command_settings()
|
self._command_settings()
|
||||||
elif choice == 8:
|
elif choice == 7:
|
||||||
self._weather_settings()
|
self._weather_settings()
|
||||||
elif choice == 9:
|
elif choice == 8:
|
||||||
self._setup_wizard()
|
self._setup_wizard()
|
||||||
elif choice == 10:
|
elif choice == 9:
|
||||||
self._save_only()
|
self._save_only()
|
||||||
elif choice == 11:
|
elif choice == 10:
|
||||||
self._save_and_restart()
|
self._save_and_restart()
|
||||||
elif choice == 12:
|
elif choice == 11:
|
||||||
self._save_restart_exit()
|
self._save_restart_exit()
|
||||||
break
|
break
|
||||||
elif choice == 13:
|
elif choice == 12:
|
||||||
break
|
break
|
||||||
|
|
||||||
def _show_header(self) -> None:
|
def _show_header(self) -> None:
|
||||||
|
|
@ -148,18 +145,13 @@ class Configurator:
|
||||||
table.add_column("Setting", style="white")
|
table.add_column("Setting", style="white")
|
||||||
table.add_column("Value", style="green")
|
table.add_column("Value", style="green")
|
||||||
|
|
||||||
table.add_row("1", "Bot Name (@mention)", self.config.bot.name)
|
table.add_row("1", "Bot Name", self.config.bot.name)
|
||||||
table.add_row("2", "Owner", self.config.bot.owner or "[dim]not set[/dim]")
|
table.add_row("2", "Owner", self.config.bot.owner or "[dim]not set[/dim]")
|
||||||
table.add_row(
|
table.add_row(
|
||||||
"3",
|
"3", "Respond to DMs", self._status_icon(self.config.bot.respond_to_dms)
|
||||||
"Respond to @mentions",
|
|
||||||
self._status_icon(self.config.bot.respond_to_mentions),
|
|
||||||
)
|
)
|
||||||
table.add_row(
|
table.add_row(
|
||||||
"4", "Respond to DMs", self._status_icon(self.config.bot.respond_to_dms)
|
"4", "Filter BBS Protocols", self._status_icon(self.config.bot.filter_bbs_protocols)
|
||||||
)
|
|
||||||
table.add_row(
|
|
||||||
"5", "Filter BBS Protocols", self._status_icon(self.config.bot.filter_bbs_protocols)
|
|
||||||
)
|
)
|
||||||
table.add_row("0", "Back", "")
|
table.add_row("0", "Back", "")
|
||||||
|
|
||||||
|
|
@ -181,18 +173,11 @@ class Configurator:
|
||||||
self.config.bot.owner = value
|
self.config.bot.owner = value
|
||||||
self.modified = True
|
self.modified = True
|
||||||
elif choice == 3:
|
elif choice == 3:
|
||||||
value = Confirm.ask(
|
|
||||||
"Respond to @mentions?", default=self.config.bot.respond_to_mentions
|
|
||||||
)
|
|
||||||
if value != self.config.bot.respond_to_mentions:
|
|
||||||
self.config.bot.respond_to_mentions = value
|
|
||||||
self.modified = True
|
|
||||||
elif choice == 4:
|
|
||||||
value = Confirm.ask("Respond to DMs?", default=self.config.bot.respond_to_dms)
|
value = Confirm.ask("Respond to DMs?", default=self.config.bot.respond_to_dms)
|
||||||
if value != self.config.bot.respond_to_dms:
|
if value != self.config.bot.respond_to_dms:
|
||||||
self.config.bot.respond_to_dms = value
|
self.config.bot.respond_to_dms = value
|
||||||
self.modified = True
|
self.modified = True
|
||||||
elif choice == 5:
|
elif choice == 4:
|
||||||
value = Confirm.ask("Filter BBS protocols?", default=self.config.bot.filter_bbs_protocols)
|
value = Confirm.ask("Filter BBS protocols?", default=self.config.bot.filter_bbs_protocols)
|
||||||
if value != self.config.bot.filter_bbs_protocols:
|
if value != self.config.bot.filter_bbs_protocols:
|
||||||
self.config.bot.filter_bbs_protocols = value
|
self.config.bot.filter_bbs_protocols = value
|
||||||
|
|
@ -478,49 +463,6 @@ class Configurator:
|
||||||
self.config.response.max_messages = value
|
self.config.response.max_messages = value
|
||||||
self.modified = True
|
self.modified = True
|
||||||
|
|
||||||
def _channel_settings(self) -> None:
|
|
||||||
"""Channel filtering settings submenu."""
|
|
||||||
while True:
|
|
||||||
self._clear()
|
|
||||||
console.print("[bold]Channel Filtering[/bold]\n")
|
|
||||||
|
|
||||||
table = Table(box=box.ROUNDED)
|
|
||||||
table.add_column("Option", style="cyan", width=4)
|
|
||||||
table.add_column("Setting", style="white")
|
|
||||||
table.add_column("Value", style="green")
|
|
||||||
|
|
||||||
whitelist_str = ", ".join(str(c) for c in self.config.channels.whitelist)
|
|
||||||
table.add_row("1", "Mode", self.config.channels.mode)
|
|
||||||
table.add_row("2", "Whitelist Channels", whitelist_str or "[dim]none[/dim]")
|
|
||||||
table.add_row("0", "Back", "")
|
|
||||||
|
|
||||||
console.print(table)
|
|
||||||
console.print()
|
|
||||||
|
|
||||||
choice = IntPrompt.ask("Select option", default=0)
|
|
||||||
|
|
||||||
if choice == 0:
|
|
||||||
return
|
|
||||||
elif choice == 1:
|
|
||||||
console.print("\n[cyan]1.[/cyan] all - Respond on all channels")
|
|
||||||
console.print("[cyan]2.[/cyan] whitelist - Only respond on specific channels")
|
|
||||||
sel = IntPrompt.ask("Select", default=1 if self.config.channels.mode == "all" else 2)
|
|
||||||
value = "all" if sel == 1 else "whitelist"
|
|
||||||
if value != self.config.channels.mode:
|
|
||||||
self.config.channels.mode = value
|
|
||||||
self.modified = True
|
|
||||||
elif choice == 2:
|
|
||||||
value = Prompt.ask(
|
|
||||||
"Whitelist (comma-separated)", default=whitelist_str
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
channels = [int(c.strip()) for c in value.split(",") if c.strip()]
|
|
||||||
if channels != self.config.channels.whitelist:
|
|
||||||
self.config.channels.whitelist = channels
|
|
||||||
self.modified = True
|
|
||||||
except ValueError:
|
|
||||||
console.print("[red]Invalid input. Use comma-separated numbers.[/red]")
|
|
||||||
|
|
||||||
def _history_settings(self) -> None:
|
def _history_settings(self) -> None:
|
||||||
"""History settings submenu."""
|
"""History settings submenu."""
|
||||||
while True:
|
while True:
|
||||||
|
|
@ -603,7 +545,7 @@ class Configurator:
|
||||||
|
|
||||||
# Step 1: Bot identity
|
# Step 1: Bot identity
|
||||||
console.print("[bold cyan]Step 1: Bot Identity[/bold cyan]")
|
console.print("[bold cyan]Step 1: Bot Identity[/bold cyan]")
|
||||||
self.config.bot.name = Prompt.ask("Bot name (for @mentions)", default="ai")
|
self.config.bot.name = Prompt.ask("Bot name", default="ai")
|
||||||
self.config.bot.owner = Prompt.ask("Your name/callsign", default="")
|
self.config.bot.owner = Prompt.ask("Your name/callsign", default="")
|
||||||
console.print()
|
console.print()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ class BotConfig:
|
||||||
|
|
||||||
name: str = "ai"
|
name: str = "ai"
|
||||||
owner: str = ""
|
owner: str = ""
|
||||||
respond_to_mentions: bool = True
|
|
||||||
respond_to_dms: bool = True
|
respond_to_dms: bool = True
|
||||||
filter_bbs_protocols: bool = True
|
filter_bbs_protocols: bool = True
|
||||||
|
|
||||||
|
|
@ -32,14 +31,6 @@ class ConnectionConfig:
|
||||||
tcp_port: int = 4403
|
tcp_port: int = 4403
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ChannelsConfig:
|
|
||||||
"""Channel filtering settings."""
|
|
||||||
|
|
||||||
mode: str = "all" # all or whitelist
|
|
||||||
whitelist: list[int] = field(default_factory=lambda: [0])
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ResponseConfig:
|
class ResponseConfig:
|
||||||
"""Response behavior settings."""
|
"""Response behavior settings."""
|
||||||
|
|
@ -134,7 +125,6 @@ class Config:
|
||||||
|
|
||||||
bot: BotConfig = field(default_factory=BotConfig)
|
bot: BotConfig = field(default_factory=BotConfig)
|
||||||
connection: ConnectionConfig = field(default_factory=ConnectionConfig)
|
connection: ConnectionConfig = field(default_factory=ConnectionConfig)
|
||||||
channels: ChannelsConfig = field(default_factory=ChannelsConfig)
|
|
||||||
response: ResponseConfig = field(default_factory=ResponseConfig)
|
response: ResponseConfig = field(default_factory=ResponseConfig)
|
||||||
history: HistoryConfig = field(default_factory=HistoryConfig)
|
history: HistoryConfig = field(default_factory=HistoryConfig)
|
||||||
memory: MemoryConfig = field(default_factory=MemoryConfig)
|
memory: MemoryConfig = field(default_factory=MemoryConfig)
|
||||||
|
|
|
||||||
|
|
@ -174,22 +174,12 @@ class MeshAI:
|
||||||
if not response:
|
if not response:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Send response
|
# Send DM response
|
||||||
if message.is_dm:
|
await self.responder.send_response(
|
||||||
await self.responder.send_response(
|
text=response,
|
||||||
text=response,
|
destination=message.sender_id,
|
||||||
destination=message.sender_id,
|
channel=message.channel,
|
||||||
channel=message.channel,
|
)
|
||||||
)
|
|
||||||
else:
|
|
||||||
formatted = self.responder.format_channel_response(
|
|
||||||
response, message.sender_name, mention_sender=True
|
|
||||||
)
|
|
||||||
await self.responder.send_response(
|
|
||||||
text=formatted,
|
|
||||||
destination=None,
|
|
||||||
channel=message.channel,
|
|
||||||
)
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error handling message: {e}", exc_info=True)
|
logger.error(f"Error handling message: {e}", exc_info=True)
|
||||||
|
|
|
||||||
|
|
@ -137,24 +137,3 @@ class Responder:
|
||||||
return pos
|
return pos
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def format_channel_response(
|
|
||||||
self, text: str, sender_name: str, mention_sender: bool = False
|
|
||||||
) -> str:
|
|
||||||
"""Format response for channel context.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
text: Response text
|
|
||||||
sender_name: Name of sender being replied to
|
|
||||||
mention_sender: Whether to prefix with sender's name
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Formatted response
|
|
||||||
"""
|
|
||||||
if mention_sender:
|
|
||||||
# Check if adding prefix would exceed max length
|
|
||||||
prefix = f"@{sender_name}: "
|
|
||||||
if len(prefix) + len(text) <= self.config.max_length * self.config.max_messages:
|
|
||||||
return prefix + text
|
|
||||||
|
|
||||||
return text
|
|
||||||
|
|
|
||||||
|
|
@ -68,13 +68,13 @@ class MessageRouter:
|
||||||
self.dispatcher = dispatcher
|
self.dispatcher = dispatcher
|
||||||
self.llm = llm_backend
|
self.llm = llm_backend
|
||||||
|
|
||||||
# Compile mention pattern
|
|
||||||
bot_name = re.escape(config.bot.name)
|
|
||||||
self._mention_pattern = re.compile(rf"@{bot_name}\b", re.IGNORECASE)
|
|
||||||
|
|
||||||
def should_respond(self, message: MeshMessage) -> bool:
|
def should_respond(self, message: MeshMessage) -> bool:
|
||||||
"""Determine if we should respond to this message.
|
"""Determine if we should respond to this message.
|
||||||
|
|
||||||
|
DM-only bot: ignores all public channel messages.
|
||||||
|
Commands and conversational LLM responses both work in DMs.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
message: Incoming message
|
message: Incoming message
|
||||||
|
|
||||||
|
|
@ -85,35 +85,20 @@ class MessageRouter:
|
||||||
if message.sender_id == self.connector.my_node_id:
|
if message.sender_id == self.connector.my_node_id:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# Only respond to DMs
|
||||||
|
if not message.is_dm:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not self.config.bot.respond_to_dms:
|
||||||
|
return False
|
||||||
|
|
||||||
# Ignore advBBS protocol and notification messages
|
# Ignore advBBS protocol and notification messages
|
||||||
if self.config.bot.filter_bbs_protocols:
|
if self.config.bot.filter_bbs_protocols:
|
||||||
if any(message.text.startswith(p) for p in ADVBBS_PREFIXES):
|
if any(message.text.startswith(p) for p in ADVBBS_PREFIXES):
|
||||||
logger.debug(f"Ignoring advBBS message from {message.sender_id}: {message.text[:40]}...")
|
logger.debug(f"Ignoring advBBS message from {message.sender_id}: {message.text[:40]}...")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Check if DM — conversational mode only, skip !commands
|
return True
|
||||||
# (let MeshMonitor or other bots handle bang commands in DMs)
|
|
||||||
if message.is_dm:
|
|
||||||
if self.dispatcher.is_command(message.text):
|
|
||||||
return False
|
|
||||||
return self.config.bot.respond_to_dms
|
|
||||||
|
|
||||||
# Check channel filtering
|
|
||||||
if self.config.channels.mode == "whitelist":
|
|
||||||
if message.channel not in self.config.channels.whitelist:
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Check for @mention
|
|
||||||
if self.config.bot.respond_to_mentions:
|
|
||||||
if self._mention_pattern.search(message.text):
|
|
||||||
return True
|
|
||||||
|
|
||||||
# Check for bang command (always respond to commands)
|
|
||||||
if self.dispatcher.is_command(message.text):
|
|
||||||
return True
|
|
||||||
|
|
||||||
# Not a DM, no mention, no command - ignore
|
|
||||||
return False
|
|
||||||
|
|
||||||
async def route(self, message: MeshMessage) -> RouteResult:
|
async def route(self, message: MeshMessage) -> RouteResult:
|
||||||
"""Route a message and generate response.
|
"""Route a message and generate response.
|
||||||
|
|
@ -200,11 +185,8 @@ class MessageRouter:
|
||||||
logger.debug(f"Persisted summary for {user_id}")
|
logger.debug(f"Persisted summary for {user_id}")
|
||||||
|
|
||||||
def _clean_query(self, text: str) -> str:
|
def _clean_query(self, text: str) -> str:
|
||||||
"""Remove @mention and check for prompt injection."""
|
"""Clean up query text and check for prompt injection."""
|
||||||
# Remove @botname mention
|
cleaned = " ".join(text.split())
|
||||||
cleaned = self._mention_pattern.sub("", text)
|
|
||||||
# Clean up extra whitespace
|
|
||||||
cleaned = " ".join(cleaned.split())
|
|
||||||
cleaned = cleaned.strip()
|
cleaned = cleaned.strip()
|
||||||
|
|
||||||
# Check for prompt injection
|
# Check for prompt injection
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue