From 81828b9b72b245b40bf5a36e4248adc44c9576ef Mon Sep 17 00:00:00 2001 From: William Kray Date: Sun, 29 Jun 2025 22:14:10 -0700 Subject: [PATCH] better doctor commands --- community/bot.py | 201 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 181 insertions(+), 20 deletions(-) diff --git a/community/bot.py b/community/bot.py index a8d3f44..6bc221a 100644 --- a/community/bot.py +++ b/community/bot.py @@ -2627,13 +2627,19 @@ class CommunityBot(Plugin): "doctor", help="review bot permissions across the space and all rooms to identify potential issues" ) - async def doctor_check(self, evt: MessageEvent) -> None: + @command.argument("room", required=False) + async def doctor_check(self, evt: MessageEvent, room: str = None) -> None: if not await self.check_parent_room(evt): return if not await self.user_permitted(evt.sender): await evt.reply("You don't have permission to use this command") return + # If a room is specified, show detailed report for that room + if room: + await self._doctor_room_detail(evt, room) + return + msg = await evt.respond("Running diagnostic check...") try: @@ -2681,11 +2687,12 @@ class CommunityBot(Plugin): if bot_level < 100: report["issues"].append(f"Bot lacks administrative privileges in parent space (level: {bot_level})") - if report["space"]["users_higher"]: - report["warnings"].append(f"Users with higher power level in parent space: {', '.join([f'{u['user']} ({u['level']})' for u in report['space']['users_higher']])}") - - if report["space"]["users_equal"]: - report["warnings"].append(f"Users with equal power level in parent space: {', '.join([f'{u['user']} ({u['level']})' for u in report['space']['users_equal']])}") + # Remove verbose warnings from summary - these will be shown in detailed room reports + # if report["space"]["users_higher"]: + # report["warnings"].append(f"Users with higher power level in parent space: {', '.join([f'{u['user']} ({u['level']})' for u in report['space']['users_higher']])}") + # + # if report["space"]["users_equal"]: + # report["warnings"].append(f"Users with equal power level in parent space: {', '.join([f'{u['user']} ({u['level']})' for u in report['space']['users_equal']])}") except Exception as e: report["space"] = { @@ -2756,11 +2763,12 @@ class CommunityBot(Plugin): if bot_level < 100: report["issues"].append(f"Bot lacks administrative privileges in room '{room_name}' ({room_id}) - level: {bot_level}") - if room_report["users_higher"]: - report["warnings"].append(f"Users with higher power level in room '{room_name}': {', '.join([f'{u['user']} ({u['level']})' for u in room_report['users_higher']])}") - - if room_report["users_equal"]: - report["warnings"].append(f"Users with equal power level in room '{room_name}': {', '.join([f'{u['user']} ({u['level']})' for u in room_report['users_equal']])}") + # Remove verbose warnings from summary - these will be shown in detailed room reports + # if room_report["users_higher"]: + # report["warnings"].append(f"Users with higher power level in room '{room_name}': {', '.join([f'{u['user']} ({u['level']})' for u in room_report['users_higher']])}") + # + # if room_report["users_equal"]: + # report["warnings"].append(f"Users with equal power level in room '{room_name}': {', '.join([f'{u['user']} ({u['level']})' for u in room_report['users_equal']])}") report["rooms"][room_id] = room_report @@ -2771,8 +2779,8 @@ class CommunityBot(Plugin): } report["issues"].append(f"Failed to check room {room_id}: {e}") - # Generate response - response = "

🔍 Bot Permission Diagnostic Report

\n\n" + # Generate concise summary response + response = "

🔍 Bot Permission Diagnostic Summary

\n\n" # Space summary - only show if there are issues space_has_issues = False @@ -2791,7 +2799,7 @@ class CommunityBot(Plugin): response += f"⚠️ Users with equal power: {', '.join([f'{u['user']} ({u['level']})' for u in report['space']['users_equal']])}\n" response += "\n" - # Rooms summary - only show problematic rooms + # Rooms summary - only show problematic rooms with room IDs problematic_rooms = [] admin_rooms = 0 non_admin_rooms = 0 @@ -2803,27 +2811,28 @@ class CommunityBot(Plugin): error_rooms += 1 if room_data["error"] == "Bot not in room": not_in_room_count += 1 - problematic_rooms.append(f"❌ {room_data.get('room_name', room_id)}: 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)}: Error - {room_data['error']}") + problematic_rooms.append(f"❌ {room_data.get('room_name', room_id)} ({room_id}): Error - {room_data['error']}") else: if room_data["has_admin"]: admin_rooms += 1 # Only show if there are power level conflicts if room_data["users_higher"] or room_data["users_equal"]: - room_info = f"✅ {room_data['room_name']}: Admin: Yes (level: {room_data['bot_power_level']})" + room_info = f"⚠️ {room_data['room_name']} ({room_id}): Admin: Yes (level: {room_data['bot_power_level']})" if room_data["users_higher"]: - room_info += f"\n ⚠️ Higher power users: {', '.join([f'{u['user']} ({u['level']})' for u in room_data['users_higher']])}" + room_info += f" - Higher power users: {len(room_data['users_higher'])}" if room_data["users_equal"]: - room_info += f"\n ⚠️ Equal power users: {', '.join([f'{u['user']} ({u['level']})' for u in room_data['users_equal']])}" + room_info += f" - Equal power users: {len(room_data['users_equal'])}" problematic_rooms.append(room_info) else: non_admin_rooms += 1 - problematic_rooms.append(f"❌ {room_data['room_name']}: Admin: No (level: {room_data['bot_power_level']})") + problematic_rooms.append(f"❌ {room_data['room_name']} ({room_id}): Admin: No (level: {room_data['bot_power_level']})") # Only show rooms section if there are problematic rooms if problematic_rooms: response += f"

🏠 Problematic Rooms ({len(problematic_rooms)} of {len(report['rooms'])} total)

\n" + response += "Use !community doctor <room_id> for detailed analysis of specific rooms\n\n" for room_info in problematic_rooms: response += f"{room_info}\n" response += "\n" @@ -2953,3 +2962,155 @@ class CommunityBot(Plugin): chunks.append(current_chunk.strip()) return chunks + + async def _doctor_room_detail(self, evt: MessageEvent, room: str) -> None: + """Generate detailed diagnostic report for a specific room. + + Args: + evt: The message event + room: Room ID or alias to analyze + """ + msg = await evt.respond(f"Analyzing room {room}...") + + try: + # Resolve room ID if alias provided + room_id = None + if room.startswith("#"): + try: + room_info = await self.client.resolve_room_alias(room) + room_id = room_info["room_id"] + except Exception as e: + await evt.respond(f"Could not resolve room alias {room}: {e}", edits=msg) + return + elif room.startswith("!"): + room_id = room + else: + await evt.respond(f"Invalid room format. Use room ID (!roomid:server) or alias (#alias:server)", edits=msg) + return + + # Check if room is in the space + space_rooms = await self.get_space_roomlist() + if room_id not in space_rooms: + await evt.respond(f"Room {room} is not part of the configured space.", edits=msg) + return + + # Get room name + room_name = room_id + try: + room_name_event = await self.client.get_state_event(room_id, EventType.ROOM_NAME) + room_name = room_name_event.name + except: + pass + + response = f"

🔍 Detailed Analysis: {room_name}

\n" + response += f"Room ID: {room_id}\n\n" + + # Check if bot is in the room + try: + await self.client.get_state_event(room_id, EventType.ROOM_MEMBER, self.client.mxid) + response += "✅ Bot membership: Bot is a member of this room\n\n" + except Exception: + response += "❌ Bot membership: Bot is not a member of this room\n\n" + await evt.respond(response, edits=msg, allow_html=True) + return + + # Get power levels + try: + power_levels = await self.client.get_state_event(room_id, EventType.ROOM_POWER_LEVELS) + bot_level = power_levels.get_user_level(self.client.mxid) + + response += f"

📊 Power Level Analysis

\n" + response += f"• Bot power level: {bot_level}\n" + response += f"• Administrative privileges: {'✅ Yes' if bot_level >= 100 else '❌ No'}\n" + response += f"• Default user level: {power_levels.users_default}\n" + response += f"• Invite level: {power_levels.invite}\n" + response += f"• Kick level: {power_levels.kick}\n" + response += f"• Ban level: {power_levels.ban}\n" + response += f"• Redact level: {power_levels.redact}\n\n" + + # Check for users with equal or higher power level + users_higher = [] + users_equal = [] + + for user, level in power_levels.users.items(): + if user != self.client.mxid and level >= bot_level: + if level == bot_level: + users_equal.append({"user": user, "level": level}) + else: + users_higher.append({"user": user, "level": level}) + + if users_higher: + response += f"

⚠️ Users with Higher Power Level

\n" + for user_info in users_higher: + response += f"• {user_info['user']} (level: {user_info['level']})\n" + response += "\n" + + if users_equal: + response += f"

⚠️ Users with Equal Power Level

\n" + for user_info in users_equal: + response += f"• {user_info['user']} (level: {user_info['level']})\n" + response += "\n" + + if not users_higher and not users_equal: + response += "✅ No power level conflicts detected\n\n" + + # Check specific permissions + response += f"

🔐 Permission Analysis

\n" + + # Get required levels for various actions + events_default = power_levels.events_default + events = power_levels.events + + permissions = [ + ("Send messages", events.get(str(EventType.ROOM_MESSAGE), events_default)), + ("Send state events", power_levels.state_default), + ("Change power levels", events.get(str(EventType.ROOM_POWER_LEVELS), events_default)), + ("Send tombstone", events.get("m.room.tombstone", events_default)), + ("Invite users", power_levels.invite), + ("Kick users", power_levels.kick), + ("Ban users", power_levels.ban), + ("Redact messages", power_levels.redact) + ] + + for perm_name, required_level in permissions: + has_perm = bot_level >= required_level + status = "✅" if has_perm else "❌" + response += f"• {status} {perm_name}: {'Yes' if has_perm else 'No'} (required: {required_level})\n" + + except Exception as e: + response += f"❌ Error getting power levels: {e}\n\n" + + # Check room state + try: + response += f"

🏠 Room State

\n" + + # Check join rules + try: + join_rules = await self.client.get_state_event(room_id, EventType.ROOM_JOIN_RULES) + response += f"• Join rule: {join_rules.join_rule}\n" + except: + response += "• Join rule: Could not determine\n" + + # Check encryption + try: + encryption = await self.client.get_state_event(room_id, EventType.ROOM_ENCRYPTION) + response += f"• Encryption: ✅ Enabled ({encryption.algorithm})\n" + except: + response += "• Encryption: ❌ Not enabled\n" + + # Check space parent + try: + space_parent = await self.client.get_state_event(room_id, EventType.SPACE_PARENT) + response += f"• Space parent: ✅ {space_parent.state_key}\n" + except: + response += "• Space parent: ❌ Not set\n" + + except Exception as e: + response += f"❌ Error checking room state: {e}\n" + + await evt.respond(response, edits=msg, allow_html=True) + + except Exception as e: + error_msg = f"Failed to analyze room {room}: {e}" + self.log.error(error_msg) + await evt.respond(error_msg, edits=msg)