feat(matrixbot): stop auto-joining invites, restrict commands to two rooms
#general is now published to the public room directory, so the bot is reachable by strangers. Two hardening changes: - Invites are only accepted from INVITE_ALLOWED_USERS (defaults to ADMIN_USERS). Anything else is declined via room_leave so hostile invites do not accumulate as pending. Previously any invite from anyone was auto-joined. - Commands and the passive scramble/riddle answer checks only run in COMMAND_ROOMS (#commands and #management). The gate sits early in Callbacks.message, so the bot stays silent everywhere else, including #general. Set COMMAND_ROOMS="*" to restore the old behaviour. Reaction handlers are deliberately left ungated so the welcome flow keeps working; game votes elsewhere cannot match an active game. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+14
-5
@@ -21,6 +21,7 @@ from config import (
|
|||||||
MATRIX_ACCESS_TOKEN,
|
MATRIX_ACCESS_TOKEN,
|
||||||
MATRIX_DEVICE_ID,
|
MATRIX_DEVICE_ID,
|
||||||
MATRIX_PASSWORD,
|
MATRIX_PASSWORD,
|
||||||
|
INVITE_ALLOWED_USERS,
|
||||||
LOG_LEVEL,
|
LOG_LEVEL,
|
||||||
ConfigValidator,
|
ConfigValidator,
|
||||||
)
|
)
|
||||||
@@ -148,13 +149,21 @@ async def main():
|
|||||||
client.add_event_callback(callbacks.unknown_event, UnknownEvent)
|
client.add_event_callback(callbacks.unknown_event, UnknownEvent)
|
||||||
client.add_event_callback(callbacks.member, RoomMemberEvent)
|
client.add_event_callback(callbacks.member, RoomMemberEvent)
|
||||||
|
|
||||||
# Auto-accept room invites
|
# Accept invites only from trusted users, and decline the rest so they do
|
||||||
async def _auto_accept_invite(room, event):
|
# not sit pending forever. Never auto-join on an arbitrary invite.
|
||||||
if event.membership == "invite" and event.state_key == MATRIX_USER_ID:
|
async def _handle_invite(room, event):
|
||||||
logger.info("Auto-accepting invite to %s", room.room_id)
|
if event.membership != "invite" or event.state_key != MATRIX_USER_ID:
|
||||||
|
return
|
||||||
|
if event.sender not in INVITE_ALLOWED_USERS:
|
||||||
|
logger.warning(
|
||||||
|
"Declining invite to %s from untrusted sender %s", room.room_id, event.sender
|
||||||
|
)
|
||||||
|
await client.room_leave(room.room_id)
|
||||||
|
return
|
||||||
|
logger.info("Accepting invite to %s from %s", room.room_id, event.sender)
|
||||||
await client.join(room.room_id)
|
await client.join(room.room_id)
|
||||||
|
|
||||||
client.add_event_callback(_auto_accept_invite, InviteMemberEvent)
|
client.add_event_callback(_handle_invite, InviteMemberEvent)
|
||||||
|
|
||||||
# Graceful shutdown
|
# Graceful shutdown
|
||||||
loop = asyncio.get_running_loop()
|
loop = asyncio.get_running_loop()
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from functools import wraps
|
|||||||
|
|
||||||
from nio import AsyncClient
|
from nio import AsyncClient
|
||||||
|
|
||||||
from config import BOT_PREFIX, MATRIX_USER_ID
|
from config import BOT_PREFIX, COMMAND_ROOMS, MATRIX_USER_ID
|
||||||
from commands import (
|
from commands import (
|
||||||
COMMANDS,
|
COMMANDS,
|
||||||
metrics,
|
metrics,
|
||||||
@@ -50,6 +50,12 @@ class Callbacks:
|
|||||||
if event.sender == MATRIX_USER_ID:
|
if event.sender == MATRIX_USER_ID:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Only act in designated command rooms. This covers both prefixed
|
||||||
|
# commands and the passive game-answer checks below, so the bot never
|
||||||
|
# speaks in public rooms such as #general. "*" allows every room.
|
||||||
|
if "*" not in COMMAND_ROOMS and room.room_id not in COMMAND_ROOMS:
|
||||||
|
return
|
||||||
|
|
||||||
body = event.body.strip() if event.body else ""
|
body = event.body.strip() if event.body else ""
|
||||||
|
|
||||||
# Check active non-command games that monitor all room messages
|
# Check active non-command games that monitor all room messages
|
||||||
|
|||||||
+17
-1
@@ -16,12 +16,28 @@ BOT_PREFIX = os.getenv("BOT_PREFIX", "!")
|
|||||||
ADMIN_USERS = [u.strip() for u in os.getenv("ADMIN_USERS", "").split(",") if u.strip()]
|
ADMIN_USERS = [u.strip() for u in os.getenv("ADMIN_USERS", "").split(",") if u.strip()]
|
||||||
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")
|
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")
|
||||||
|
|
||||||
|
# Rooms where commands and interactive games are allowed. The bot stays silent
|
||||||
|
# everywhere else, so it never responds in public rooms like #general.
|
||||||
|
# Override with a comma-separated COMMAND_ROOMS; set to "*" to allow all rooms.
|
||||||
|
DEFAULT_COMMAND_ROOMS = (
|
||||||
|
"!ou56mVZQ8ZB7AhDYPmBV5_BR28WMZ4x5zwZkPCqjq1s", # #commands
|
||||||
|
"!mEvR5fe3jMmzwd-FwNygD72OY_yu8H3UP_N-57oK7MI", # #management
|
||||||
|
)
|
||||||
|
_command_rooms = os.getenv("COMMAND_ROOMS", ",".join(DEFAULT_COMMAND_ROOMS))
|
||||||
|
COMMAND_ROOMS = [r.strip() for r in _command_rooms.split(",") if r.strip()]
|
||||||
|
|
||||||
|
# Users whose invites the bot will accept. Invites from anyone else are
|
||||||
|
# declined: the homeserver is publicly listed, so auto-joining any invite lets
|
||||||
|
# a stranger pull the bot into an arbitrary room. Defaults to ADMIN_USERS.
|
||||||
|
_invite_allowed = os.getenv("INVITE_ALLOWED_USERS", "")
|
||||||
|
INVITE_ALLOWED_USERS = [u.strip() for u in _invite_allowed.split(",") if u.strip()] or list(ADMIN_USERS)
|
||||||
|
|
||||||
# Integrations
|
# Integrations
|
||||||
OLLAMA_URL = os.getenv("OLLAMA_URL", "http://10.10.10.157:11434")
|
OLLAMA_URL = os.getenv("OLLAMA_URL", "http://10.10.10.157:11434")
|
||||||
OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "phi4-mini:latest")
|
OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "phi4-mini:latest")
|
||||||
CREATIVE_MODEL = os.getenv("CREATIVE_MODEL", "huihui_ai/llama3.2-abliterate:3b")
|
CREATIVE_MODEL = os.getenv("CREATIVE_MODEL", "huihui_ai/llama3.2-abliterate:3b")
|
||||||
ASK_MODEL = os.getenv("ASK_MODEL", "phi4-mini:latest")
|
ASK_MODEL = os.getenv("ASK_MODEL", "phi4-mini:latest")
|
||||||
MINECRAFT_RCON_HOST = os.getenv("MINECRAFT_RCON_HOST", "10.10.10.67")
|
MINECRAFT_RCON_HOST = os.getenv("MINECRAFT_RCON_HOST", "10.10.10.68")
|
||||||
MINECRAFT_RCON_PORT = int(os.getenv("MINECRAFT_RCON_PORT", "25575"))
|
MINECRAFT_RCON_PORT = int(os.getenv("MINECRAFT_RCON_PORT", "25575"))
|
||||||
MINECRAFT_RCON_PASSWORD = os.getenv("MINECRAFT_RCON_PASSWORD", "")
|
MINECRAFT_RCON_PASSWORD = os.getenv("MINECRAFT_RCON_PASSWORD", "")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user