Compare commits
2 commits
4a3f97ef2a
...
8018958090
Author | SHA1 | Date | |
---|---|---|---|
8018958090 | |||
f9bd2a46a1 |
3 changed files with 85 additions and 53 deletions
|
@ -9,9 +9,10 @@ A Matrix-Bot which is able to:
|
||||||
|
|
||||||
1. create default rooms
|
1. create default rooms
|
||||||
2. load users and their groups from matrix and ldap
|
2. load users and their groups from matrix and ldap
|
||||||
3. create rooms for all projects which have `sophomorixMailList=TRUE`
|
3. create rooms for all projects which end in `lab`
|
||||||
4. load all rooms (created by the bot) and their power levels from matrix
|
4. create rooms for all projects which have `sophomorixMailList=TRUE`
|
||||||
5. sync user memberships and power levels
|
5. load all rooms (created by the bot) and their power levels from matrix
|
||||||
|
6. sync user memberships and power levels
|
||||||
|
|
||||||
# How to use
|
# How to use
|
||||||
|
|
||||||
|
|
92
src/main.py
92
src/main.py
|
@ -35,72 +35,76 @@ class MlmMatrixBot:
|
||||||
|
|
||||||
async def run(self):
|
async def run(self):
|
||||||
|
|
||||||
|
self._current_log_step = 0
|
||||||
logging.info("==> Sync started <==")
|
logging.info("==> Sync started <==")
|
||||||
|
|
||||||
await self._create_default_rooms()
|
await self._create_default_rooms()
|
||||||
|
|
||||||
current_users, all_projects = await self._load_users_and_groups()
|
current_users, all_projects = await self._load_users_and_groups()
|
||||||
|
|
||||||
await self._create_project_rooms(all_projects)
|
await self._create_project_rooms(all_projects)
|
||||||
|
|
||||||
|
await self._create_lab_rooms()
|
||||||
|
|
||||||
logging.info("= (4/5) Loading current rooms =")
|
self._log_step("Loading current rooms")
|
||||||
matrix_rooms = await self._get_managed_rooms(with_power_levels=True)
|
matrix_rooms = await self._get_managed_rooms(with_power_levels=True)
|
||||||
matrix_room_ids = list(map(lambda room: room['id'], matrix_rooms))
|
matrix_room_ids = list(map(lambda room: room['id'], matrix_rooms))
|
||||||
room_name_id_map = {}
|
room_name_id_map = {}
|
||||||
room_id_map = {
|
room_id_map = {}
|
||||||
self._config['MATRIX_SPACE_ID']: {
|
|
||||||
'name': "default space",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for room in matrix_rooms:
|
for room in matrix_rooms:
|
||||||
room_name_id_map[room['name']] = room['id']
|
room_name_id_map[room['name']] = room['id']
|
||||||
room_id_map[room['id']] = room
|
room_id_map[room['id']] = room
|
||||||
|
|
||||||
logging.info("= (5/5) Syncing user memberships =")
|
self._log_step("Syncing user memberships")
|
||||||
for user in current_users:
|
for user in current_users:
|
||||||
if not "mlm" in user['groups']:
|
if not "mlm" in user['groups']:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# default groups and space
|
# default groups and space
|
||||||
rooms_to_join = self._get_default_rooms() + [self._config["MATRIX_SPACE_ID"]]
|
rooms_to_join = self._get_default_rooms(
|
||||||
|
) + [self._config["MATRIX_SPACE_ID"]]
|
||||||
# projects
|
# projects
|
||||||
rooms_to_join += list(map(lambda group: group.replace('p_', ''), filter(lambda project: project.startswith("p_"), user['groups'])))
|
rooms_to_join += list(map(lambda group: group.replace('p_', ''),
|
||||||
|
filter(lambda project: project.startswith("p_"), user['groups'])))
|
||||||
# resolve names to ids
|
# resolve names to ids
|
||||||
rooms_to_join = list(map(
|
rooms_to_join = list(map(
|
||||||
lambda room: room if room.startswith("!") else room_name_id_map[room],
|
lambda room: room if room.startswith(
|
||||||
|
"!") else room_name_id_map[room],
|
||||||
rooms_to_join))
|
rooms_to_join))
|
||||||
|
|
||||||
logging.info(f" \t* Syncing user {user['username']}")
|
logging.info(f" \t* Syncing user {user['username']}")
|
||||||
for room_id in rooms_to_join:
|
for room_id in rooms_to_join:
|
||||||
# set power level
|
# set power level
|
||||||
if room_id in user['rooms']:
|
if room_id in user['rooms']:
|
||||||
if ( user['matrix_username'] in room_id_map[room_id]['power_levels'] and room_id_map[room_id]['power_levels'][user['matrix_username']] != user['power_level']) or (user['matrix_username'] not in room_id_map[room_id]['power_levels'] and user['power_level'] != 0):
|
|
||||||
logging.info(f"\t\t* Setting power level in {room_id_map[room_id]['name']} to {user['power_level']}")
|
|
||||||
await self._matrixHelper.set_user_power_level_in_room(user['matrix_username'], room_id, user['power_level'])
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
logging.info(f"\t* joining {room_id_map[room_id]['name']}")
|
logging.info(f"\t\t* joining {room_id_map[room_id]['name']}")
|
||||||
await self._matrixHelper.add_user_to_room(user['matrix_username'], room_id)
|
await self._matrixHelper.add_user_to_room(user['matrix_username'], room_id)
|
||||||
if user['power_level'] != 0:
|
|
||||||
logging.info(f"\t\t* Setting power level in {room_id_map[room_id]['name']} to {user['power_level']}")
|
|
||||||
await self._matrixHelper.set_user_power_level_in_room(user['matrix_username'], room_id, user['power_level'])
|
|
||||||
|
|
||||||
|
|
||||||
# remove from rooms that are not in the groups
|
# remove from rooms that are not in the groups
|
||||||
rooms_to_be_left = list(filter(lambda room: room in matrix_room_ids and not room in rooms_to_join, user['rooms']))
|
rooms_to_be_left = list(filter(
|
||||||
|
lambda room: room in matrix_room_ids and not room in rooms_to_join, user['rooms']))
|
||||||
for room_id in rooms_to_be_left:
|
for room_id in rooms_to_be_left:
|
||||||
logging.info(f"\t* leaving {room_id_map[room_id]['name']}")
|
logging.info(f"\t\t* leaving {room_id_map[room_id]['name']}")
|
||||||
await self._matrixHelper.remove_user_from_room(
|
await self._matrixHelper.remove_user_from_room(
|
||||||
user['matrix_username'], room_id)
|
user['matrix_username'], room_id)
|
||||||
|
|
||||||
|
# update power levels
|
||||||
|
for room in matrix_rooms:
|
||||||
|
if not room['id'] in rooms_to_join and not room['id'] in user['rooms']:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if (user['matrix_username'] in room['power_levels'] and room['power_levels'][user['matrix_username']] != user['power_level']) or (user['matrix_username'] not in room['power_levels'] and user['power_level'] != 0):
|
||||||
|
logging.info(
|
||||||
|
f"\t\t* Setting power level in {room['name']} to {user['power_level']}")
|
||||||
|
await self._matrixHelper.set_user_power_level_in_room(user['matrix_username'], room['id'], user['power_level'])
|
||||||
|
|
||||||
logging.info("==> Sync finished <==")
|
logging.info("==> Sync finished <==")
|
||||||
|
|
||||||
async def _create_default_rooms(self):
|
async def _create_default_rooms(self):
|
||||||
# create default rooms
|
# create default rooms
|
||||||
logging.info("= (1/5) Creating default rooms =")
|
self._log_step("Creating default rooms")
|
||||||
await self._create_rooms(self._get_default_rooms())
|
await self._create_rooms(self._get_default_rooms())
|
||||||
|
|
||||||
async def _load_users_and_groups(self):
|
async def _load_users_and_groups(self):
|
||||||
|
@ -121,7 +125,7 @@ class MlmMatrixBot:
|
||||||
'p_3d-printing'
|
'p_3d-printing'
|
||||||
])
|
])
|
||||||
"""
|
"""
|
||||||
logging.info("= (2/5) Loading users and their groups =")
|
self._log_step("Loading users and their groups")
|
||||||
matrix_users = await self._matrixHelper.get_users()
|
matrix_users = await self._matrixHelper.get_users()
|
||||||
current_users = []
|
current_users = []
|
||||||
all_projects = []
|
all_projects = []
|
||||||
|
@ -134,15 +138,17 @@ class MlmMatrixBot:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
rc, raw_groups = self._ldapHelper.search(f"(&(member:1.2.840.113556.1.4.1941:={ldap_user[0]['distinguishedName']})(|(sophomorixType=adminclass)(sophomorixType=project)))", [
|
rc, raw_groups = self._ldapHelper.search(f"(&(member:1.2.840.113556.1.4.1941:={ldap_user[0]['distinguishedName']})(|(sophomorixType=adminclass)(sophomorixType=project)))", [
|
||||||
'sAMAccountName', 'sophomorixType', 'sophomorixMailList'])
|
'sAMAccountName', 'sophomorixType', 'sophomorixMailList'])
|
||||||
|
|
||||||
if not rc:
|
if not rc:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
all_groups = list(map(lambda group: group['sAMAccountName'], raw_groups))
|
all_groups = list(
|
||||||
|
map(lambda group: group['sAMAccountName'], raw_groups))
|
||||||
power_level = 100 if self._config['ADMIN_GROUP'] in all_groups else 50 if self._config['MODERATOR_GROUP'] in all_groups else 0
|
power_level = 100 if self._config['ADMIN_GROUP'] in all_groups else 50 if self._config['MODERATOR_GROUP'] in all_groups else 0
|
||||||
|
|
||||||
groups = list(filter(lambda group: group['sophomorixMailList'] == 'TRUE' or group['sophomorixType'] == "adminclass", raw_groups))
|
groups = list(filter(lambda group: group['sophomorixMailList'] ==
|
||||||
|
'TRUE' or group['sophomorixType'] == "adminclass", raw_groups))
|
||||||
|
|
||||||
all_projects.extend(list(map(lambda group: group['sAMAccountName'], filter(
|
all_projects.extend(list(map(lambda group: group['sAMAccountName'], filter(
|
||||||
lambda group: 'sophomorixType' in group and group['sophomorixType'] == 'project', groups))))
|
lambda group: 'sophomorixType' in group and group['sophomorixType'] == 'project', groups))))
|
||||||
|
@ -163,16 +169,28 @@ class MlmMatrixBot:
|
||||||
return current_users, all_projects
|
return current_users, all_projects
|
||||||
|
|
||||||
async def _create_project_rooms(self, projects):
|
async def _create_project_rooms(self, projects):
|
||||||
logging.info("= (3/5) Creating project rooms =")
|
self._log_step("Creating project rooms")
|
||||||
projects = list(map(lambda project: project.replace('p_', ''), projects))
|
projects = list(
|
||||||
|
map(lambda project: project.replace('p_', ''), projects))
|
||||||
await self._create_rooms(projects)
|
await self._create_rooms(projects)
|
||||||
|
|
||||||
async def _create_rooms(self, rooms):
|
async def _create_lab_rooms(self):
|
||||||
|
self._log_step("Creating lab rooms")
|
||||||
|
|
||||||
|
rc, labs = self._ldapHelper.search(
|
||||||
|
f"(&(sophomorixType=project)(sAMAccountName=*lab))", ['sAMAccountName'])
|
||||||
|
if not rc:
|
||||||
|
return
|
||||||
|
|
||||||
|
labs = list(map(lambda lab: lab['sAMAccountName'].replace("p_", "").replace("lab", "-lab"), labs))
|
||||||
|
await self._create_rooms(labs, joinable_for_space_members=True)
|
||||||
|
|
||||||
|
async def _create_rooms(self, rooms, joinable_for_space_members=False, suggested=False):
|
||||||
matrix_room_names = list(map(lambda room: room['name'], await self._get_managed_rooms()))
|
matrix_room_names = list(map(lambda room: room['name'], await self._get_managed_rooms()))
|
||||||
for room in rooms:
|
for room in rooms:
|
||||||
if room not in matrix_room_names:
|
if room not in matrix_room_names:
|
||||||
logging.info(f"* Creating room {room}")
|
logging.info(f"* Creating room {room}")
|
||||||
await self._matrixHelper.create_room(room, room)
|
await self._matrixHelper.create_room(room, room, joinable_for_space_members=joinable_for_space_members, suggested=True)
|
||||||
|
|
||||||
async def _get_managed_rooms(self, with_power_levels=False):
|
async def _get_managed_rooms(self, with_power_levels=False):
|
||||||
matrix_rooms = await self._matrixHelper.get_rooms()
|
matrix_rooms = await self._matrixHelper.get_rooms()
|
||||||
|
@ -184,7 +202,8 @@ class MlmMatrixBot:
|
||||||
|
|
||||||
if with_power_levels:
|
if with_power_levels:
|
||||||
for i in range(len(matrix_rooms)):
|
for i in range(len(matrix_rooms)):
|
||||||
logging.info(f"\t* Getting power levels of room {matrix_rooms[i]['name']}")
|
logging.info(
|
||||||
|
f"\t* Getting power levels of room {matrix_rooms[i]['name']}")
|
||||||
matrix_rooms[i]['power_levels'] = await self._matrixHelper.get_room_power_levels(matrix_rooms[i]['id'])
|
matrix_rooms[i]['power_levels'] = await self._matrixHelper.get_room_power_levels(matrix_rooms[i]['id'])
|
||||||
|
|
||||||
return matrix_rooms
|
return matrix_rooms
|
||||||
|
@ -192,6 +211,10 @@ class MlmMatrixBot:
|
||||||
def _get_default_rooms(self):
|
def _get_default_rooms(self):
|
||||||
return self._config['DEFAULT_ROOMS'].split(',')
|
return self._config['DEFAULT_ROOMS'].split(',')
|
||||||
|
|
||||||
|
def _log_step(self, message):
|
||||||
|
logging.info(f"= ({self._current_log_step}/6) {message} =")
|
||||||
|
self._current_log_step += 1
|
||||||
|
|
||||||
def _readConfig(self):
|
def _readConfig(self):
|
||||||
requiredConfigKeys = [
|
requiredConfigKeys = [
|
||||||
'MATRIX_BOT_LDAP_URI',
|
'MATRIX_BOT_LDAP_URI',
|
||||||
|
@ -216,7 +239,8 @@ class MlmMatrixBot:
|
||||||
|
|
||||||
for configKey in requiredConfigKeys:
|
for configKey in requiredConfigKeys:
|
||||||
if configKey not in os.environ:
|
if configKey not in os.environ:
|
||||||
logging.error(f"Required environment value {configKey} is not set")
|
logging.error(
|
||||||
|
f"Required environment value {configKey} is not set")
|
||||||
sys.exit()
|
sys.exit()
|
||||||
config[configKey.replace('MATRIX_BOT_', '')
|
config[configKey.replace('MATRIX_BOT_', '')
|
||||||
] = os.environ[configKey]
|
] = os.environ[configKey]
|
||||||
|
|
|
@ -57,27 +57,16 @@ class MatrixHelper:
|
||||||
async def logout(self):
|
async def logout(self):
|
||||||
await self._client.close()
|
await self._client.close()
|
||||||
|
|
||||||
async def create_room(self, name, alias, joinable_for_space_members=False):
|
async def create_room(self, name, alias, joinable_for_space_members=False, suggested=False):
|
||||||
# we not only need to create the room but also have to add it to the space
|
# we not only need to create the room but also have to add it to the space
|
||||||
|
via_domain = self._space_id.split(":")[1]
|
||||||
initial_state = [
|
initial_state = [
|
||||||
{
|
{
|
||||||
"type": "m.space.parent",
|
"type": "m.space.parent",
|
||||||
"state_key": self._space_id,
|
"state_key": self._space_id,
|
||||||
"content": {
|
"content": {
|
||||||
"canonical": True,
|
"canonical": True,
|
||||||
"via": [self._space_id.split(":")[1]],
|
"via": [via_domain],
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "m.room.join_rules",
|
|
||||||
"content": {
|
|
||||||
"join_rule": "restricted",
|
|
||||||
"allow": [
|
|
||||||
{
|
|
||||||
"type": "m.room_membership",
|
|
||||||
"room_id": self._space_id
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -95,16 +84,34 @@ class MatrixHelper:
|
||||||
]
|
]
|
||||||
|
|
||||||
if joinable_for_space_members:
|
if joinable_for_space_members:
|
||||||
initial_state.append({
|
initial_state.insert(0, {
|
||||||
"type": "m.room.guest_access",
|
"type": "m.room.guest_access",
|
||||||
"state_key": "",
|
"state_key": "",
|
||||||
"content": {"guest_access": "can_join"}
|
"content": {"guest_access": "can_join"}
|
||||||
})
|
})
|
||||||
|
initial_state.insert(2, {
|
||||||
|
"type": "m.room.join_rules",
|
||||||
|
"content": {
|
||||||
|
"join_rule": "restricted",
|
||||||
|
"allow": [
|
||||||
|
{
|
||||||
|
"type": "m.room_membership",
|
||||||
|
"room_id": self._space_id
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
room = await self._client.room_create(visibility=nio.RoomVisibility.private, name=name, alias=alias, federate=False, initial_state=initial_state)
|
room = await self._client.room_create(visibility=nio.RoomVisibility.private, name=name, alias=alias, federate=False, initial_state=initial_state)
|
||||||
|
|
||||||
assert room.room_id is not None
|
assert room.room_id is not None
|
||||||
|
|
||||||
|
# add as child room
|
||||||
|
state_update = await self._client.room_put_state(self._space_id, "m.space.child", {
|
||||||
|
"suggested": suggested,
|
||||||
|
"via": [via_domain],
|
||||||
|
}, state_key=room.room_id)
|
||||||
|
assert state_update.event_id is not None
|
||||||
|
|
||||||
return room
|
return room
|
||||||
|
|
||||||
async def get_rooms(self):
|
async def get_rooms(self):
|
||||||
|
|
Loading…
Reference in a new issue