Last active
September 4, 2020 14:08
-
-
Save SpiritCroc/2905c50887a9de5790d790a943b15c0b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# A script to set room-specific display names for your Matrix (-> https://matrix.org) account | |
# for all your joined rooms, depending on whether the room members are personal contacts or not. | |
# Requires the matrix-nio library. | |
# | |
# Before execution, adapt at least following values: | |
# - MY_HOMESERVER | |
# - MY_MX_ID | |
# - PERSONAL_SERVERS | |
# - PERSONAL_CONTACTS | |
# - PERSONAL_DISPLAY_NAME | |
# - DEFAULT_DISPLAY_NAME | |
import asyncio | |
import getpass | |
from colorama import Fore, Style | |
from nio import AsyncClient, MatrixRoom, RoomGetStateEventError | |
# Change these | |
MY_HOMESERVER = "https://myhomeserver.com" | |
MY_MX_ID = "@mxid:myhomeserver.com" | |
# All users in personal servers are automatically regarded as personal contacts | |
PERSONAL_SERVERS = [ | |
"myhomeserver.com" | |
] | |
PERSONAL_CONTACTS = [ | |
MY_MX_ID, | |
"@friend1:friend1-homeserver.org", | |
"@friend2:friend2-hs.com", | |
# + automatically include all from PERSONAL_SERVERS | |
] | |
# Name to use in rooms where only personal contacts are (except empty rooms) | |
PERSONAL_DISPLAY_NAME = "My name for personal contacts" | |
# Default display name | |
DEFAULT_DISPLAY_NAME = "My name for the rest of the internet" | |
# Names not included here are assumed manually set and will not be touched | |
MANAGED_DISPLAY_NAMES = [ | |
PERSONAL_DISPLAY_NAME, | |
DEFAULT_DISPLAY_NAME, | |
] | |
SCRIPT_DEVICE_ID = "RENAMERSCR" | |
VERBOSE = False | |
class PlannedRename: | |
def __init__(self, room_id, room_name, old_name, new_name, old_avatar, new_avatar): | |
self.room_id = room_id | |
self.room_name = room_name | |
self.old_name = old_name | |
self.new_name = new_name | |
self.old_avatar = old_avatar | |
self.new_avatar = new_avatar | |
async def main(): | |
client = AsyncClient(MY_HOMESERVER, MY_MX_ID, SCRIPT_DEVICE_ID) | |
passwd = getpass.getpass("Password: ") | |
await client.login(passwd) | |
# Sync fetches rooms | |
await client.sync() | |
planned_renames = [] | |
max_room_name_len = 0 | |
for room in client.rooms.values(): | |
room_id = room.room_id | |
max_room_name_len = max(max_room_name_len, len(room.display_name)) | |
myroomnick = room.user_name(MY_MX_ID) | |
myavatarurl = room.avatar_url(MY_MX_ID) | |
nick_change_allowed = myroomnick in MANAGED_DISPLAY_NAMES | |
print("ROOM {} {} {}".format(room.display_name, room_id, myroomnick if nick_change_allowed else (Fore.MAGENTA + myroomnick + Style.RESET_ALL))) | |
if not nick_change_allowed: | |
print(" => skip") | |
continue | |
member_response = await client.joined_members(room_id = room_id) | |
members = member_response.members | |
if len(members) <= 1: | |
print(" Ignore: {} members".format(len(members))) | |
room_is_personal = True | |
for member in members: | |
member_name = member.display_name | |
member_id = member.user_id | |
whitelisted = False | |
for allowed_server in PERSONAL_SERVERS: | |
if member_id.count(":") == 1 and member_id.endswith(":{}".format(allowed_server)): | |
whitelisted = True | |
break | |
if not whitelisted: | |
whitelisted = member_id in PERSONAL_CONTACTS | |
if VERBOSE: | |
print(" {} {} {}".format("ok" if whitelisted else "--", member_name, member_id)) | |
room_is_personal = room_is_personal and whitelisted | |
if room_is_personal: | |
new_name = PERSONAL_DISPLAY_NAME | |
else: | |
new_name = DEFAULT_DISPLAY_NAME | |
# TODO support avatar changes | |
if myroomnick == new_name: | |
print(" => keep {}".format(myroomnick)) | |
else: | |
print(f" => {Fore.YELLOW}{myroomnick}{Style.RESET_ALL} -> {Fore.CYAN}{new_name}{Style.RESET_ALL}") | |
planned_renames.append(PlannedRename(room_id=room_id, room_name=room.display_name, old_name=myroomnick, new_name=new_name, old_avatar=myavatarurl, new_avatar=myavatarurl)) | |
print("-"*42) | |
print("Planned renames:") | |
room_format = "{{:<{}}}".format(max_room_name_len) | |
for pr in planned_renames: | |
room_name = room_format.format(pr.room_name) | |
print(f"{room_name} | {pr.old_name} -> {pr.new_name}") | |
print("-"*42) | |
input("Enter to rename") | |
for pr in planned_renames: | |
room_id = pr.room_id | |
# Compare https://github.com/matrix-org/matrix-react-sdk/blob/7c4a84aae0b764842fadd38237c1a857437c4f51/src/SlashCommands.tsx#L274 | |
# https://github.com/matrix-org/matrix-doc/blob/8eb1c531442093d239ab35027d784c4d9cfc8ac9/specification/client_server_api.rst#L1975 | |
# https://github.com/matrix-org/matrix-doc/blob/9281d0ca13c39b83b8bbba184c8887d3d4faf045/event-schemas/schema/m.room.member | |
# https://github.com/matrix-org/matrix-doc/blob/370ae8b9fe873b3ce061e4a8dbd7cf836388d640/event-schemas/examples/m.room.member | |
# https://github.com/poljar/matrix-nio/blob/41636f04c14ffede01cf31abc309615b16ac949b/nio/client/async_client.py#L1570 | |
# TODO support avatar changes | |
content = { | |
"membership": "join", | |
"displayname": pr.new_name | |
#"avatar_url": pr.new_avatar | |
} | |
print(f"{pr.room_name}: {content}") | |
result = await client.room_put_state(room_id = room_id, event_type = "m.room.member", content = content, state_key = MY_MX_ID) | |
if VERBOSE: | |
print(result) | |
await client.logout() | |
await client.close() | |
asyncio.get_event_loop().run_until_complete(main()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment