358 lines
13 KiB
Python
358 lines
13 KiB
Python
"""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)
|
||
|
||
# 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, parent_room
|
||
)
|
||
|
||
space_info = {
|
||
"room_id": parent_room,
|
||
"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 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"<h4>📋 Parent Space</h4><br />❌ <b>Error:</b> {space_data['error']}<br /><br />"
|
||
|
||
space_status = "✅" if space_data.get("has_admin", False) else "❌"
|
||
response = f"<h4>📋 Parent Space</h4><br />"
|
||
|
||
# Show admin status with appropriate details
|
||
if space_data.get("bot_has_unlimited_power", False):
|
||
response += f"{space_status} <b>Administrative privileges:</b> Yes (unlimited power - creator)<br />"
|
||
else:
|
||
response += f"{space_status} <b>Administrative privileges:</b> {'Yes' if space_data['has_admin'] else 'No'} (level: {space_data['bot_power_level']})<br />"
|
||
|
||
if space_data.get("users_higher"):
|
||
response += f"⚠️ <b>Users with higher power:</b> {', '.join([f'{u['user']} ({u['level']})' for u in space_data['users_higher']])}<br />"
|
||
if space_data.get("users_equal"):
|
||
response += f"⚠️ <b>Users with equal power:</b> {', '.join([f'{u['user']} ({u['level']})' for u in space_data['users_equal']])}<br />"
|
||
|
||
response += "<br />"
|
||
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"❌ <b>{room_data.get('room_name', room_id)}</b> ({room_id}): Bot not in room"
|
||
)
|
||
else:
|
||
problematic_rooms.append(
|
||
f"❌ <b>{room_data.get('room_name', room_id)}</b> ({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"✅ <b>{room_data['room_name']}</b> ({room_id}): Unlimited Power (Creator) [v{room_data.get('room_version', '1')}]"
|
||
else:
|
||
room_info = f"✅ <b>{room_data['room_name']}</b> ({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"❌ <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
|
||
response = ""
|
||
if problematic_rooms:
|
||
response += f"<h4>🏠 Problematic Rooms ({len(problematic_rooms)} of {len(rooms_data)} total)</h4><br />"
|
||
response += "<i>Use <code>!community doctor <room_id></code> for detailed analysis of specific rooms</i><br /><br />"
|
||
for room_info in problematic_rooms:
|
||
response += f"{room_info}<br />"
|
||
response += "<br />"
|
||
|
||
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"<h4>📊 Summary</h4><br />"
|
||
response += f"• Parent space: {'✅ Admin' if space_data.get('has_admin', False) else '❌ No admin'}<br />"
|
||
response += f"• Rooms with admin: {room_stats['admin_rooms']}<br />"
|
||
response += f"• Rooms without admin: {room_stats['non_admin_rooms']}<br />"
|
||
response += f"• Modern room versions (12+): {room_stats['modern_rooms']}<br />"
|
||
response += f"• Legacy room versions (1-11): {room_stats['legacy_rooms']}<br />"
|
||
|
||
# Add note about unlimited power for modern rooms
|
||
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 />"
|
||
|
||
if room_stats["not_in_room_count"] > 0:
|
||
response += f"• Rooms bot not in: {room_stats['not_in_room_count']}<br />"
|
||
if room_stats["error_rooms"] > 0:
|
||
response += f"• Rooms with errors: {room_stats['error_rooms']}<br />"
|
||
|
||
response += "<br />"
|
||
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"<h4>🚨 Critical Issues</h4><br />"
|
||
for issue in issues:
|
||
response += f"• {issue}<br />"
|
||
response += "<br />"
|
||
|
||
if warnings:
|
||
response += f"<h4>⚠️ Warnings</h4><br />"
|
||
for warning in warnings:
|
||
response += f"• {warning}<br />"
|
||
response += "<br />"
|
||
|
||
return response
|
||
|
||
|
||
def generate_all_clear_message() -> str:
|
||
"""Generate all clear message HTML.
|
||
|
||
Returns:
|
||
str: HTML formatted all clear message
|
||
"""
|
||
return "<h4>✅ All Clear</h4><br />No permission issues detected. The bot should be able to manage all rooms and users effectively.<br />"
|