formatting
This commit is contained in:
+637
-282
File diff suppressed because it is too large
Load Diff
@@ -1,2 +1,15 @@
|
|||||||
# Helper modules for community bot
|
# Helper modules for community bot
|
||||||
from . import message_utils, room_utils, user_utils, database_utils, report_utils, decorators, common_utils, room_creation_utils, config_manager, response_builder, diagnostic_utils, base_command_handler
|
from . import (
|
||||||
|
message_utils,
|
||||||
|
room_utils,
|
||||||
|
user_utils,
|
||||||
|
database_utils,
|
||||||
|
report_utils,
|
||||||
|
decorators,
|
||||||
|
common_utils,
|
||||||
|
room_creation_utils,
|
||||||
|
config_manager,
|
||||||
|
response_builder,
|
||||||
|
diagnostic_utils,
|
||||||
|
base_command_handler,
|
||||||
|
)
|
||||||
|
|||||||
@@ -36,7 +36,9 @@ class BaseCommandHandler(ABC):
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
async def check_permissions(self, evt: MessageEvent, min_level: int = 50, room_id: str = None) -> bool:
|
async def check_permissions(
|
||||||
|
self, evt: MessageEvent, min_level: int = 50, room_id: str = None
|
||||||
|
) -> bool:
|
||||||
"""Check if user has required permissions.
|
"""Check if user has required permissions.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -78,7 +80,9 @@ class BaseCommandHandler(ABC):
|
|||||||
"""
|
"""
|
||||||
await evt.reply(message)
|
await evt.reply(message)
|
||||||
|
|
||||||
async def respond_html(self, evt: MessageEvent, message: str, edits: Optional[MessageEvent] = None) -> None:
|
async def respond_html(
|
||||||
|
self, evt: MessageEvent, message: str, edits: Optional[MessageEvent] = None
|
||||||
|
) -> None:
|
||||||
"""Respond with HTML content.
|
"""Respond with HTML content.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -174,7 +178,9 @@ class ModeratorCommandHandler(BaseCommandHandler):
|
|||||||
return await self.execute_moderator_command(evt, *args, **kwargs)
|
return await self.execute_moderator_command(evt, *args, **kwargs)
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def execute_moderator_command(self, evt: MessageEvent, *args, **kwargs) -> Any:
|
async def execute_moderator_command(
|
||||||
|
self, evt: MessageEvent, *args, **kwargs
|
||||||
|
) -> Any:
|
||||||
"""Execute the moderator command logic.
|
"""Execute the moderator command logic.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -225,7 +231,9 @@ class SpaceModeratorCommandHandler(SpaceCommandHandler, ModeratorCommandHandler)
|
|||||||
return await self.execute_space_moderator_command(evt, *args, **kwargs)
|
return await self.execute_space_moderator_command(evt, *args, **kwargs)
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def execute_space_moderator_command(self, evt: MessageEvent, *args, **kwargs) -> Any:
|
async def execute_space_moderator_command(
|
||||||
|
self, evt: MessageEvent, *args, **kwargs
|
||||||
|
) -> Any:
|
||||||
"""Execute the space moderator command logic.
|
"""Execute the space moderator command logic.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -252,7 +260,9 @@ class SpaceAdminCommandHandler(SpaceCommandHandler, AdminCommandHandler):
|
|||||||
return await self.execute_space_admin_command(evt, *args, **kwargs)
|
return await self.execute_space_admin_command(evt, *args, **kwargs)
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def execute_space_admin_command(self, evt: MessageEvent, *args, **kwargs) -> Any:
|
async def execute_space_admin_command(
|
||||||
|
self, evt: MessageEvent, *args, **kwargs
|
||||||
|
) -> Any:
|
||||||
"""Execute the space admin command logic.
|
"""Execute the space admin command logic.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
|||||||
@@ -179,7 +179,6 @@ class ConfigManager:
|
|||||||
"""
|
"""
|
||||||
return self.config.get("verification_timeout", 300)
|
return self.config.get("verification_timeout", 300)
|
||||||
|
|
||||||
|
|
||||||
def get_banlist_rooms(self) -> List[str]:
|
def get_banlist_rooms(self) -> List[str]:
|
||||||
"""Get the list of banlist rooms.
|
"""Get the list of banlist rooms.
|
||||||
|
|
||||||
@@ -202,11 +201,7 @@ class ConfigManager:
|
|||||||
Returns:
|
Returns:
|
||||||
List[str]: List of missing required configuration keys
|
List[str]: List of missing required configuration keys
|
||||||
"""
|
"""
|
||||||
required_configs = [
|
required_configs = ["parent_room", "room_version", "community_slug"]
|
||||||
"parent_room",
|
|
||||||
"room_version",
|
|
||||||
"community_slug"
|
|
||||||
]
|
|
||||||
|
|
||||||
missing = []
|
missing = []
|
||||||
for config_key in required_configs:
|
for config_key in required_configs:
|
||||||
@@ -239,7 +234,7 @@ class ConfigManager:
|
|||||||
"invitees": self.get_invitees(),
|
"invitees": self.get_invitees(),
|
||||||
"invite_power_level": self.get_invite_power_level(),
|
"invite_power_level": self.get_invite_power_level(),
|
||||||
"encrypt": self.is_encryption_enabled(),
|
"encrypt": self.is_encryption_enabled(),
|
||||||
"parent_room": self.get_parent_room()
|
"parent_room": self.get_parent_room(),
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_tracking_settings(self) -> Dict[str, Any]:
|
def get_tracking_settings(self) -> Dict[str, Any]:
|
||||||
@@ -253,7 +248,7 @@ class ConfigManager:
|
|||||||
"track_messages": self.is_message_tracking_enabled(),
|
"track_messages": self.is_message_tracking_enabled(),
|
||||||
"track_reactions": self.is_reaction_tracking_enabled(),
|
"track_reactions": self.is_reaction_tracking_enabled(),
|
||||||
"warn_threshold_days": self.get_warn_threshold_days(),
|
"warn_threshold_days": self.get_warn_threshold_days(),
|
||||||
"kick_threshold_days": self.get_kick_threshold_days()
|
"kick_threshold_days": self.get_kick_threshold_days(),
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_verification_settings(self) -> Dict[str, Any]:
|
def get_verification_settings(self) -> Dict[str, Any]:
|
||||||
@@ -266,5 +261,5 @@ class ConfigManager:
|
|||||||
"verification_enabled": self.is_verification_enabled(),
|
"verification_enabled": self.is_verification_enabled(),
|
||||||
"verification_phrase": self.get_verification_phrase(),
|
"verification_phrase": self.get_verification_phrase(),
|
||||||
"verification_attempts": self.get_verification_attempts(),
|
"verification_attempts": self.get_verification_attempts(),
|
||||||
"verification_timeout": self.get_verification_timeout()
|
"verification_timeout": self.get_verification_timeout(),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,9 @@ async def get_messages_to_redact(client, room_id: str, mxid: str, logger) -> Lis
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
async def redact_messages(client, database, room_id: str, sleep_time: float, logger) -> Dict[str, int]:
|
async def redact_messages(
|
||||||
|
client, database, room_id: str, sleep_time: float, logger
|
||||||
|
) -> Dict[str, int]:
|
||||||
"""Redact messages queued for redaction in a room.
|
"""Redact messages queued for redaction in a room.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -59,9 +61,7 @@ async def redact_messages(client, database, room_id: str, sleep_time: float, log
|
|||||||
)
|
)
|
||||||
for event in events:
|
for event in events:
|
||||||
try:
|
try:
|
||||||
await client.redact(
|
await client.redact(room_id, event["event_id"], reason="content removed")
|
||||||
room_id, event["event_id"], reason="content removed"
|
|
||||||
)
|
|
||||||
counters["success"] += 1
|
counters["success"] += 1
|
||||||
await database.execute(
|
await database.execute(
|
||||||
"DELETE FROM redaction_tasks WHERE event_id = $1", event["event_id"]
|
"DELETE FROM redaction_tasks WHERE event_id = $1", event["event_id"]
|
||||||
@@ -103,8 +103,9 @@ async def upsert_user_timestamp(database, mxid: str, timestamp: int, logger) ->
|
|||||||
logger.error(f"Failed to upsert user timestamp: {e}")
|
logger.error(f"Failed to upsert user timestamp: {e}")
|
||||||
|
|
||||||
|
|
||||||
async def get_inactive_users(database, warn_threshold_days: int, kick_threshold_days: int,
|
async def get_inactive_users(
|
||||||
logger) -> Dict[str, List[str]]:
|
database, warn_threshold_days: int, kick_threshold_days: int, logger
|
||||||
|
) -> Dict[str, List[str]]:
|
||||||
"""Get lists of users who should be warned or kicked for inactivity.
|
"""Get lists of users who should be warned or kicked for inactivity.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -145,7 +146,7 @@ async def get_inactive_users(database, warn_threshold_days: int, kick_threshold_
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
"warn": [row["mxid"] for row in warn_results],
|
"warn": [row["mxid"] for row in warn_results],
|
||||||
"kick": [row["mxid"] for row in kick_results]
|
"kick": [row["mxid"] for row in kick_results],
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to get inactive users: {e}")
|
logger.error(f"Failed to get inactive users: {e}")
|
||||||
@@ -182,17 +183,22 @@ async def get_verification_state(database, dm_room_id: str) -> Dict[str, Any]:
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
result = await database.fetchrow(
|
result = await database.fetchrow(
|
||||||
"SELECT * FROM verification_states WHERE dm_room_id = $1",
|
"SELECT * FROM verification_states WHERE dm_room_id = $1", dm_room_id
|
||||||
dm_room_id
|
|
||||||
)
|
)
|
||||||
return dict(result) if result else None
|
return dict(result) if result else None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
async def create_verification_state(database, dm_room_id: str, user_id: str,
|
async def create_verification_state(
|
||||||
target_room_id: str, verification_phrase: str,
|
database,
|
||||||
attempts_remaining: int, required_power_level: int) -> None:
|
dm_room_id: str,
|
||||||
|
user_id: str,
|
||||||
|
target_room_id: str,
|
||||||
|
verification_phrase: str,
|
||||||
|
attempts_remaining: int,
|
||||||
|
required_power_level: int,
|
||||||
|
) -> None:
|
||||||
"""Create a new verification state.
|
"""Create a new verification state.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -211,14 +217,20 @@ async def create_verification_state(database, dm_room_id: str, user_id: str,
|
|||||||
(dm_room_id, user_id, target_room_id, verification_phrase, attempts_remaining, required_power_level)
|
(dm_room_id, user_id, target_room_id, verification_phrase, attempts_remaining, required_power_level)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6)
|
VALUES ($1, $2, $3, $4, $5, $6)
|
||||||
""",
|
""",
|
||||||
dm_room_id, user_id, target_room_id, verification_phrase,
|
dm_room_id,
|
||||||
attempts_remaining, required_power_level
|
user_id,
|
||||||
|
target_room_id,
|
||||||
|
verification_phrase,
|
||||||
|
attempts_remaining,
|
||||||
|
required_power_level,
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
pass # Verification state creation is not critical
|
pass # Verification state creation is not critical
|
||||||
|
|
||||||
|
|
||||||
async def update_verification_attempts(database, dm_room_id: str, attempts_remaining: int) -> None:
|
async def update_verification_attempts(
|
||||||
|
database, dm_room_id: str, attempts_remaining: int
|
||||||
|
) -> None:
|
||||||
"""Update verification attempts remaining.
|
"""Update verification attempts remaining.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -229,7 +241,8 @@ async def update_verification_attempts(database, dm_room_id: str, attempts_remai
|
|||||||
try:
|
try:
|
||||||
await database.execute(
|
await database.execute(
|
||||||
"UPDATE verification_states SET attempts_remaining = $1 WHERE dm_room_id = $2",
|
"UPDATE verification_states SET attempts_remaining = $1 WHERE dm_room_id = $2",
|
||||||
attempts_remaining, dm_room_id
|
attempts_remaining,
|
||||||
|
dm_room_id,
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
pass # Verification state update is not critical
|
pass # Verification state update is not critical
|
||||||
@@ -244,8 +257,7 @@ async def delete_verification_state(database, dm_room_id: str) -> None:
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
await database.execute(
|
await database.execute(
|
||||||
"DELETE FROM verification_states WHERE dm_room_id = $1",
|
"DELETE FROM verification_states WHERE dm_room_id = $1", dm_room_id
|
||||||
dm_room_id
|
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
pass # Verification state deletion is not critical
|
pass # Verification state deletion is not critical
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ def require_permission(min_level: int = 50, room_id: Optional[str] = None):
|
|||||||
min_level: Minimum required power level (default 50 for moderator)
|
min_level: Minimum required power level (default 50 for moderator)
|
||||||
room_id: Room ID to check permissions in (None for parent room)
|
room_id: Room ID to check permissions in (None for parent room)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def decorator(func: Callable) -> Callable:
|
def decorator(func: Callable) -> Callable:
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
async def wrapper(self, evt: MessageEvent, *args, **kwargs) -> Any:
|
async def wrapper(self, evt: MessageEvent, *args, **kwargs) -> Any:
|
||||||
@@ -19,17 +20,21 @@ def require_permission(min_level: int = 50, room_id: Optional[str] = None):
|
|||||||
await evt.reply("You don't have permission to use this command")
|
await evt.reply("You don't have permission to use this command")
|
||||||
return
|
return
|
||||||
return await func(self, evt, *args, **kwargs)
|
return await func(self, evt, *args, **kwargs)
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
def require_parent_room(func: Callable) -> Callable:
|
def require_parent_room(func: Callable) -> Callable:
|
||||||
"""Decorator to require parent room to be configured."""
|
"""Decorator to require parent room to be configured."""
|
||||||
|
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
async def wrapper(self, evt: MessageEvent, *args, **kwargs) -> Any:
|
async def wrapper(self, evt: MessageEvent, *args, **kwargs) -> Any:
|
||||||
if not await self.check_parent_room(evt):
|
if not await self.check_parent_room(evt):
|
||||||
return
|
return
|
||||||
return await func(self, evt, *args, **kwargs)
|
return await func(self, evt, *args, **kwargs)
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
@@ -39,6 +44,7 @@ def handle_errors(error_message: str = "An error occurred"):
|
|||||||
Args:
|
Args:
|
||||||
error_message: Default error message to show to user
|
error_message: Default error message to show to user
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def decorator(func: Callable) -> Callable:
|
def decorator(func: Callable) -> Callable:
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
async def wrapper(self, evt: MessageEvent, *args, **kwargs) -> Any:
|
async def wrapper(self, evt: MessageEvent, *args, **kwargs) -> Any:
|
||||||
@@ -47,5 +53,7 @@ def handle_errors(error_message: str = "An error occurred"):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.log.error(f"Error in {func.__name__}: {e}")
|
self.log.error(f"Error in {func.__name__}: {e}")
|
||||||
await evt.reply(f"{error_message}: {e}")
|
await evt.reply(f"{error_message}: {e}")
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|||||||
@@ -6,9 +6,7 @@ from mautrix.client import Client
|
|||||||
|
|
||||||
|
|
||||||
async def check_space_permissions(
|
async def check_space_permissions(
|
||||||
client: Client,
|
client: Client, parent_room: str, logger
|
||||||
parent_room: str,
|
|
||||||
logger
|
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""Check bot permissions in the parent space.
|
"""Check bot permissions in the parent space.
|
||||||
|
|
||||||
@@ -28,7 +26,10 @@ async def check_space_permissions(
|
|||||||
|
|
||||||
# Check if bot has unlimited power (creator in modern room versions)
|
# Check if bot has unlimited power (creator in modern room versions)
|
||||||
from .room_utils import user_has_unlimited_power
|
from .room_utils import user_has_unlimited_power
|
||||||
bot_has_unlimited_power = await user_has_unlimited_power(client, client.mxid, parent_room)
|
|
||||||
|
bot_has_unlimited_power = await user_has_unlimited_power(
|
||||||
|
client, client.mxid, parent_room
|
||||||
|
)
|
||||||
|
|
||||||
space_info = {
|
space_info = {
|
||||||
"room_id": parent_room,
|
"room_id": parent_room,
|
||||||
@@ -37,40 +38,28 @@ async def check_space_permissions(
|
|||||||
"bot_has_unlimited_power": bot_has_unlimited_power,
|
"bot_has_unlimited_power": bot_has_unlimited_power,
|
||||||
"users_higher_or_equal": [],
|
"users_higher_or_equal": [],
|
||||||
"users_equal": [],
|
"users_equal": [],
|
||||||
"users_higher": []
|
"users_higher": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
# Check for users with equal or higher power level
|
# Check for users with equal or higher power level
|
||||||
for user, level in space_power_levels.users.items():
|
for user, level in space_power_levels.users.items():
|
||||||
if user != client.mxid and level >= bot_level:
|
if user != client.mxid and level >= bot_level:
|
||||||
if level == bot_level:
|
if level == bot_level:
|
||||||
space_info["users_equal"].append({
|
space_info["users_equal"].append({"user": user, "level": level})
|
||||||
"user": user,
|
|
||||||
"level": level
|
|
||||||
})
|
|
||||||
else:
|
else:
|
||||||
space_info["users_higher"].append({
|
space_info["users_higher"].append({"user": user, "level": level})
|
||||||
"user": user,
|
space_info["users_higher_or_equal"].append(
|
||||||
"level": level
|
{"user": user, "level": level}
|
||||||
})
|
)
|
||||||
space_info["users_higher_or_equal"].append({
|
|
||||||
"user": user,
|
|
||||||
"level": level
|
|
||||||
})
|
|
||||||
|
|
||||||
return space_info
|
return space_info
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to check space permissions: {e}")
|
logger.error(f"Failed to check space permissions: {e}")
|
||||||
return {
|
return {"room_id": parent_room, "error": str(e)}
|
||||||
"room_id": parent_room,
|
|
||||||
"error": str(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async def check_room_permissions(
|
async def check_room_permissions(
|
||||||
client: Client,
|
client: Client, room_id: str, logger
|
||||||
room_id: str,
|
|
||||||
logger
|
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""Check bot permissions in a specific room.
|
"""Check bot permissions in a specific room.
|
||||||
|
|
||||||
@@ -87,10 +76,7 @@ async def check_room_permissions(
|
|||||||
try:
|
try:
|
||||||
await client.get_state_event(room_id, EventType.ROOM_MEMBER, client.mxid)
|
await client.get_state_event(room_id, EventType.ROOM_MEMBER, client.mxid)
|
||||||
except:
|
except:
|
||||||
return {
|
return {"room_id": room_id, "error": "Bot not in room"}
|
||||||
"room_id": room_id,
|
|
||||||
"error": "Bot not in room"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Get power levels
|
# Get power levels
|
||||||
room_power_levels = await client.get_state_event(
|
room_power_levels = await client.get_state_event(
|
||||||
@@ -102,17 +88,24 @@ async def check_room_permissions(
|
|||||||
room_name = room_id
|
room_name = room_id
|
||||||
try:
|
try:
|
||||||
from .common_utils import get_room_name
|
from .common_utils import get_room_name
|
||||||
|
|
||||||
room_name = await get_room_name(client, room_id, logger) or room_id
|
room_name = await get_room_name(client, room_id, logger) or room_id
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Get room version and creators
|
# Get room version and creators
|
||||||
from .room_utils import get_room_version_and_creators
|
from .room_utils import get_room_version_and_creators
|
||||||
room_version, creators = await get_room_version_and_creators(client, room_id, logger)
|
|
||||||
|
room_version, creators = await get_room_version_and_creators(
|
||||||
|
client, room_id, logger
|
||||||
|
)
|
||||||
|
|
||||||
# Check if bot has unlimited power (creator in modern room versions)
|
# Check if bot has unlimited power (creator in modern room versions)
|
||||||
from .room_utils import user_has_unlimited_power
|
from .room_utils import user_has_unlimited_power
|
||||||
bot_has_unlimited_power = await user_has_unlimited_power(client, client.mxid, room_id)
|
|
||||||
|
bot_has_unlimited_power = await user_has_unlimited_power(
|
||||||
|
client, client.mxid, room_id
|
||||||
|
)
|
||||||
|
|
||||||
room_report = {
|
room_report = {
|
||||||
"room_id": room_id,
|
"room_id": room_id,
|
||||||
@@ -124,39 +117,28 @@ async def check_room_permissions(
|
|||||||
"bot_has_unlimited_power": bot_has_unlimited_power,
|
"bot_has_unlimited_power": bot_has_unlimited_power,
|
||||||
"users_higher_or_equal": [],
|
"users_higher_or_equal": [],
|
||||||
"users_equal": [],
|
"users_equal": [],
|
||||||
"users_higher": []
|
"users_higher": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
# Check for users with equal or higher power level
|
# Check for users with equal or higher power level
|
||||||
for user, level in room_power_levels.users.items():
|
for user, level in room_power_levels.users.items():
|
||||||
if user != client.mxid and level >= bot_level:
|
if user != client.mxid and level >= bot_level:
|
||||||
if level == bot_level:
|
if level == bot_level:
|
||||||
room_report["users_equal"].append({
|
room_report["users_equal"].append({"user": user, "level": level})
|
||||||
"user": user,
|
|
||||||
"level": level
|
|
||||||
})
|
|
||||||
else:
|
else:
|
||||||
room_report["users_higher"].append({
|
room_report["users_higher"].append({"user": user, "level": level})
|
||||||
"user": user,
|
room_report["users_higher_or_equal"].append(
|
||||||
"level": level
|
{"user": user, "level": level}
|
||||||
})
|
)
|
||||||
room_report["users_higher_or_equal"].append({
|
|
||||||
"user": user,
|
|
||||||
"level": level
|
|
||||||
})
|
|
||||||
|
|
||||||
return room_report
|
return room_report
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to check room permissions for {room_id}: {e}")
|
logger.error(f"Failed to check room permissions for {room_id}: {e}")
|
||||||
return {
|
return {"room_id": room_id, "error": str(e)}
|
||||||
"room_id": room_id,
|
|
||||||
"error": str(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def analyze_room_data(
|
def analyze_room_data(
|
||||||
room_data: Dict[str, Any],
|
room_data: Dict[str, Any], is_modern_room_version_func
|
||||||
is_modern_room_version_func
|
|
||||||
) -> Tuple[str, str, bool, bool, bool]:
|
) -> Tuple[str, str, bool, bool, bool]:
|
||||||
"""Analyze room data to determine status and categorization.
|
"""Analyze room data to determine status and categorization.
|
||||||
|
|
||||||
@@ -185,9 +167,7 @@ def analyze_room_data(
|
|||||||
return "no_admin", "problematic", False, is_modern, False
|
return "no_admin", "problematic", False, is_modern, False
|
||||||
|
|
||||||
|
|
||||||
def generate_space_summary(
|
def generate_space_summary(space_data: Dict[str, Any]) -> str:
|
||||||
space_data: Dict[str, Any]
|
|
||||||
) -> str:
|
|
||||||
"""Generate HTML summary for space permissions.
|
"""Generate HTML summary for space permissions.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -218,8 +198,7 @@ def generate_space_summary(
|
|||||||
|
|
||||||
|
|
||||||
def generate_room_summary(
|
def generate_room_summary(
|
||||||
rooms_data: Dict[str, Any],
|
rooms_data: Dict[str, Any], is_modern_room_version_func
|
||||||
is_modern_room_version_func
|
|
||||||
) -> Tuple[str, Dict[str, int]]:
|
) -> Tuple[str, Dict[str, int]]:
|
||||||
"""Generate HTML summary for room permissions.
|
"""Generate HTML summary for room permissions.
|
||||||
|
|
||||||
@@ -237,7 +216,7 @@ def generate_room_summary(
|
|||||||
"error_rooms": 0,
|
"error_rooms": 0,
|
||||||
"not_in_room_count": 0,
|
"not_in_room_count": 0,
|
||||||
"modern_rooms": 0,
|
"modern_rooms": 0,
|
||||||
"legacy_rooms": 0
|
"legacy_rooms": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
for room_id, room_data in rooms_data.items():
|
for room_id, room_data in rooms_data.items():
|
||||||
@@ -262,12 +241,18 @@ def generate_room_summary(
|
|||||||
stats["legacy_rooms"] += 1
|
stats["legacy_rooms"] += 1
|
||||||
|
|
||||||
# Generate room info for problematic rooms
|
# Generate room info for problematic rooms
|
||||||
if category in ["error", "problematic"] or (is_admin and (room_data.get("users_higher") or room_data.get("users_equal"))):
|
if category in ["error", "problematic"] or (
|
||||||
|
is_admin and (room_data.get("users_higher") or room_data.get("users_equal"))
|
||||||
|
):
|
||||||
if has_error:
|
if has_error:
|
||||||
if room_data["error"] == "Bot not in room":
|
if room_data["error"] == "Bot not in room":
|
||||||
problematic_rooms.append(f"❌ <b>{room_data.get('room_name', room_id)}</b> ({room_id}): Bot not in room")
|
problematic_rooms.append(
|
||||||
|
f"❌ <b>{room_data.get('room_name', room_id)}</b> ({room_id}): Bot not in room"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
problematic_rooms.append(f"❌ <b>{room_data.get('room_name', room_id)}</b> ({room_id}): Error - {room_data['error']}")
|
problematic_rooms.append(
|
||||||
|
f"❌ <b>{room_data.get('room_name', room_id)}</b> ({room_id}): Error - {room_data['error']}"
|
||||||
|
)
|
||||||
elif is_admin:
|
elif is_admin:
|
||||||
# Show unlimited power status for modern rooms
|
# Show unlimited power status for modern rooms
|
||||||
if room_data.get("bot_has_unlimited_power", False):
|
if room_data.get("bot_has_unlimited_power", False):
|
||||||
@@ -283,10 +268,14 @@ def generate_room_summary(
|
|||||||
if room_data.get("users_higher"):
|
if room_data.get("users_higher"):
|
||||||
room_info += f" - Higher power users: {len(room_data['users_higher'])}"
|
room_info += f" - Higher power users: {len(room_data['users_higher'])}"
|
||||||
if room_data.get("users_equal"):
|
if room_data.get("users_equal"):
|
||||||
room_info += f" - Equal power users: {len(room_data['users_equal'])}"
|
room_info += (
|
||||||
|
f" - Equal power users: {len(room_data['users_equal'])}"
|
||||||
|
)
|
||||||
problematic_rooms.append(room_info)
|
problematic_rooms.append(room_info)
|
||||||
else:
|
else:
|
||||||
problematic_rooms.append(f"❌ <b>{room_data['room_name']}</b> ({room_id}): Admin: No (level: {room_data['bot_power_level']}) [v{room_data.get('room_version', '1')}]")
|
problematic_rooms.append(
|
||||||
|
f"❌ <b>{room_data['room_name']}</b> ({room_id}): Admin: No (level: {room_data['bot_power_level']}) [v{room_data.get('room_version', '1')}]"
|
||||||
|
)
|
||||||
|
|
||||||
# Generate HTML response
|
# Generate HTML response
|
||||||
response = ""
|
response = ""
|
||||||
@@ -301,8 +290,7 @@ def generate_room_summary(
|
|||||||
|
|
||||||
|
|
||||||
def generate_summary_stats(
|
def generate_summary_stats(
|
||||||
space_data: Dict[str, Any],
|
space_data: Dict[str, Any], room_stats: Dict[str, int]
|
||||||
room_stats: Dict[str, int]
|
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Generate summary statistics HTML.
|
"""Generate summary statistics HTML.
|
||||||
|
|
||||||
@@ -321,22 +309,19 @@ def generate_summary_stats(
|
|||||||
response += f"• Legacy room versions (1-11): {room_stats['legacy_rooms']}<br />"
|
response += f"• Legacy room versions (1-11): {room_stats['legacy_rooms']}<br />"
|
||||||
|
|
||||||
# Add note about unlimited power for modern rooms
|
# Add note about unlimited power for modern rooms
|
||||||
if room_stats['modern_rooms'] > 0:
|
if room_stats["modern_rooms"] > 0:
|
||||||
response += f"<br />ℹ️ <b>Note:</b> In modern room versions (12+), creators have unlimited power and cannot be restricted by power levels.<br />"
|
response += f"<br />ℹ️ <b>Note:</b> In modern room versions (12+), creators have unlimited power and cannot be restricted by power levels.<br />"
|
||||||
|
|
||||||
if room_stats['not_in_room_count'] > 0:
|
if room_stats["not_in_room_count"] > 0:
|
||||||
response += f"• Rooms bot not in: {room_stats['not_in_room_count']}<br />"
|
response += f"• Rooms bot not in: {room_stats['not_in_room_count']}<br />"
|
||||||
if room_stats['error_rooms'] > 0:
|
if room_stats["error_rooms"] > 0:
|
||||||
response += f"• Rooms with errors: {room_stats['error_rooms']}<br />"
|
response += f"• Rooms with errors: {room_stats['error_rooms']}<br />"
|
||||||
|
|
||||||
response += "<br />"
|
response += "<br />"
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
def generate_issues_and_warnings(
|
def generate_issues_and_warnings(issues: List[str], warnings: List[str]) -> str:
|
||||||
issues: List[str],
|
|
||||||
warnings: List[str]
|
|
||||||
) -> str:
|
|
||||||
"""Generate issues and warnings HTML.
|
"""Generate issues and warnings HTML.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
|||||||
@@ -95,4 +95,4 @@ def generate_community_slug(community_name: str) -> str:
|
|||||||
str: A slug made from the first letter of each word, lowercase
|
str: A slug made from the first letter of each word, lowercase
|
||||||
"""
|
"""
|
||||||
words = community_name.strip().split()
|
words = community_name.strip().split()
|
||||||
return ''.join(word[0].lower() for word in words if word)
|
return "".join(word[0].lower() for word in words if word)
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ def split_doctor_report(report_text: str, max_chunk_size: int = 4000) -> List[st
|
|||||||
chunks = []
|
chunks = []
|
||||||
current_chunk = ""
|
current_chunk = ""
|
||||||
|
|
||||||
for line in report_text.split('\n'):
|
for line in report_text.split("\n"):
|
||||||
if len(current_chunk) + len(line) + 1 > max_chunk_size:
|
if len(current_chunk) + len(line) + 1 > max_chunk_size:
|
||||||
if current_chunk:
|
if current_chunk:
|
||||||
chunks.append(current_chunk.strip())
|
chunks.append(current_chunk.strip())
|
||||||
@@ -63,7 +63,7 @@ def split_doctor_report(report_text: str, max_chunk_size: int = 4000) -> List[st
|
|||||||
current_chunk = line[max_chunk_size:]
|
current_chunk = line[max_chunk_size:]
|
||||||
else:
|
else:
|
||||||
if current_chunk:
|
if current_chunk:
|
||||||
current_chunk += '\n' + line
|
current_chunk += "\n" + line
|
||||||
else:
|
else:
|
||||||
current_chunk = line
|
current_chunk = line
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ def _split_by_sections(text: str, max_size: int) -> List[str]:
|
|||||||
sections = []
|
sections = []
|
||||||
current_section = ""
|
current_section = ""
|
||||||
|
|
||||||
lines = text.split('\n')
|
lines = text.split("\n")
|
||||||
for line in lines:
|
for line in lines:
|
||||||
if any(line.startswith(header) for header in section_headers):
|
if any(line.startswith(header) for header in section_headers):
|
||||||
if current_section and len(current_section) > max_size:
|
if current_section and len(current_section) > max_size:
|
||||||
@@ -101,7 +101,7 @@ def _split_by_sections(text: str, max_size: int) -> List[str]:
|
|||||||
# This section would be too big
|
# This section would be too big
|
||||||
return []
|
return []
|
||||||
if current_section:
|
if current_section:
|
||||||
current_section += '\n' + line
|
current_section += "\n" + line
|
||||||
else:
|
else:
|
||||||
current_section = line
|
current_section = line
|
||||||
|
|
||||||
@@ -133,7 +133,7 @@ def format_ban_results(ban_event_map: Dict[str, List[str]]) -> str:
|
|||||||
if rooms:
|
if rooms:
|
||||||
result_parts.append(f"Failed to ban {user} from: {', '.join(rooms)}")
|
result_parts.append(f"Failed to ban {user} from: {', '.join(rooms)}")
|
||||||
|
|
||||||
return '\n'.join(result_parts) if result_parts else "No ban operations performed"
|
return "\n".join(result_parts) if result_parts else "No ban operations performed"
|
||||||
|
|
||||||
|
|
||||||
def format_sync_results(sync_results: Dict[str, List[str]]) -> str:
|
def format_sync_results(sync_results: Dict[str, List[str]]) -> str:
|
||||||
|
|||||||
@@ -57,7 +57,9 @@ class ResponseBuilder:
|
|||||||
return f"Success: {message}"
|
return f"Success: {message}"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def build_list_response(title: str, items: List[str], allow_html: bool = True) -> str:
|
def build_list_response(
|
||||||
|
title: str, items: List[str], allow_html: bool = True
|
||||||
|
) -> str:
|
||||||
"""Build a list response.
|
"""Build a list response.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -69,7 +71,9 @@ class ResponseBuilder:
|
|||||||
str: Formatted list response
|
str: Formatted list response
|
||||||
"""
|
"""
|
||||||
if not items:
|
if not items:
|
||||||
return ResponseBuilder.build_html_response(title, "No items found.", allow_html)
|
return ResponseBuilder.build_html_response(
|
||||||
|
title, "No items found.", allow_html
|
||||||
|
)
|
||||||
|
|
||||||
if allow_html:
|
if allow_html:
|
||||||
items_html = "<br />".join(items)
|
items_html = "<br />".join(items)
|
||||||
@@ -104,7 +108,9 @@ class ResponseBuilder:
|
|||||||
return f"<a href='https://matrix.to/#/{user_id}'>{user_id}</a>"
|
return f"<a href='https://matrix.to/#/{user_id}'>{user_id}</a>"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def build_activity_report_response(report: Dict[str, List[str]], config: Dict[str, Any]) -> str:
|
def build_activity_report_response(
|
||||||
|
report: Dict[str, List[str]], config: Dict[str, Any]
|
||||||
|
) -> str:
|
||||||
"""Build an activity report response.
|
"""Build an activity report response.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -135,9 +141,7 @@ class ResponseBuilder:
|
|||||||
|
|
||||||
if report.get("ignored"):
|
if report.get("ignored"):
|
||||||
ignored_list = "<br />".join(report["ignored"])
|
ignored_list = "<br />".join(report["ignored"])
|
||||||
response_parts.append(
|
response_parts.append(f"<p><b>Ignored users:</b><br />{ignored_list}</p>")
|
||||||
f"<p><b>Ignored users:</b><br />{ignored_list}</p>"
|
|
||||||
)
|
|
||||||
|
|
||||||
return "".join(response_parts)
|
return "".join(response_parts)
|
||||||
|
|
||||||
@@ -158,11 +162,15 @@ class ResponseBuilder:
|
|||||||
|
|
||||||
if ban_list:
|
if ban_list:
|
||||||
ban_list_html = "<br />".join(ban_list)
|
ban_list_html = "<br />".join(ban_list)
|
||||||
response_parts.append(f"<p><b>Users banned:</b><br /><code>{ban_list_html}</code></p>")
|
response_parts.append(
|
||||||
|
f"<p><b>Users banned:</b><br /><code>{ban_list_html}</code></p>"
|
||||||
|
)
|
||||||
|
|
||||||
if error_list:
|
if error_list:
|
||||||
error_list_html = "<br />".join(error_list)
|
error_list_html = "<br />".join(error_list)
|
||||||
response_parts.append(f"<p><b>Errors:</b><br /><code>{error_list_html}</code></p>")
|
response_parts.append(
|
||||||
|
f"<p><b>Errors:</b><br /><code>{error_list_html}</code></p>"
|
||||||
|
)
|
||||||
|
|
||||||
if not response_parts:
|
if not response_parts:
|
||||||
response_parts.append("<p>No users were banned.</p>")
|
response_parts.append("<p>No users were banned.</p>")
|
||||||
@@ -213,7 +221,9 @@ class ResponseBuilder:
|
|||||||
if report.get("space"):
|
if report.get("space"):
|
||||||
space = report["space"]
|
space = report["space"]
|
||||||
space_info = f"<b>Space:</b> {space.get('room_id', 'Unknown')}<br />"
|
space_info = f"<b>Space:</b> {space.get('room_id', 'Unknown')}<br />"
|
||||||
space_info += f"Bot Power Level: {space.get('bot_power_level', 'Unknown')}<br />"
|
space_info += (
|
||||||
|
f"Bot Power Level: {space.get('bot_power_level', 'Unknown')}<br />"
|
||||||
|
)
|
||||||
space_info += f"Has Admin: {space.get('has_admin', False)}<br />"
|
space_info += f"Has Admin: {space.get('has_admin', False)}<br />"
|
||||||
response_parts.append(f"<p>{space_info}</p>")
|
response_parts.append(f"<p>{space_info}</p>")
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,7 @@ from mautrix.client import Client
|
|||||||
|
|
||||||
|
|
||||||
async def validate_room_creation_params(
|
async def validate_room_creation_params(
|
||||||
roomname: str,
|
roomname: str, config: dict, evt: Optional[MessageEvent] = None
|
||||||
config: dict,
|
|
||||||
evt: Optional[MessageEvent] = None
|
|
||||||
) -> Tuple[str, bool, bool, str]:
|
) -> Tuple[str, bool, bool, str]:
|
||||||
"""Validate and process room creation parameters.
|
"""Validate and process room creation parameters.
|
||||||
|
|
||||||
@@ -51,7 +49,7 @@ async def prepare_room_creation_data(
|
|||||||
sanitized_name: str,
|
sanitized_name: str,
|
||||||
config: dict,
|
config: dict,
|
||||||
client: Client,
|
client: Client,
|
||||||
invitees: Optional[List[str]] = None
|
invitees: Optional[List[str]] = None,
|
||||||
) -> Tuple[str, str, List[str], str]:
|
) -> Tuple[str, str, List[str], str]:
|
||||||
"""Prepare data needed for room creation.
|
"""Prepare data needed for room creation.
|
||||||
|
|
||||||
@@ -79,7 +77,7 @@ async def prepare_power_levels(
|
|||||||
client: Client,
|
client: Client,
|
||||||
config: dict,
|
config: dict,
|
||||||
parent_room: str,
|
parent_room: str,
|
||||||
power_level_override: Optional[PowerLevelStateEventContent] = None
|
power_level_override: Optional[PowerLevelStateEventContent] = None,
|
||||||
) -> PowerLevelStateEventContent:
|
) -> PowerLevelStateEventContent:
|
||||||
"""Prepare power levels for room creation.
|
"""Prepare power levels for room creation.
|
||||||
|
|
||||||
@@ -106,7 +104,11 @@ async def prepare_power_levels(
|
|||||||
power_levels = PowerLevelStateEventContent()
|
power_levels = PowerLevelStateEventContent()
|
||||||
|
|
||||||
# Copy only user power levels from parent space, not the entire permission set
|
# Copy only user power levels from parent space, not the entire permission set
|
||||||
if parent_power_levels and hasattr(parent_power_levels, 'users') and parent_power_levels.users:
|
if (
|
||||||
|
parent_power_levels
|
||||||
|
and hasattr(parent_power_levels, "users")
|
||||||
|
and parent_power_levels.users
|
||||||
|
):
|
||||||
try:
|
try:
|
||||||
user_power_levels = parent_power_levels.users.copy()
|
user_power_levels = parent_power_levels.users.copy()
|
||||||
# Ensure bot has highest power
|
# Ensure bot has highest power
|
||||||
@@ -150,7 +152,7 @@ def prepare_initial_state(
|
|||||||
server: str,
|
server: str,
|
||||||
force_encryption: bool,
|
force_encryption: bool,
|
||||||
force_unencryption: bool,
|
force_unencryption: bool,
|
||||||
creation_content: Optional[Dict[str, Any]] = None
|
creation_content: Optional[Dict[str, Any]] = None,
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""Prepare initial state events for room creation.
|
"""Prepare initial state events for room creation.
|
||||||
|
|
||||||
@@ -169,51 +171,52 @@ def prepare_initial_state(
|
|||||||
|
|
||||||
# Only add space parent state if we have a parent room
|
# Only add space parent state if we have a parent room
|
||||||
if parent_room:
|
if parent_room:
|
||||||
initial_state.extend([
|
initial_state.extend(
|
||||||
|
[
|
||||||
{
|
{
|
||||||
"type": str(EventType.SPACE_PARENT),
|
"type": str(EventType.SPACE_PARENT),
|
||||||
"state_key": parent_room,
|
"state_key": parent_room,
|
||||||
"content": {
|
"content": {"via": [server], "canonical": True},
|
||||||
"via": [server],
|
|
||||||
"canonical": True
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": str(EventType.ROOM_JOIN_RULES),
|
"type": str(EventType.ROOM_JOIN_RULES),
|
||||||
"content": {
|
"content": {
|
||||||
"join_rule": "restricted",
|
"join_rule": "restricted",
|
||||||
"allow": [{
|
"allow": [
|
||||||
"type": "m.room_membership",
|
{"type": "m.room_membership", "room_id": parent_room}
|
||||||
"room_id": parent_room
|
],
|
||||||
}]
|
},
|
||||||
}
|
},
|
||||||
}
|
]
|
||||||
])
|
)
|
||||||
|
|
||||||
# Add encryption if needed
|
# Add encryption if needed
|
||||||
if (config.get("encrypt", False) and not force_unencryption) or force_encryption:
|
if (config.get("encrypt", False) and not force_unencryption) or force_encryption:
|
||||||
initial_state.append({
|
initial_state.append(
|
||||||
|
{
|
||||||
"type": str(EventType.ROOM_ENCRYPTION),
|
"type": str(EventType.ROOM_ENCRYPTION),
|
||||||
"content": {
|
"content": {"algorithm": "m.megolm.v1.aes-sha2"},
|
||||||
"algorithm": "m.megolm.v1.aes-sha2"
|
|
||||||
}
|
}
|
||||||
})
|
)
|
||||||
|
|
||||||
# Add history visibility if specified in creation_content
|
# Add history visibility if specified in creation_content
|
||||||
if creation_content and "m.room.history_visibility" in creation_content:
|
if creation_content and "m.room.history_visibility" in creation_content:
|
||||||
initial_state.append({
|
initial_state.append(
|
||||||
|
{
|
||||||
"type": str(EventType.ROOM_HISTORY_VISIBILITY),
|
"type": str(EventType.ROOM_HISTORY_VISIBILITY),
|
||||||
"content": {
|
"content": {
|
||||||
"history_visibility": creation_content.get("m.room.history_visibility", "joined")
|
"history_visibility": creation_content.get(
|
||||||
|
"m.room.history_visibility", "joined"
|
||||||
|
)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
})
|
)
|
||||||
|
|
||||||
return initial_state
|
return initial_state
|
||||||
|
|
||||||
|
|
||||||
def adjust_power_levels_for_modern_rooms(
|
def adjust_power_levels_for_modern_rooms(
|
||||||
power_levels: PowerLevelStateEventContent,
|
power_levels: PowerLevelStateEventContent, room_version: str
|
||||||
room_version: str
|
|
||||||
) -> PowerLevelStateEventContent:
|
) -> PowerLevelStateEventContent:
|
||||||
"""Adjust power levels for modern room versions.
|
"""Adjust power levels for modern room versions.
|
||||||
|
|
||||||
@@ -229,17 +232,15 @@ def adjust_power_levels_for_modern_rooms(
|
|||||||
if room_version and int(room_version) >= 12 and power_levels:
|
if room_version and int(room_version) >= 12 and power_levels:
|
||||||
if power_levels.users:
|
if power_levels.users:
|
||||||
# Remove bot from users list but keep other important settings
|
# Remove bot from users list but keep other important settings
|
||||||
power_levels.users.pop("bot_mxid", None) # Will be replaced with actual bot mxid
|
power_levels.users.pop(
|
||||||
|
"bot_mxid", None
|
||||||
|
) # Will be replaced with actual bot mxid
|
||||||
|
|
||||||
return power_levels
|
return power_levels
|
||||||
|
|
||||||
|
|
||||||
async def add_room_to_space(
|
async def add_room_to_space(
|
||||||
client: Client,
|
client: Client, parent_room: str, room_id: str, server: str, sleep_duration: float
|
||||||
parent_room: str,
|
|
||||||
room_id: str,
|
|
||||||
server: str,
|
|
||||||
sleep_duration: float
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Add created room to parent space.
|
"""Add created room to parent space.
|
||||||
|
|
||||||
@@ -254,20 +255,14 @@ async def add_room_to_space(
|
|||||||
await client.send_state_event(
|
await client.send_state_event(
|
||||||
parent_room,
|
parent_room,
|
||||||
EventType.SPACE_CHILD,
|
EventType.SPACE_CHILD,
|
||||||
{
|
{"via": [server], "suggested": False},
|
||||||
"via": [server],
|
state_key=room_id,
|
||||||
"suggested": False
|
|
||||||
},
|
|
||||||
state_key=room_id
|
|
||||||
)
|
)
|
||||||
await asyncio.sleep(sleep_duration)
|
await asyncio.sleep(sleep_duration)
|
||||||
|
|
||||||
|
|
||||||
async def verify_room_creation(
|
async def verify_room_creation(
|
||||||
client: Client,
|
client: Client, room_id: str, expected_version: str, logger
|
||||||
room_id: str,
|
|
||||||
expected_version: str,
|
|
||||||
logger
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Verify that room was created with correct settings.
|
"""Verify that room was created with correct settings.
|
||||||
|
|
||||||
@@ -279,9 +274,16 @@ async def verify_room_creation(
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
from .room_utils import get_room_version_and_creators
|
from .room_utils import get_room_version_and_creators
|
||||||
actual_version, actual_creators = await get_room_version_and_creators(client, room_id, logger)
|
|
||||||
logger.info(f"Room {room_id} created with version {actual_version} (requested: {expected_version})")
|
actual_version, actual_creators = await get_room_version_and_creators(
|
||||||
|
client, room_id, logger
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
f"Room {room_id} created with version {actual_version} (requested: {expected_version})"
|
||||||
|
)
|
||||||
if actual_version != expected_version:
|
if actual_version != expected_version:
|
||||||
logger.warning(f"Room version mismatch: requested {expected_version}, got {actual_version}")
|
logger.warning(
|
||||||
|
f"Room version mismatch: requested {expected_version}, got {actual_version}"
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Could not verify room version for {room_id}: {e}")
|
logger.warning(f"Could not verify room version for {room_id}: {e}")
|
||||||
|
|||||||
@@ -30,7 +30,9 @@ async def validate_room_alias(client, alias_localpart: str, server: str) -> bool
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def validate_room_aliases(client, room_names: list[str], community_slug: str, server: str) -> Tuple[bool, List[str]]:
|
async def validate_room_aliases(
|
||||||
|
client, room_names: list[str], community_slug: str, server: str
|
||||||
|
) -> Tuple[bool, List[str]]:
|
||||||
"""Validate that all room aliases are available.
|
"""Validate that all room aliases are available.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -50,6 +52,7 @@ async def validate_room_aliases(client, room_names: list[str], community_slug: s
|
|||||||
for room_name in room_names:
|
for room_name in room_names:
|
||||||
# Clean the room name and create alias
|
# Clean the room name and create alias
|
||||||
from .message_utils import sanitize_room_name
|
from .message_utils import sanitize_room_name
|
||||||
|
|
||||||
sanitized_name = sanitize_room_name(room_name)
|
sanitized_name = sanitize_room_name(room_name)
|
||||||
alias_localpart = f"{sanitized_name}-{community_slug}"
|
alias_localpart = f"{sanitized_name}-{community_slug}"
|
||||||
|
|
||||||
@@ -61,7 +64,9 @@ async def validate_room_aliases(client, room_names: list[str], community_slug: s
|
|||||||
return len(conflicting_aliases) == 0, conflicting_aliases
|
return len(conflicting_aliases) == 0, conflicting_aliases
|
||||||
|
|
||||||
|
|
||||||
async def get_room_version_and_creators(client, room_id: str, logger=None) -> Tuple[str, List[str]]:
|
async def get_room_version_and_creators(
|
||||||
|
client, room_id: str, logger=None
|
||||||
|
) -> Tuple[str, List[str]]:
|
||||||
"""Get the room version and creators for a room.
|
"""Get the room version and creators for a room.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -134,7 +139,9 @@ async def user_has_unlimited_power(client, user_id: str, room_id: str) -> bool:
|
|||||||
bool: True if user has unlimited power
|
bool: True if user has unlimited power
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
room_version, creators = await get_room_version_and_creators(client, room_id, None)
|
room_version, creators = await get_room_version_and_creators(
|
||||||
|
client, room_id, None
|
||||||
|
)
|
||||||
|
|
||||||
# In modern room versions (12+), creators have unlimited power
|
# In modern room versions (12+), creators have unlimited power
|
||||||
if is_modern_room_version(room_version):
|
if is_modern_room_version(room_version):
|
||||||
|
|||||||
@@ -42,9 +42,9 @@ async def check_if_banned(client, userid: str, banlists: List[str], logger) -> b
|
|||||||
|
|
||||||
for rule in user_policies:
|
for rule in user_policies:
|
||||||
try:
|
try:
|
||||||
if bool(
|
if bool(fnmatch.fnmatch(userid, rule["content"]["entity"])) and bool(
|
||||||
fnmatch.fnmatch(userid, rule["content"]["entity"])
|
re.search("ban$", rule["content"]["recommendation"])
|
||||||
) and bool(re.search("ban$", rule["content"]["recommendation"])):
|
):
|
||||||
return True
|
return True
|
||||||
except Exception:
|
except Exception:
|
||||||
# Skip invalid rules
|
# Skip invalid rules
|
||||||
@@ -81,10 +81,18 @@ async def get_banlist_roomids(client, banlists: List[str], logger) -> List[str]:
|
|||||||
return banlist_roomids
|
return banlist_roomids
|
||||||
|
|
||||||
|
|
||||||
async def ban_user_from_rooms(client, user: str, roomlist: List[str], reason: str = "banned",
|
async def ban_user_from_rooms(
|
||||||
all_rooms: bool = False, redact_on_ban: bool = False,
|
client,
|
||||||
get_messages_to_redact_func=None, database=None,
|
user: str,
|
||||||
sleep_time: float = 0.1, logger=None) -> Dict:
|
roomlist: List[str],
|
||||||
|
reason: str = "banned",
|
||||||
|
all_rooms: bool = False,
|
||||||
|
redact_on_ban: bool = False,
|
||||||
|
get_messages_to_redact_func=None,
|
||||||
|
database=None,
|
||||||
|
sleep_time: float = 0.1,
|
||||||
|
logger=None,
|
||||||
|
) -> Dict:
|
||||||
"""Ban a user from a list of rooms.
|
"""Ban a user from a list of rooms.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -149,8 +157,14 @@ async def ban_user_from_rooms(client, user: str, roomlist: List[str], reason: st
|
|||||||
return ban_event_map
|
return ban_event_map
|
||||||
|
|
||||||
|
|
||||||
async def user_permitted(client, user_id: UserID, parent_room: str, min_level: int = 50,
|
async def user_permitted(
|
||||||
room_id: str = None, logger=None) -> bool:
|
client,
|
||||||
|
user_id: UserID,
|
||||||
|
parent_room: str,
|
||||||
|
min_level: int = 50,
|
||||||
|
room_id: str = None,
|
||||||
|
logger=None,
|
||||||
|
) -> bool:
|
||||||
"""Check if a user has sufficient power level in a room.
|
"""Check if a user has sufficient power level in a room.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -169,6 +183,7 @@ async def user_permitted(client, user_id: UserID, parent_room: str, min_level: i
|
|||||||
|
|
||||||
# First check if user has unlimited power (creator in modern room versions)
|
# First check if user has unlimited power (creator in modern room versions)
|
||||||
from .room_utils import user_has_unlimited_power
|
from .room_utils import user_has_unlimited_power
|
||||||
|
|
||||||
if await user_has_unlimited_power(client, user_id, target_room):
|
if await user_has_unlimited_power(client, user_id, target_room):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -196,4 +211,5 @@ async def user_has_unlimited_power(client, user_id: str, room_id: str) -> bool:
|
|||||||
bool: True if user has unlimited power
|
bool: True if user has unlimited power
|
||||||
"""
|
"""
|
||||||
from .room_utils import user_has_unlimited_power as room_user_has_unlimited_power
|
from .room_utils import user_has_unlimited_power as room_user_has_unlimited_power
|
||||||
|
|
||||||
return await room_user_has_unlimited_power(client, user_id, room_id)
|
return await room_user_has_unlimited_power(client, user_id, room_id)
|
||||||
|
|||||||
Reference in New Issue
Block a user