Fix message edit on approval. Add groups command. Prevent requesting admin group

This commit is contained in:
Astra Logical 2026-07-22 18:08:51 -04:00
parent d9c38ff119
commit 1fc97b6256

View file

@ -49,6 +49,25 @@ def get_keycloak_client() -> KeycloakAdmin:
verify=True, verify=True,
) )
def get_requestable_groups() -> tuple[bool, str]:
"""Fetches available Keycloak groups, filtering out the 'admin' group."""
try:
kc = get_keycloak_client()
groups = kc.get_groups()
# Filter out 'admin' (case-insensitive)
valid_groups = [g["name"] for g in groups if g["name"].lower() != "admin"]
if not valid_groups:
return True, "No groups are currently available to request."
group_list = "\n".join([f"• `{name}`" for name in valid_groups])
return True, f"**Available Groups:**\n{group_list}"
except KeycloakError as e:
return False, f"Keycloak API error: {str(e)}"
except Exception as e:
return False, f"Unexpected error: {str(e)}"
def add_user_to_kc_group(username: str, group_name: str) -> tuple[bool, str]: def add_user_to_kc_group(username: str, group_name: str) -> tuple[bool, str]:
"""Queries Keycloak for the user and group UUIDs and adds the user to the group.""" """Queries Keycloak for the user and group UUIDs and adds the user to the group."""
@ -100,12 +119,29 @@ async def on_invite(client: AsyncClient, room: MatrixRoom, event: InviteMemberEv
async def on_message(client: AsyncClient, room: MatrixRoom, event: RoomMessageText): async def on_message(client: AsyncClient, room: MatrixRoom, event: RoomMessageText):
"""Listens for `!request <group_name>` commands.""" """Listens for commands."""
# Ignore messages sent by the bot itself # Ignore messages sent by the bot itself
if event.sender == client.user_id: if event.sender == client.user_id:
return return
body = event.body.strip() body = event.body.strip()
# --- COMMAND: !groups ---
if body.startswith("!groups"):
success, msg = get_requestable_groups()
await send_message_safe(
client,
room_id=room.room_id,
content={
"msgtype": "m.notice" if not success else "m.text",
"body": msg,
"format": "org.matrix.custom.html",
"formatted_body": msg.replace("\n", "<br>")
},
)
return
# --- COMMAND: !request ---
if not body.startswith("!request"): if not body.startswith("!request"):
return return
@ -114,17 +150,27 @@ async def on_message(client: AsyncClient, room: MatrixRoom, event: RoomMessageTe
await send_message_safe( await send_message_safe(
client, client,
room_id=room.room_id, room_id=room.room_id,
message_type="m.room.message", content={"msgtype": "m.notice", "body": "Usage: !request <group_name> or !groups"},
content={"msgtype": "m.notice", "body": "Usage: !request <group_name>"},
) )
return return
requested_group = parts[1].strip() requested_group = parts[1].strip()
# Block 'admin' requests immediately
if requested_group.lower() == "admin":
await send_message_safe(
client,
room_id=room.room_id,
content={
"msgtype": "m.notice",
"body": "❌ Requests for the 'admin' group are not permitted."
},
)
return
matrix_user = event.sender matrix_user = event.sender
# Extract username (e.g., "@alice:nest.local" -> "alice")
kc_username = matrix_user.split(":")[0].lstrip("@") kc_username = matrix_user.split(":")[0].lstrip("@")
# Send notification message to the private Admin Room
admin_msg_body = ( admin_msg_body = (
f"📋 **Access Request**\n" f"📋 **Access Request**\n"
f"**User:** `{matrix_user}` (KC Username: `{kc_username}`)\n" f"**User:** `{matrix_user}` (KC Username: `{kc_username}`)\n"
@ -144,7 +190,6 @@ async def on_message(client: AsyncClient, room: MatrixRoom, event: RoomMessageTe
) )
if isinstance(res, RoomSendResponse): if isinstance(res, RoomSendResponse):
# Store pending request keyed by the admin message event_id
pending_requests[res.event_id] = { pending_requests[res.event_id] = {
"matrix_user": matrix_user, "matrix_user": matrix_user,
"kc_username": kc_username, "kc_username": kc_username,
@ -152,7 +197,6 @@ async def on_message(client: AsyncClient, room: MatrixRoom, event: RoomMessageTe
"origin_room_id": room.room_id, "origin_room_id": room.room_id,
} }
# Confirm request receipt to the user
await send_message_safe( await send_message_safe(
client, client,
room_id=room.room_id, room_id=room.room_id,
@ -245,15 +289,26 @@ async def on_reaction(client: AsyncClient, room: MatrixRoom, event: ReactionEven
return # Ignore other reactions return # Ignore other reactions
# Update the original admin room message to reflect resolution status # Update the original admin room message to reflect resolution status
fallback_text = f"* {status_text}"
await send_message_safe( await send_message_safe(
client, client,
room_id=ADMIN_ROOM_ID, room_id=ADMIN_ROOM_ID,
content={ content={
"msgtype": "m.text",
"body": fallback_text,
"format": "org.matrix.custom.html",
"formatted_body": fallback_text.replace("\n", "<br>"),
"m.new_content": {
"msgtype": "m.text", "msgtype": "m.text",
"body": status_text, "body": status_text,
"format": "org.matrix.custom.html", "format": "org.matrix.custom.html",
"formatted_body": status_text.replace("\n", "<br>"), "formatted_body": status_text.replace("\n", "<br>"),
"m.relates_to": {"rel_type": "m.replace", "event_id": target_event_id}, },
"m.relates_to": {
"rel_type": "m.replace",
"event_id": target_event_id
},
}, },
) )