From d3ac4726e2022862ca1b4081cead6708eafb46ca Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Thu, 8 Jan 2026 00:26:20 -0300 Subject: [PATCH] comms: rename guests via MAS admin sessions --- services/communication/guest-name-job.yaml | 105 ++++++++++++++------- 1 file changed, 72 insertions(+), 33 deletions(-) diff --git a/services/communication/guest-name-job.yaml b/services/communication/guest-name-job.yaml index 5e9a885..3e101f8 100644 --- a/services/communication/guest-name-job.yaml +++ b/services/communication/guest-name-job.yaml @@ -125,49 +125,88 @@ spec: r.raise_for_status() return r.json()["room_id"] - def list_guests(token): + def room_members(token, room_id): + headers = {"Authorization": f"Bearer {token}"} + r = requests.get(f"{BASE}/_matrix/client/v3/rooms/{urllib.parse.quote(room_id)}/members", headers=headers) + r.raise_for_status() + members = set() + existing_names = set() + for ev in r.json().get("chunk", []): + user_id = ev.get("state_key") + if user_id: + members.add(user_id) + disp = (ev.get("content") or {}).get("displayname") + if disp: + existing_names.add(disp) + return members, existing_names + + def mas_list_users(token): headers = {"Authorization": f"Bearer {token}"} users = [] - existing_names = set() - from_token = None + cursor = None while True: - url = f"{BASE}/_synapse/admin/v2/users?local=true&deactivated=false&limit=100" - if from_token: - url += f"&from={from_token}" - res = requests.get(url, headers=headers) - res.raise_for_status() - data = res.json() - for u in data.get("users", []): - disp = u.get("displayname", "") - if disp: - existing_names.add(disp) - if u.get("is_guest") and (not disp or disp.isdigit()): - users.append(u["name"]) - from_token = data.get("next_token") - if not from_token: + url = f"{MAS_ADMIN_API_BASE}/users?page[size]=100" + if cursor: + url += f"&page[after]={urllib.parse.quote(cursor)}" + r = requests.get(url, headers=headers, timeout=30) + r.raise_for_status() + data = r.json().get("data", []) + if not data: break - return users, existing_names + users.extend(data) + cursor = data[-1].get("meta", {}).get("page", {}).get("cursor") + if not cursor: + break + return users - def set_displayname(token, room_id, user_id, name): + def user_id_for_username(username): + return f"@{username}:live.bstein.dev" + + def get_displayname(token, user_id): + headers = {"Authorization": f"Bearer {token}"} + r = requests.get(f"{BASE}/_matrix/client/v3/profile/{urllib.parse.quote(user_id)}", headers=headers) + r.raise_for_status() + return r.json().get("displayname") + + def set_displayname(token, room_id, user_id, name, in_room): headers = {"Authorization": f"Bearer {token}"} payload = {"displayname": name} - # Update global profile - r = requests.put(f"{BASE}/_matrix/client/v3/profile/{urllib.parse.quote(user_id)}/displayname", headers=headers, json=payload) + r = requests.put( + f"{BASE}/_matrix/client/v3/profile/{urllib.parse.quote(user_id)}/displayname", + headers=headers, + json=payload, + ) r.raise_for_status() - # Update Othrys member event so clients see the change quickly + if not in_room: + return state_url = f"{BASE}/_matrix/client/v3/rooms/{urllib.parse.quote(room_id)}/state/m.room.member/{urllib.parse.quote(user_id)}" - r2 = requests.get(state_url, headers=headers) - content = r2.json() if r2.status_code == 200 else {"membership": "join"} - content["displayname"] = name - requests.put(state_url, headers=headers, json=content) + content = {"membership": "join", "displayname": name} + requests.put(state_url, headers=headers, json=content, timeout=30) admin_token = mas_admin_token() seeder_id = mas_user_id(admin_token, SEEDER_USER) - token, session_id = mas_personal_session(admin_token, seeder_id) + seeder_token, seeder_session = mas_personal_session(admin_token, seeder_id) try: - room_id = resolve_alias(token, ROOM_ALIAS) - guests, existing = list_guests(token) - for g in guests: + room_id = resolve_alias(seeder_token, ROOM_ALIAS) + members, existing = room_members(seeder_token, room_id) + finally: + mas_revoke_session(admin_token, seeder_session) + + users = mas_list_users(admin_token) + for user in users: + attrs = user.get("attributes") or {} + username = attrs.get("username") or "" + legacy_guest = attrs.get("legacy_guest") + if not username: + continue + if not (legacy_guest or username.isdigit() or username.startswith("guest-")): + continue + user_id = user_id_for_username(username) + access_token, session_id = mas_personal_session(admin_token, user["id"]) + try: + display = get_displayname(access_token, user_id) + if display and (not display.isdigit()) and (not display.startswith("guest-")): + continue new = None for _ in range(30): candidate = f"{random.choice(ADJ)}-{random.choice(NOUN)}" @@ -177,7 +216,7 @@ spec: break if not new: continue - set_displayname(token, room_id, g, new) - finally: - mas_revoke_session(admin_token, session_id) + set_displayname(access_token, room_id, user_id, new, user_id in members) + finally: + mas_revoke_session(admin_token, session_id) PY