From 93a2e2436d16c1c68859e0abb0bc7619d24b3093 Mon Sep 17 00:00:00 2001 From: Emmy D'Anello Date: Sat, 13 Jan 2024 16:46:19 +0100 Subject: [PATCH] Drop Matrix support Signed-off-by: Emmy D'Anello --- README.md | 1 - .../commands/fix_matrix_channels.py | 477 ------------------ participation/models.py | 11 - participation/signals.py | 3 +- participation/views.py | 15 +- registration/auth.py | 17 - registration/fixtures/initial.json | 26 - registration/models.py | 4 - requirements.txt | 2 - tfjm.cron | 3 - tfjm/matrix.py | 432 ---------------- tfjm/settings.py | 3 - 12 files changed, 3 insertions(+), 991 deletions(-) delete mode 100644 participation/management/commands/fix_matrix_channels.py delete mode 100644 registration/auth.py delete mode 100644 registration/fixtures/initial.json delete mode 100644 tfjm/matrix.py diff --git a/README.md b/README.md index ef82004..8a643e2 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,6 @@ SERVER_EMAIL=contact@tfjm.org # Adresse e-mail expéditrice SYMPA_URL=lists.example.com # Serveur Sympa à utiliser SYMPA_EMAIL= # Adresse e-mail du compte administrateur de Sympa SYMPA_PASSWORD= # Mot de passe du compte administrateur de Sympa -SYNAPSE_PASSWORD= # Mot de passe du robot Matrix ``` Si le type de base de données sélectionné est SQLite, la variable `DJANGO_DB_HOST` sera utilisée en guise de chemin vers diff --git a/participation/management/commands/fix_matrix_channels.py b/participation/management/commands/fix_matrix_channels.py deleted file mode 100644 index 80ad1e9..0000000 --- a/participation/management/commands/fix_matrix_channels.py +++ /dev/null @@ -1,477 +0,0 @@ -# Copyright (C) 2020 by Animath -# SPDX-License-Identifier: GPL-3.0-or-later - -import asyncio -import os - -from django.core.management import BaseCommand -from django.utils.http import urlencode -from django.utils.translation import activate -from participation.models import Team, Tournament -from registration.models import Registration, VolunteerRegistration -from tfjm.matrix import Matrix, RoomPreset, RoomVisibility - - -class Command(BaseCommand): - def handle(self, *args, **options): # noqa: C901 - activate("fr") - - async def main(): - await Matrix.set_display_name("Bot du TFJM²") - - if not os.getenv("SYNAPSE_PASSWORD"): - avatar_uri = "plop" - else: # pragma: no cover - if not os.path.isfile(".matrix_avatar"): - avatar_uri = await Matrix.get_avatar() - if isinstance(avatar_uri, str): - with open(".matrix_avatar", "w") as f: - f.write(avatar_uri) - else: - stat_file = os.stat("tfjm/static/logo.png") - with open("tfjm/static/logo.png", "rb") as f: - resp = (await Matrix.upload(f, filename="../../../tfjm/static/logo.png", content_type="image/png", - filesize=stat_file.st_size))[0][0] - avatar_uri = resp.content_uri - with open(".matrix_avatar", "w") as f: - f.write(avatar_uri) - await Matrix.set_avatar(avatar_uri) - - with open(".matrix_avatar", "r") as f: - avatar_uri = f.read().rstrip(" \t\r\n") - - # Create basic channels - if not await Matrix.resolve_room_alias("#aide-jurys-orgas:tfjm.org"): - await Matrix.create_room( - visibility=RoomVisibility.public, - alias="aide-jurys-orgas", - name="Aide jurys & orgas", - topic="Pour discuter de propblèmes d'organisation", - federate=False, - preset=RoomPreset.private_chat, - ) - - if not await Matrix.resolve_room_alias("#annonces:tfjm.org"): - await Matrix.create_room( - visibility=RoomVisibility.public, - alias="annonces", - name="Annonces", - topic="Informations importantes du TFJM²", - federate=False, - preset=RoomPreset.public_chat, - ) - - if not await Matrix.resolve_room_alias("#bienvenue:tfjm.org"): - await Matrix.create_room( - visibility=RoomVisibility.public, - alias="bienvenue", - name="Bienvenue", - topic="Bienvenue au TFJM² 2023 !", - federate=False, - preset=RoomPreset.public_chat, - ) - - if not await Matrix.resolve_room_alias("#bot:tfjm.org"): - await Matrix.create_room( - visibility=RoomVisibility.public, - alias="bot", - name="Bot", - topic="Vive les r0b0ts", - federate=False, - preset=RoomPreset.public_chat, - ) - - if not await Matrix.resolve_room_alias("#cno:tfjm.org"): - await Matrix.create_room( - visibility=RoomVisibility.public, - alias="cno", - name="CNO", - topic="Channel des dieux", - federate=False, - preset=RoomPreset.private_chat, - ) - - if not await Matrix.resolve_room_alias("#dev-bot:tfjm.org"): - await Matrix.create_room( - visibility=RoomVisibility.public, - alias="dev-bot", - name="Bot - développement", - topic="Vive le bot", - federate=False, - preset=RoomPreset.private_chat, - ) - - if not await Matrix.resolve_room_alias("#faq:tfjm.org"): - await Matrix.create_room( - visibility=RoomVisibility.public, - alias="faq", - name="FAQ", - topic="Posez toutes vos questions ici !", - federate=False, - preset=RoomPreset.public_chat, - ) - - if not await Matrix.resolve_room_alias("#flood:tfjm.org"): - await Matrix.create_room( - visibility=RoomVisibility.public, - alias="flood", - name="Flood", - topic="Discutez de tout et de rien !", - federate=False, - preset=RoomPreset.public_chat, - ) - - if not await Matrix.resolve_room_alias("#je-cherche-une-equipe:tfjm.org"): - await Matrix.create_room( - visibility=RoomVisibility.public, - alias="je-cherche-une-equipe", - name="Je cherche une équipe", - topic="Le Tinder du TFJM²", - federate=False, - preset=RoomPreset.public_chat, - ) - - # Setup avatars - await Matrix.set_room_avatar("#aide-jurys-orgas:tfjm.org", avatar_uri) - await Matrix.set_room_avatar("#annonces:tfjm.org", avatar_uri) - await Matrix.set_room_avatar("#bienvenue:tfjm.org", avatar_uri) - await Matrix.set_room_avatar("#bot:tfjm.org", avatar_uri) - await Matrix.set_room_avatar("#cno:tfjm.org", avatar_uri) - await Matrix.set_room_avatar("#dev-bot:tfjm.org", avatar_uri) - await Matrix.set_room_avatar("#faq:tfjm.org", avatar_uri) - await Matrix.set_room_avatar("#flood:tfjm.org", avatar_uri) - await Matrix.set_room_avatar("#je-cherche-une-equipe:tfjm.org", avatar_uri) - - # Read-only channels - await Matrix.set_room_power_level_event("#annonces:tfjm.org", "events_default", 50) - await Matrix.set_room_power_level_event("#bienvenue:tfjm.org", "events_default", 50) - - # Invite everyone to public channels - for r in Registration.objects.all(): - await Matrix.invite("#annonces:tfjm.org", f"@{r.matrix_username}:tfjm.org") - await Matrix.invite("#bienvenue:tfjm.org", f"@{r.matrix_username}:tfjm.org") - await Matrix.invite("#bot:tfjm.org", f"@{r.matrix_username}:tfjm.org") - await Matrix.invite("#faq:tfjm.org", f"@{r.matrix_username}:tfjm.org") - await Matrix.invite("#flood:tfjm.org", f"@{r.matrix_username}:tfjm.org") - await Matrix.invite("#je-cherche-une-equipe:tfjm.org", - f"@{r.matrix_username}:tfjm.org") - self.stdout.write(f"Invite {r} in most common channels...") - - # Volunteers have access to the help channel - for volunteer in VolunteerRegistration.objects.all(): - await Matrix.invite("#aide-jurys-orgas:tfjm.org", f"@{volunteer.matrix_username}:tfjm.org") - self.stdout.write(f"Invite {volunteer} in #aide-jury-orgas...") - - # Admins are admins - for admin in VolunteerRegistration.objects.filter(admin=True).all(): - self.stdout.write(f"Invite {admin} in #cno and #dev-bot...") - await Matrix.invite("#cno:tfjm.org", f"@{admin.matrix_username}:tfjm.org") - await Matrix.invite("#dev-bot:tfjm.org", f"@{admin.matrix_username}:tfjm.org") - - self.stdout.write(f"Give admin permissions for {admin}...") - await Matrix.set_room_power_level("#aide-jurys-orgas:tfjm.org", - f"@{admin.matrix_username}:tfjm.org", 95) - await Matrix.set_room_power_level("#annonces:tfjm.org", - f"@{admin.matrix_username}:tfjm.org", 95) - await Matrix.set_room_power_level("#bienvenue:tfjm.org", - f"@{admin.matrix_username}:tfjm.org", 95) - await Matrix.set_room_power_level("#bot:tfjm.org", - f"@{admin.matrix_username}:tfjm.org", 95) - await Matrix.set_room_power_level("#cno:tfjm.org", - f"@{admin.matrix_username}:tfjm.org", 95) - await Matrix.set_room_power_level("#dev-bot:tfjm.org", - f"@{admin.matrix_username}:tfjm.org", 95) - await Matrix.set_room_power_level("#faq:tfjm.org", - f"@{admin.matrix_username}:tfjm.org", 95) - await Matrix.set_room_power_level("#flood:tfjm.org", - f"@{admin.matrix_username}:tfjm.org", 95) - await Matrix.set_room_power_level("#je-cherche-une-equipe:tfjm.org", - f"@{admin.matrix_username}:tfjm.org", 95) - - # Create tournament-specific channels - for tournament in Tournament.objects.all(): - self.stdout.write(f"Managing tournament of {tournament.name}.") - - name = tournament.name - slug = name.lower().replace(" ", "-") - - if not await Matrix.resolve_room_alias(f"#annonces-{slug}:tfjm.org"): - await Matrix.create_room( - visibility=RoomVisibility.public, - alias=f"annonces-{slug}", - name=f"{name} - Annonces", - topic=f"Annonces du tournoi de {name}", - federate=False, - preset=RoomPreset.private_chat, - ) - - if not await Matrix.resolve_room_alias(f"#general-{slug}:tfjm.org"): - await Matrix.create_room( - visibility=RoomVisibility.public, - alias=f"general-{slug}", - name=f"{name} - Général", - topic=f"Accueil du tournoi de {name}", - federate=False, - preset=RoomPreset.private_chat, - ) - - if not await Matrix.resolve_room_alias(f"#flood-{slug}:tfjm.org"): - await Matrix.create_room( - visibility=RoomVisibility.public, - alias=f"flood-{slug}", - name=f"{name} - Flood", - topic=f"Discussion libre du tournoi de {name}", - federate=False, - preset=RoomPreset.private_chat, - ) - - if not await Matrix.resolve_room_alias(f"#jury-{slug}:tfjm.org"): - await Matrix.create_room( - visibility=RoomVisibility.public, - alias=f"jury-{slug}", - name=f"{name} - Jury", - topic=f"Discussion entre les orgas et jurys du tournoi de {name}", - federate=False, - preset=RoomPreset.private_chat, - ) - - if not await Matrix.resolve_room_alias(f"#orga-{slug}:tfjm.org"): - await Matrix.create_room( - visibility=RoomVisibility.public, - alias=f"orga-{slug}", - name=f"{name} - Organisateurs", - topic=f"Discussion entre les orgas du tournoi de {name}", - federate=False, - preset=RoomPreset.private_chat, - ) - - if not await Matrix.resolve_room_alias(f"#tirage-au-sort-{slug}:tfjm.org"): - await Matrix.create_room( - visibility=RoomVisibility.public, - alias=f"tirage-au-sort-{slug}", - name=f"{name} - Tirage au sort", - topic=f"Tirage au sort du tournoi de {name}", - federate=False, - preset=RoomPreset.private_chat, - ) - - # Setup avatars - await Matrix.set_room_avatar(f"#annonces-{slug}:tfjm.org", avatar_uri) - await Matrix.set_room_avatar(f"#flood-{slug}:tfjm.org", avatar_uri) - await Matrix.set_room_avatar(f"#general-{slug}:tfjm.org", avatar_uri) - await Matrix.set_room_avatar(f"#jury-{slug}:tfjm.org", avatar_uri) - await Matrix.set_room_avatar(f"#orga-{slug}:tfjm.org", avatar_uri) - await Matrix.set_room_avatar(f"#tirage-au-sort-{slug}:tfjm.org", avatar_uri) - - # Invite admins and give permissions - for admin in VolunteerRegistration.objects.filter(admin=True).all(): - self.stdout.write(f"Invite {admin} in all channels of the tournament {name}...") - await Matrix.invite(f"#annonces-{slug}:tfjm.org", f"@{admin.matrix_username}:tfjm.org") - await Matrix.invite(f"#flood-{slug}:tfjm.org", f"@{admin.matrix_username}:tfjm.org") - await Matrix.invite(f"#general-{slug}:tfjm.org", f"@{admin.matrix_username}:tfjm.org") - await Matrix.invite(f"#jury-{slug}:tfjm.org", f"@{admin.matrix_username}:tfjm.org") - await Matrix.invite(f"#orga-{slug}:tfjm.org", f"@{admin.matrix_username}:tfjm.org") - await Matrix.invite(f"#tirage-au-sort-{slug}:tfjm.org", f"@{admin.matrix_username}:tfjm.org") - - self.stdout.write(f"Give permissions to {admin} in all channels of the tournament {name}...") - await Matrix.set_room_power_level(f"#annonces-{slug}:tfjm.org", - f"@{admin.matrix_username}:tfjm.org", 95) - await Matrix.set_room_power_level(f"#flood-{slug}:tfjm.org", - f"@{admin.matrix_username}:tfjm.org", 95) - await Matrix.set_room_power_level(f"#general-{slug}:tfjm.org", - f"@{admin.matrix_username}:tfjm.org", 95) - await Matrix.set_room_power_level(f"#jury-{slug}:tfjm.org", - f"@{admin.matrix_username}:tfjm.org", 95) - await Matrix.set_room_power_level(f"#orga-{slug}:tfjm.org", - f"@{admin.matrix_username}:tfjm.org", 95) - await Matrix.set_room_power_level(f"#tirage-au-sort-{slug}:tfjm.org", - f"@{admin.matrix_username}:tfjm.org", 95) - - # Invite organizers and give permissions - for orga in tournament.organizers.all(): - self.stdout.write(f"Invite organizer {orga} in all channels of the tournament {name}...") - await Matrix.invite(f"#annonces-{slug}:tfjm.org", f"@{orga.matrix_username}:tfjm.org") - await Matrix.invite(f"#flood-{slug}:tfjm.org", f"@{orga.matrix_username}:tfjm.org") - await Matrix.invite(f"#general-{slug}:tfjm.org", f"@{orga.matrix_username}:tfjm.org") - await Matrix.invite(f"#jury-{slug}:tfjm.org", f"@{orga.matrix_username}:tfjm.org") - await Matrix.invite(f"#orga-{slug}:tfjm.org", f"@{orga.matrix_username}:tfjm.org") - await Matrix.invite(f"#tirage-au-sort-{slug}:tfjm.org", f"@{orga.matrix_username}:tfjm.org") - - if not orga.is_admin: - await Matrix.set_room_power_level(f"#annonces-{slug}:tfjm.org", - f"@{orga.matrix_username}:tfjm.org", 50) - await Matrix.set_room_power_level(f"#flood-{slug}:tfjm.org", - f"@{orga.matrix_username}:tfjm.org", 50) - await Matrix.set_room_power_level(f"#general-{slug}:tfjm.org", - f"@{orga.matrix_username}:tfjm.org", 50) - await Matrix.set_room_power_level(f"#jury-{slug}:tfjm.org", - f"@{orga.matrix_username}:tfjm.org", 50) - await Matrix.set_room_power_level(f"#orga-{slug}:tfjm.org", - f"@{orga.matrix_username}:tfjm.org", 50) - await Matrix.set_room_power_level(f"#tirage-au-sort-{slug}:tfjm.org", - f"@{orga.matrix_username}:tfjm.org", 50) - - # Invite participants - for participation in tournament.participations.filter(valid=True).all(): - for participant in participation.team.participants.all(): - self.stdout.write(f"Invite {participant} in public channels of the tournament {name}...") - await Matrix.invite(f"#annonces-{slug}:tfjm.org", - f"@{participant.matrix_username}:tfjm.org") - await Matrix.invite(f"#flood-{slug}:tfjm.org", - f"@{participant.matrix_username}:tfjm.org") - await Matrix.invite(f"#general-{slug}:tfjm.org", - f"@{participant.matrix_username}:tfjm.org") - await Matrix.invite(f"#tirage-au-sort-{slug}:tfjm.org", - f"@{participant.matrix_username}:tfjm.org") - - # Create pool-specific channels - for pool in tournament.pools.all(): - self.stdout.write(f"Managing {pool}...") - five = pool.participations.count() >= 5 - for i in range(2 if five else 1): - # Fix for five teams-pools - suffix = f"-{chr(ord('A') + i)}" if five else "" - if not await Matrix.resolve_room_alias(f"#poule-{slug}-{pool.id}{suffix}:tfjm.org"): - await Matrix.create_room( - visibility=RoomVisibility.public, - alias=f"poule-{slug}-{pool.id}{suffix}", - name=f"{name} - Jour {pool.round} - Poule " + - ', '.join(participation.team.trigram - for participation in pool.participations.all()) + suffix, - topic=f"Discussion avec les équipes - {pool}{suffix}", - federate=False, - preset=RoomPreset.private_chat, - ) - if not await Matrix.resolve_room_alias(f"#poule-{slug}-{pool.id}{suffix}-jurys:tfjm.org"): - await Matrix.create_room( - visibility=RoomVisibility.public, - alias=f"poule-{slug}-{pool.id}{suffix}-jurys", - name=f"{name} - Jour {pool.round}{suffix} - Jurys poule " + - ', '.join(participation.team.trigram - for participation in pool.participations.all()) + suffix, - topic=f"Discussion avec les jurys - {pool}{suffix}", - federate=False, - preset=RoomPreset.private_chat, - ) - - await Matrix.set_room_avatar(f"#poule-{slug}-{pool.id}{suffix}:tfjm.org", avatar_uri) - await Matrix.set_room_avatar(f"#poule-{slug}-{pool.id}{suffix}-jurys:tfjm.org", avatar_uri) - - bbb_url = pool.bbb_url.strip() - if five and ';' in bbb_url: - bbb_url = bbb_url.split(";")[i].strip() - url_params = urlencode(dict(url=bbb_url, - isAudioConf='false', displayName='$matrix_display_name', - avatarUrl='$matrix_avatar_url', userId='$matrix_user_id')) \ - .replace("%24", "$") - await Matrix.add_integration( - f"#poule-{slug}-{pool.id}{suffix}:tfjm.org", - f"https://scalar.vector.im/api/widgets/bigbluebutton.html?{url_params}", - f"bbb-{slug}-{pool.id}{suffix}", "bigbluebutton", "BigBlueButton", str(pool)) - await Matrix.add_integration( - f"#poule-{slug}-{pool.id}:tfjm.org", - f"https://board.tfjm.org/boards/{slug}-{pool.id}", f"board-{slug}-{pool.id}", - "customwidget", "Tableau", str(pool)) - - # Invite admins and give permissions - for admin in VolunteerRegistration.objects.filter(admin=True).all(): - await Matrix.invite(f"#poule-{slug}-{pool.id}{suffix}:tfjm.org", - f"@{admin.matrix_username}:tfjm.org") - await Matrix.invite(f"#poule-{slug}-{pool.id}{suffix}-jurys:tfjm.org", - f"@{admin.matrix_username}:tfjm.org") - - await Matrix.set_room_power_level(f"#poule-{slug}-{pool.id}{suffix}:tfjm.org", - f"@{admin.matrix_username}:tfjm.org", 95) - await Matrix.set_room_power_level(f"#poule-{slug}-{pool.id}{suffix}-jurys:tfjm.org", - f"@{admin.matrix_username}:tfjm.org", 95) - - # Invite organizers and give permissions - for orga in tournament.organizers.all(): - await Matrix.invite(f"#poule-{slug}-{pool.id}{suffix}:tfjm.org", - f"@{orga.matrix_username}:tfjm.org") - await Matrix.invite(f"#poule-{slug}-{pool.id}{suffix}-jurys:tfjm.org", - f"@{orga.matrix_username}:tfjm.org") - - if not orga.is_admin: - await Matrix.set_room_power_level(f"#poule-{slug}-{pool.id}{suffix}:tfjm.org", - f"@{orga.matrix_username}:tfjm.org", 50) - await Matrix.set_room_power_level(f"#poule-{slug}-{pool.id}{suffix}-jurys:tfjm.org", - f"@{orga.matrix_username}:tfjm.org", 50) - - # Invite the jury, give good permissions - for jury in pool.juries.all(): - await Matrix.invite(f"#annonces-{slug}:tfjm.org", f"@{jury.matrix_username}:tfjm.org") - await Matrix.invite(f"#general-{slug}:tfjm.org", f"@{jury.matrix_username}:tfjm.org") - await Matrix.invite(f"#flood-{slug}:tfjm.org", f"@{jury.matrix_username}:tfjm.org") - await Matrix.invite(f"#jury-{slug}:tfjm.org", f"@{jury.matrix_username}:tfjm.org") - await Matrix.invite(f"#orga-{slug}:tfjm.org", f"@{jury.matrix_username}:tfjm.org") - await Matrix.invite(f"#poule-{slug}-{pool.id}{suffix}:tfjm.org", - f"@{jury.matrix_username}:tfjm.org") - await Matrix.invite(f"#poule-{slug}-{pool.id}{suffix}-jurys:tfjm.org", - f"@{jury.matrix_username}:tfjm.org") - await Matrix.invite(f"#tirage-au-sort-{slug}:tfjm.org", - f"@{jury.matrix_username}:tfjm.org") - - if not jury.is_admin: - await Matrix.set_room_power_level(f"#jury-{slug}:tfjm.org", - f"@{jury.matrix_username}:tfjm.org", 50) - await Matrix.set_room_power_level(f"#poule-{slug}-{pool.id}{suffix}:tfjm.org", - f"@{jury.matrix_username}:tfjm.org", 50) - await Matrix.set_room_power_level(f"#poule-{slug}-{pool.id}{suffix}-jurys:tfjm.org", - f"@{jury.matrix_username}:tfjm.org", 50) - - # Invite participants to the right pool - for participation in pool.participations.all(): - for participant in participation.team.participants.all(): - await Matrix.invite(f"#poule-{slug}-{pool.id}{suffix}:tfjm.org", - f"@{participant.matrix_username}:tfjm.org") - - # Create private channels for teams - for team in Team.objects.all(): - self.stdout.write(f"Create private channel for {team}...") - if not await Matrix.resolve_room_alias(f"#equipe-{team.trigram.lower()}:tfjm.org"): - await Matrix.create_room( - visibility=RoomVisibility.public, - alias=f"equipe-{team.trigram.lower()}", - name=f"Équipe {team.trigram}", - topic=f"Discussion interne de l'équipe {team.name}", - federate=False, - preset=RoomPreset.private_chat, - ) - for participant in team.participants.all(): - await Matrix.invite(f"#equipe-{team.trigram.lower}:tfjm.org", - f"@{participant.matrix_username}:tfjm.org") - await Matrix.set_room_power_level(f"#equipe-{team.trigram.lower()}:tfjm.org", - f"@{participant.matrix_username}:tfjm.org", 50) - - """ - # Manage channels to discuss about problems - for i in range(9): - self.stdout.write(f"Create channel for problem {i}...") - if not await Matrix.resolve_room_alias(f"#mec-{i}:tfjm.org"): - await Matrix.create_room( - visibility=RoomVisibility.public, - alias=f"mec-{i}", - name=f"Mise en commun - {'Général' if i == 0 else f'Problème {i}'}", - topic=f"Discussion autour des problèmes", - federate=False, - preset=RoomPreset.public_chat, - invite=[f"@{registration.matrix_username}:tfjm.org" - for registration in Registration.objects.all()], - power_level_override={ - f"@{registration.matrix_username}:tfjm.org": (95 if registration.is_admin else 50) - for registration in VolunteerRegistration.objects.all() - }, - ) - await Matrix.set_room_avatar(f"#mec-{i}:tfjm.org", avatar_uri) - - for registration in Registration.objects.all(): - await Matrix.invite(f"#mec-{i}:tfjm.org", registration.matrix_username) - - for registration in VolunteerRegistration.objects.all(): - await Matrix.set_room_power_level(f"#mec-{i}:tfjm.org", - f"@{registration.matrix_username}:tfjm.org", - 95 if registration.is_admin else 50) - """ - - asyncio.get_event_loop().run_until_complete(main()) diff --git a/participation/models.py b/participation/models.py index 494f36a..91ea617 100644 --- a/participation/models.py +++ b/participation/models.py @@ -16,8 +16,6 @@ from django.utils.text import format_lazy from django.utils.translation import gettext_lazy as _ from registration.models import VolunteerRegistration from tfjm.lists import get_sympa_client -from tfjm.matrix import Matrix, RoomPreset, RoomVisibility - def get_motivation_letter_filename(instance, filename): return f"authorization/motivation_letters/motivation_letter_{instance.trigram}" @@ -101,18 +99,9 @@ class Team(models.Model): def save(self, *args, **kwargs): if not self.access_code: # if the team got created, generate the access code, create the contact mailing list - # and create a dedicated Matrix room. self.access_code = get_random_string(6) self.create_mailing_list() - Matrix.create_room( - visibility=RoomVisibility.private, - name=f"#équipe-{self.trigram.lower()}", - alias=f"equipe-{self.trigram.lower()}", - topic=f"Discussion de l'équipe {self.name}", - preset=RoomPreset.private_chat, - ) - return super().save(*args, **kwargs) def get_absolute_url(self): diff --git a/participation/signals.py b/participation/signals.py index 878258f..119ab16 100644 --- a/participation/signals.py +++ b/participation/signals.py @@ -19,12 +19,11 @@ def create_team_participation(instance, created, raw, **_): def update_mailing_list(instance: Team, raw, **_): """ - When a team name or trigram got updated, update mailing lists and Matrix rooms + When a team name or trigram got updated, update mailing lists """ if instance.pk and not raw: old_team = Team.objects.get(pk=instance.pk) if old_team.trigram != instance.trigram: - # TODO Rename Matrix room # Delete old mailing list, create a new one old_team.delete_mailing_list() instance.create_mailing_list() diff --git a/participation/views.py b/participation/views.py index 3af432f..b0432a6 100644 --- a/participation/views.py +++ b/participation/views.py @@ -32,7 +32,6 @@ from odf.table import CoveredTableCell, Table, TableCell, TableColumn, TableRow from odf.text import P from registration.models import StudentRegistration, VolunteerRegistration from tfjm.lists import get_sympa_client -from tfjm.matrix import Matrix from tfjm.views import AdminMixin, VolunteerMixin from .forms import AddJuryForm, JoinTeamForm, MotivationLetterForm, NoteForm, ParticipationForm, PassageForm, \ @@ -68,8 +67,7 @@ class CreateTeamView(LoginRequiredMixin, CreateView): """ When a team is about to be created, the user automatically joins the team, a mailing list got created and the user is - automatically subscribed to this mailing list, and finally - a Matrix room is created and the user is invited in this room. + automatically subscribed to this mailing list. """ ret = super().form_valid(form) # The user joins the team @@ -82,9 +80,6 @@ class CreateTeamView(LoginRequiredMixin, CreateView): get_sympa_client().subscribe(user.email, f"equipe-{form.instance.trigram.lower()}", False, f"{user.first_name} {user.last_name}") - # Invite the user in the team Matrix room - Matrix.invite(f"#equipe-{form.instance.trigram.lower()}:tfjm.org", - f"@{user.registration.matrix_username}:tfjm.org") return ret @@ -112,7 +107,7 @@ class JoinTeamView(LoginRequiredMixin, FormView): def form_valid(self, form): """ When a user joins a team, the user is automatically subscribed to - the team mailing list,the user is invited in the team Matrix room. + the team mailing list. """ self.object = form.instance ret = super().form_valid(form) @@ -127,9 +122,6 @@ class JoinTeamView(LoginRequiredMixin, FormView): get_sympa_client().subscribe(user.email, f"equipe-{form.instance.trigram.lower()}", False, f"{user.first_name} {user.last_name}") - # Invite the user in the team Matrix room - Matrix.invite(f"#equipe-{form.instance.trigram.lower()}:tfjm.org", - f"@{user.registration.matrix_username}:tfjm.org") return ret def get_success_url(self): @@ -468,9 +460,6 @@ class TeamLeaveView(LoginRequiredMixin, TemplateView): request.user.registration.team = None request.user.registration.save() get_sympa_client().unsubscribe(request.user.email, f"equipe-{team.trigram.lower()}", False) - Matrix.kick(f"#equipe-{team.trigram.lower()}:tfjm.org", - f"@{request.user.registration.matrix_username}:tfjm.org", - "Équipe quittée") if team.students.count() + team.coaches.count() == 0: team.delete() return redirect(reverse_lazy("index")) diff --git a/registration/auth.py b/registration/auth.py deleted file mode 100644 index 78b0a84..0000000 --- a/registration/auth.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (C) 2020 by Animath -# SPDX-License-Identifier: GPL-3.0-or-later - -from cas_server.auth import DjangoAuthUser # pragma: no cover - - -class CustomAuthUser(DjangoAuthUser): # pragma: no cover - """ - Override Django Auth User model to define a custom Matrix username. - """ - - def attributs(self): - d = super().attributs() - if self.user: - d["matrix_username"] = self.user.registration.matrix_username - d["display_name"] = str(self.user.registration) - return d diff --git a/registration/fixtures/initial.json b/registration/fixtures/initial.json deleted file mode 100644 index 38fc9df..0000000 --- a/registration/fixtures/initial.json +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "model": "cas_server.servicepattern", - "pk": 1, - "fields": { - "pos": 100, - "name": "Plateforme du TFJM²", - "pattern": "^https://tfjm.org(:8448)?/.*$", - "user_field": "matrix_username", - "restrict_users": false, - "proxy": true, - "proxy_callback": true, - "single_log_out": true, - "single_log_out_callback": "" - } - }, - { - "model": "cas_server.replaceattributname", - "pk": 1, - "fields": { - "name": "display_name", - "replace": "", - "service_pattern": 1 - } - } -] diff --git a/registration/models.py b/registration/models.py index d4dbea0..54515f6 100644 --- a/registration/models.py +++ b/registration/models.py @@ -85,10 +85,6 @@ class Registration(PolymorphicModel): def is_volunteer(self): return isinstance(self, VolunteerRegistration) - @property - def matrix_username(self): - return f"tfjm_{self.user.pk}" - def get_absolute_url(self): return reverse_lazy("registration:user_detail", args=(self.user_id,)) diff --git a/requirements.txt b/requirements.txt index 185e289..5a3d383 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,6 @@ channels[daphne]~=4.0.0 channels-redis~=4.0.0 crispy-bootstrap5~=0.7 Django>=4.1,<5.0 -django-cas-server~=2.0 django-crispy-forms~=1.14 django-extensions~=3.2 django-filter~=22.1 @@ -14,7 +13,6 @@ django-tables2~=2.4 djangorestframework~=3.14 django-rest-polymorphic~=0.1 gunicorn~=20.1 -matrix-nio~=0.20 odfpy~=1.4.1 phonenumbers~=8.12.57 psycopg2-binary~=2.9.5 diff --git a/tfjm.cron b/tfjm.cron index 9337d36..bceb9ee 100644 --- a/tfjm.cron +++ b/tfjm.cron @@ -10,9 +10,6 @@ # Recreate sympa lists */2 * * * * cd /code && python manage.py fix_sympa_lists &> /dev/null -# Update matrix channels -03 */6 * * * cd /code && python manage.py fix_matrix_channels &> /dev/null - # Check payments from Hello Asso */6 * * * * cd /code && python manage.py check_hello_asso &> /dev/null diff --git a/tfjm/matrix.py b/tfjm/matrix.py deleted file mode 100644 index 2023d81..0000000 --- a/tfjm/matrix.py +++ /dev/null @@ -1,432 +0,0 @@ -# Copyright (C) 2020 by Animath -# SPDX-License-Identifier: GPL-3.0-or-later - -from enum import Enum -import os - - -class Matrix: - """ - Utility class to manage interaction with the Matrix homeserver. - This log in the @tfjmbot account (must be created before). - The access token is then stored. - All is done with this bot account, that is a server administrator. - Tasks are normally asynchronous, but for compatibility we make - them synchronous. - """ - _token = None - _device_id = None - - @classmethod - async def _get_client(cls): # pragma: no cover - """ - Retrieve the bot account. - If not logged, log in and store access token. - """ - if not os.getenv("SYNAPSE_PASSWORD") and not os.getenv("SYNAPSE_TOKEN"): - return FakeMatrixClient() - - from nio import AsyncClient - client = AsyncClient("https://tfjm.org", "@tfjmbot:tfjm.org") - client.user_id = "@tfjmbot:tfjm.org" - - if os.getenv("SYNAPSE_TOKEN"): - client.access_token = os.getenv("SYNAPSE_TOKEN") - client.device_id = os.getenv("SYNAPSE_DEVICE") - return client - elif os.path.isfile(".matrix_token"): - with open(".matrix_device", "r") as f: - cls._device_id = f.read().rstrip(" \t\r\n") - client.device_id = cls._device_id - with open(".matrix_token", "r") as f: - cls._token = f.read().rstrip(" \t\r\n") - client.access_token = cls._token - return client - - await client.login(password=os.getenv("SYNAPSE_PASSWORD"), device_name="Plateforme") - cls._token = client.access_token - cls._device_id = client.device_id - with open(".matrix_token", "w") as f: - f.write(cls._token) - with open(".matrix_device", "w") as f: - f.write(cls._device_id) - return client - - @classmethod - async def set_display_name(cls, name: str): - """ - Set the display name of the bot account. - """ - client = await cls._get_client() - return await client.set_displayname(name) - - @classmethod - async def set_avatar(cls, avatar_url: str): # pragma: no cover - """ - Set the display avatar of the bot account. - """ - client = await cls._get_client() - return await client.set_avatar(avatar_url) - - @classmethod - async def get_avatar(cls): # pragma: no cover - """ - Set the display avatar of the bot account. - """ - client = await cls._get_client() - resp = await client.get_avatar() - return resp.avatar_url if hasattr(resp, "avatar_url") else resp - - @classmethod - async def upload( - cls, - data_provider, - content_type: str = "application/octet-stream", - filename: str = None, - encrypt: bool = False, - monitor=None, - filesize: int = None, - ): # pragma: no cover - """ - Upload a file to the content repository. - - Returns a tuple containing: - - - Either a `UploadResponse` if the request was successful, or a - `UploadError` if there was an error with the request - - - A dict with file decryption info if encrypt is ``True``, - else ``None``. - Args: - data_provider (Callable, SynchronousFile, AsyncFile): A function - returning the data to upload or a file object. File objects - must be opened in binary mode (``mode="r+b"``). Callables - returning a path string, Path, async iterable or aiofiles - open binary file object allow the file data to be read in an - asynchronous and lazy way (without reading the entire file - into memory). Returning a synchronous iterable or standard - open binary file object will still allow the data to be read - lazily, but not asynchronously. - - The function will be called again if the upload fails - due to a server timeout, in which case it must restart - from the beginning. - Callables receive two arguments: the total number of - 429 "Too many request" errors that occured, and the total - number of server timeout exceptions that occured, thus - cleanup operations can be performed for retries if necessary. - - content_type (str): The content MIME type of the file, - e.g. "image/png". - Defaults to "application/octet-stream", corresponding to a - generic binary file. - Custom values are ignored if encrypt is ``True``. - - filename (str, optional): The file's original name. - - encrypt (bool): If the file's content should be encrypted, - necessary for files that will be sent to encrypted rooms. - Defaults to ``False``. - - monitor (TransferMonitor, optional): If a ``TransferMonitor`` - object is passed, it will be updated by this function while - uploading. - From this object, statistics such as currently - transferred bytes or estimated remaining time can be gathered - while the upload is running as a task; it also allows - for pausing and cancelling. - - filesize (int, optional): Size in bytes for the file to transfer. - If left as ``None``, some servers might refuse the upload. - """ - client = await cls._get_client() - return await client.upload(data_provider, content_type, filename, encrypt, monitor, filesize) \ - if not isinstance(client, FakeMatrixClient) else None, None - - @classmethod - async def create_room( - cls, - visibility=None, - alias=None, - name=None, - topic=None, - room_version=None, - federate=True, - is_direct=False, - preset=None, - invite=(), - initial_state=(), - power_level_override=None, - ): - """ - Create a new room. - - Returns either a `RoomCreateResponse` if the request was successful or - a `RoomCreateError` if there was an error with the request. - - Args: - visibility (RoomVisibility): whether to have the room published in - the server's room directory or not. - Defaults to ``RoomVisibility.private``. - - alias (str, optional): The desired canonical alias local part. - For example, if set to "foo" and the room is created on the - "example.com" server, the room alias will be - "#foo:example.com". - - name (str, optional): A name to set for the room. - - topic (str, optional): A topic to set for the room. - - room_version (str, optional): The room version to set. - If not specified, the homeserver will use its default setting. - If a version not supported by the homeserver is specified, - a 400 ``M_UNSUPPORTED_ROOM_VERSION`` error will be returned. - - federate (bool): Whether to allow users from other homeservers from - joining the room. Defaults to ``True``. - Cannot be changed later. - - is_direct (bool): If this should be considered a - direct messaging room. - If ``True``, the server will set the ``is_direct`` flag on - ``m.room.member events`` sent to the users in ``invite``. - Defaults to ``False``. - - preset (RoomPreset, optional): The selected preset will set various - rules for the room. - If unspecified, the server will choose a preset from the - ``visibility``: ``RoomVisibility.public`` equates to - ``RoomPreset.public_chat``, and - ``RoomVisibility.private`` equates to a - ``RoomPreset.private_chat``. - - invite (list): A list of user id to invite to the room. - - initial_state (list): A list of state event dicts to send when - the room is created. - For example, a room could be made encrypted immediatly by - having a ``m.room.encryption`` event dict. - - power_level_override (dict): A ``m.room.power_levels content`` dict - to override the default. - The dict will be applied on top of the generated - ``m.room.power_levels`` event before it is sent to the room. - """ - client = await cls._get_client() - return await client.room_create( - visibility, alias, name, topic, room_version, federate, is_direct, preset, invite, initial_state, - power_level_override) - - @classmethod - async def resolve_room_alias(cls, room_alias: str): - """ - Resolve a room alias to a room ID. - Return None if the alias does not exist. - """ - client = await cls._get_client() - resp = await client.room_resolve_alias(room_alias) - return resp.room_id if resp and hasattr(resp, "room_id") else None - - @classmethod - async def invite(cls, room_id: str, user_id: str): - """ - Invite a user to a room. - - Returns either a `RoomInviteResponse` if the request was successful or - a `RoomInviteError` if there was an error with the request. - - Args: - room_id (str): The room id of the room that the user will be - invited to. - user_id (str): The user id of the user that should be invited. - """ - client = await cls._get_client() - if room_id.startswith("#"): - room_id = await cls.resolve_room_alias(room_id) - return await client.room_invite(room_id, user_id) - - @classmethod - async def send_message(cls, room_id: str, body: str, formatted_body: str = None, - msgtype: str = "m.text", html: bool = True): - """ - Send a message to a room. - """ - client = await cls._get_client() - if room_id.startswith("#"): - room_id = await cls.resolve_room_alias(room_id) - content = { - "msgtype": msgtype, - "body": body, - "formatted_body": formatted_body or body, - } - if html: - content["format"] = "org.matrix.custom.html" - return await client.room_send( - room_id=room_id, - message_type="m.room.message", - content=content, - ) - - @classmethod - async def add_integration(cls, room_id: str, widget_url: str, state_key: str, - widget_type: str = "customwidget", widget_name: str = "Custom widget", - widget_title: str = ""): - client = await cls._get_client() - if room_id.startswith("#"): - room_id = await cls.resolve_room_alias(room_id) - content = { - "type": widget_type, - "url": widget_url, - "name": widget_name, - "data": { - "curl": widget_url, - "title": widget_title, - }, - "creatorUserId": client.user, - "roomId": room_id, - "id": state_key, - } - return await client.room_put_state( - room_id=room_id, - event_type="im.vector.modular.widgets", - content=content, - state_key=state_key, - ) - - @classmethod - async def remove_integration(cls, room_id: str, state_key: str): - client = await cls._get_client() - if room_id.startswith("#"): - room_id = await cls.resolve_room_alias(room_id) - return await client.room_put_state( - room_id=room_id, - event_type="im.vector.modular.widgets", - content={}, - state_key=state_key, - ) - - @classmethod - async def kick(cls, room_id: str, user_id: str, reason: str = None): - """ - Kick a user from a room, or withdraw their invitation. - - Kicking a user adjusts their membership to "leave" with an optional - reason. -² - Returns either a `RoomKickResponse` if the request was successful or - a `RoomKickError` if there was an error with the request. - - Args: - room_id (str): The room id of the room that the user will be - kicked from. - user_id (str): The user_id of the user that should be kicked. - reason (str, optional): A reason for which the user is kicked. - """ - client = await cls._get_client() - if room_id.startswith("#"): - room_id = await cls.resolve_room_alias(room_id) - return await client.room_kick(room_id, user_id, reason) - - @classmethod - async def set_room_power_level(cls, room_id: str, user_id: str, power_level: int): # pragma: no cover - """ - Put a given power level to a user in a certain room. - - Returns either a `RoomPutStateResponse` if the request was successful or - a `RoomPutStateError` if there was an error with the request. - - Args: - room_id (str): The room id of the room where the power level - of the user should be updated. - user_id (str): The user_id of the user which power level should - be updated. - power_level (int): The target power level to give. - """ - client = await cls._get_client() - if isinstance(client, FakeMatrixClient): - return None - - if room_id.startswith("#"): - room_id = await cls.resolve_room_alias(room_id) - resp = await client.room_get_state_event(room_id, "m.room.power_levels") - content = resp.content - content["users"][user_id] = power_level - return await client.room_put_state(room_id, "m.room.power_levels", content=content, state_key=resp.state_key) - - @classmethod - async def set_room_power_level_event(cls, room_id: str, event: str, power_level: int): # pragma: no cover - """ - Define the minimal power level to have to send a certain event type - in a given room. - - Returns either a `RoomPutStateResponse` if the request was successful or - a `RoomPutStateError` if there was an error with the request. - - Args: - room_id (str): The room id of the room where the power level - of the event should be updated. - event (str): The event name which minimal power level should - be updated. - power_level (int): The target power level to give. - """ - client = await cls._get_client() - if isinstance(client, FakeMatrixClient): - return None - - if room_id.startswith("#"): - room_id = await cls.resolve_room_alias(room_id) - resp = await client.room_get_state_event(room_id, "m.room.power_levels") - content = resp.content - if event.startswith("m."): - content["events"][event] = power_level - else: - content[event] = power_level - return await client.room_put_state(room_id, "m.room.power_levels", content=content, state_key=resp.state_key) - - @classmethod - async def set_room_avatar(cls, room_id: str, avatar_uri: str): - """ - Define the avatar of a room. - - Returns either a `RoomPutStateResponse` if the request was successful or - a `RoomPutStateError` if there was an error with the request. - - Args: - room_id (str): The room id of the room where the avatar - should be changed. - avatar_uri (str): The internal avatar URI to apply. - """ - client = await cls._get_client() - if room_id.startswith("#"): - room_id = await cls.resolve_room_alias(room_id) - return await client.room_put_state(room_id, "m.room.avatar", content={ - "url": avatar_uri - }, state_key="") - - -if os.getenv("SYNAPSE_PASSWORD"): # pragma: no cover - from nio import RoomVisibility, RoomPreset - RoomVisibility = RoomVisibility - RoomPreset = RoomPreset -else: - # When running tests, faking matrix-nio classes to don't include the module - class RoomVisibility(Enum): - private = 'private' - public = 'public' - - class RoomPreset(Enum): - private_chat = "private_chat" - trusted_private_chat = "trusted_private_chat" - public_chat = "public_chat" - - -class FakeMatrixClient: - """ - Simulate a Matrix client to run tests, if no Matrix homeserver is connected. - """ - - def __getattribute__(self, item): - async def func(*_, **_2): - return None - return func diff --git a/tfjm/settings.py b/tfjm/settings.py index ac94fab..9874ae9 100644 --- a/tfjm/settings.py +++ b/tfjm/settings.py @@ -74,7 +74,6 @@ INSTALLED_APPS = [ if "test" not in sys.argv: # pragma: no cover INSTALLED_APPS += [ - 'cas_server', 'django_extensions', 'mailer', ] @@ -147,8 +146,6 @@ PASSWORD_HASHERS = [ 'django.contrib.auth.hashers.BCryptPasswordHasher', ] -CAS_AUTH_CLASS = 'registration.auth.CustomAuthUser' - REST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.IsAdminUser'