"""Diagnostic utility functions for the community bot."""
from typing import Dict, List, Any, Optional, Tuple
from mautrix.types import EventType
from mautrix.client import Client
async def check_space_permissions(
client: Client,
parent_room: str,
logger
) -> Dict[str, Any]:
"""Check bot permissions in the parent space.
Args:
client: Matrix client
parent_room: Parent room ID
logger: Logger instance
Returns:
Dict containing space permission information
"""
try:
space_power_levels = await client.get_state_event(
parent_room, EventType.ROOM_POWER_LEVELS
)
bot_level = space_power_levels.get_user_level(client.mxid)
space_info = {
"room_id": parent_room,
"bot_power_level": bot_level,
"has_admin": bot_level >= 100,
"users_higher_or_equal": [],
"users_equal": [],
"users_higher": []
}
# Check for users with equal or higher power level
for user, level in space_power_levels.users.items():
if user != client.mxid and level >= bot_level:
if level == bot_level:
space_info["users_equal"].append({
"user": user,
"level": level
})
else:
space_info["users_higher"].append({
"user": user,
"level": level
})
space_info["users_higher_or_equal"].append({
"user": user,
"level": level
})
return space_info
except Exception as e:
logger.error(f"Failed to check space permissions: {e}")
return {
"room_id": parent_room,
"error": str(e)
}
async def check_room_permissions(
client: Client,
room_id: str,
logger
) -> Dict[str, Any]:
"""Check bot permissions in a specific room.
Args:
client: Matrix client
room_id: Room ID to check
logger: Logger instance
Returns:
Dict containing room permission information
"""
try:
# Check if bot is in the room
try:
await client.get_state_event(room_id, EventType.ROOM_MEMBER, client.mxid)
except:
return {
"room_id": room_id,
"error": "Bot not in room"
}
# Get power levels
room_power_levels = await client.get_state_event(
room_id, EventType.ROOM_POWER_LEVELS
)
bot_level = room_power_levels.get_user_level(client.mxid)
# Get room name if available
room_name = room_id
try:
from .common_utils import get_room_name
room_name = await get_room_name(client, room_id, logger) or room_id
except:
pass
# 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)
# Check if bot has unlimited power (creator in modern room versions)
from .room_utils import user_has_unlimited_power
bot_has_unlimited_power = await user_has_unlimited_power(client, client.mxid, room_id)
room_report = {
"room_id": room_id,
"room_name": room_name,
"room_version": room_version,
"creators": creators,
"bot_power_level": bot_level,
"has_admin": bot_level >= 100 or bot_has_unlimited_power,
"bot_has_unlimited_power": bot_has_unlimited_power,
"users_higher_or_equal": [],
"users_equal": [],
"users_higher": []
}
# Check for users with equal or higher power level
for user, level in room_power_levels.users.items():
if user != client.mxid and level >= bot_level:
if level == bot_level:
room_report["users_equal"].append({
"user": user,
"level": level
})
else:
room_report["users_higher"].append({
"user": user,
"level": level
})
room_report["users_higher_or_equal"].append({
"user": user,
"level": level
})
return room_report
except Exception as e:
logger.error(f"Failed to check room permissions for {room_id}: {e}")
return {
"room_id": room_id,
"error": str(e)
}
def analyze_room_data(
room_data: Dict[str, Any],
is_modern_room_version_func
) -> Tuple[str, str, bool, bool, bool]:
"""Analyze room data to determine status and categorization.
Args:
room_data: Room data dictionary
is_modern_room_version_func: Function to check if room version is modern
Returns:
Tuple of (status, category, is_admin, is_modern, has_error)
"""
if "error" in room_data:
if room_data["error"] == "Bot not in room":
return "not_in_room", "error", False, False, True
else:
return "error", "error", False, False, True
# Check if modern room version
is_modern = is_modern_room_version_func(room_data.get("room_version", "1"))
# Check admin status
is_admin = room_data.get("has_admin", False)
if is_admin:
return "admin", "admin", True, is_modern, False
else:
return "no_admin", "problematic", False, is_modern, False
def generate_space_summary(
space_data: Dict[str, Any]
) -> str:
"""Generate HTML summary for space permissions.
Args:
space_data: Space permission data
Returns:
str: HTML formatted space summary
"""
if "error" in space_data:
return f"
đ Parent Space
â Error: {space_data['error']}
"
space_status = "â
" if space_data.get("has_admin", False) else "â"
response = f"đ Parent Space
"
response += f"{space_status} Administrative privileges: {'Yes' if space_data['has_admin'] else 'No'} (level: {space_data['bot_power_level']})
"
if space_data.get("users_higher"):
response += f"â ī¸ Users with higher power: {', '.join([f'{u['user']} ({u['level']})' for u in space_data['users_higher']])}
"
if space_data.get("users_equal"):
response += f"â ī¸ Users with equal power: {', '.join([f'{u['user']} ({u['level']})' for u in space_data['users_equal']])}
"
response += "
"
return response
def generate_room_summary(
rooms_data: Dict[str, Any],
is_modern_room_version_func
) -> Tuple[str, Dict[str, int]]:
"""Generate HTML summary for room permissions.
Args:
rooms_data: Dictionary of room data
is_modern_room_version_func: Function to check if room version is modern
Returns:
Tuple of (HTML response, statistics dict)
"""
problematic_rooms = []
stats = {
"admin_rooms": 0,
"non_admin_rooms": 0,
"error_rooms": 0,
"not_in_room_count": 0,
"modern_rooms": 0,
"legacy_rooms": 0
}
for room_id, room_data in rooms_data.items():
status, category, is_admin, is_modern, has_error = analyze_room_data(
room_data, is_modern_room_version_func
)
# Update statistics
if has_error:
stats["error_rooms"] += 1
if room_data.get("error") == "Bot not in room":
stats["not_in_room_count"] += 1
else:
if is_admin:
stats["admin_rooms"] += 1
else:
stats["non_admin_rooms"] += 1
if is_modern:
stats["modern_rooms"] += 1
else:
stats["legacy_rooms"] += 1
# 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 has_error:
if room_data["error"] == "Bot not in room":
problematic_rooms.append(f"â {room_data.get('room_name', room_id)} ({room_id}): Bot not in room")
else:
problematic_rooms.append(f"â {room_data.get('room_name', room_id)} ({room_id}): Error - {room_data['error']}")
elif is_admin:
# Show unlimited power status for modern rooms
if room_data.get("bot_has_unlimited_power", False):
room_info = f"â
{room_data['room_name']} ({room_id}): Unlimited Power (Creator) [v{room_data.get('room_version', '1')}]"
else:
room_info = f"â
{room_data['room_name']} ({room_id}): Admin: Yes (level: {room_data['bot_power_level']}) [v{room_data.get('room_version', '1')}]"
# Add power level conflict info
if room_data.get("users_higher") or room_data.get("users_equal"):
if room_data.get("bot_has_unlimited_power", False):
room_info += f" - Note: Power level conflicts are irrelevant for creators with unlimited power"
else:
if room_data.get("users_higher"):
room_info += f" - Higher power users: {len(room_data['users_higher'])}"
if room_data.get("users_equal"):
room_info += f" - Equal power users: {len(room_data['users_equal'])}"
problematic_rooms.append(room_info)
else:
problematic_rooms.append(f"â {room_data['room_name']} ({room_id}): Admin: No (level: {room_data['bot_power_level']}) [v{room_data.get('room_version', '1')}]")
# Generate HTML response
response = ""
if problematic_rooms:
response += f"đ Problematic Rooms ({len(problematic_rooms)} of {len(rooms_data)} total)
"
response += "Use !community doctor <room_id> for detailed analysis of specific rooms
"
for room_info in problematic_rooms:
response += f"{room_info}
"
response += "
"
return response, stats
def generate_summary_stats(
space_data: Dict[str, Any],
room_stats: Dict[str, int]
) -> str:
"""Generate summary statistics HTML.
Args:
space_data: Space permission data
room_stats: Room statistics
Returns:
str: HTML formatted summary statistics
"""
response = f"đ Summary
"
response += f"âĸ Parent space: {'â
Admin' if space_data.get('has_admin', False) else 'â No admin'}
"
response += f"âĸ Rooms with admin: {room_stats['admin_rooms']}
"
response += f"âĸ Rooms without admin: {room_stats['non_admin_rooms']}
"
response += f"âĸ Modern room versions (12+): {room_stats['modern_rooms']}
"
response += f"âĸ Legacy room versions (1-11): {room_stats['legacy_rooms']}
"
# Add note about unlimited power for modern rooms
if room_stats['modern_rooms'] > 0:
response += f"
âšī¸ Note: In modern room versions (12+), creators have unlimited power and cannot be restricted by power levels.
"
if room_stats['not_in_room_count'] > 0:
response += f"âĸ Rooms bot not in: {room_stats['not_in_room_count']}
"
if room_stats['error_rooms'] > 0:
response += f"âĸ Rooms with errors: {room_stats['error_rooms']}
"
response += "
"
return response
def generate_issues_and_warnings(
issues: List[str],
warnings: List[str]
) -> str:
"""Generate issues and warnings HTML.
Args:
issues: List of critical issues
warnings: List of warnings
Returns:
str: HTML formatted issues and warnings
"""
response = ""
if issues:
response += f"đ¨ Critical Issues
"
for issue in issues:
response += f"âĸ {issue}
"
response += "
"
if warnings:
response += f"â ī¸ Warnings
"
for warning in warnings:
response += f"âĸ {warning}
"
response += "
"
return response
def generate_all_clear_message() -> str:
"""Generate all clear message HTML.
Returns:
str: HTML formatted all clear message
"""
return "â
All Clear
No permission issues detected. The bot should be able to manage all rooms and users effectively.
"