Drop Matrix support
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
parent
2f4755ffc7
commit
93a2e2436d
|
@ -54,7 +54,6 @@ SERVER_EMAIL=contact@tfjm.org # Adresse e-mail expéditrice
|
||||||
SYMPA_URL=lists.example.com # Serveur Sympa à utiliser
|
SYMPA_URL=lists.example.com # Serveur Sympa à utiliser
|
||||||
SYMPA_EMAIL= # Adresse e-mail du compte administrateur de Sympa
|
SYMPA_EMAIL= # Adresse e-mail du compte administrateur de Sympa
|
||||||
SYMPA_PASSWORD= # Mot de passe 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
|
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
|
||||||
|
|
|
@ -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())
|
|
|
@ -16,8 +16,6 @@ from django.utils.text import format_lazy
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from registration.models import VolunteerRegistration
|
from registration.models import VolunteerRegistration
|
||||||
from tfjm.lists import get_sympa_client
|
from tfjm.lists import get_sympa_client
|
||||||
from tfjm.matrix import Matrix, RoomPreset, RoomVisibility
|
|
||||||
|
|
||||||
|
|
||||||
def get_motivation_letter_filename(instance, filename):
|
def get_motivation_letter_filename(instance, filename):
|
||||||
return f"authorization/motivation_letters/motivation_letter_{instance.trigram}"
|
return f"authorization/motivation_letters/motivation_letter_{instance.trigram}"
|
||||||
|
@ -101,18 +99,9 @@ class Team(models.Model):
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if not self.access_code:
|
if not self.access_code:
|
||||||
# if the team got created, generate the access code, create the contact mailing list
|
# 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.access_code = get_random_string(6)
|
||||||
self.create_mailing_list()
|
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)
|
return super().save(*args, **kwargs)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
|
|
|
@ -19,12 +19,11 @@ def create_team_participation(instance, created, raw, **_):
|
||||||
|
|
||||||
def update_mailing_list(instance: Team, 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:
|
if instance.pk and not raw:
|
||||||
old_team = Team.objects.get(pk=instance.pk)
|
old_team = Team.objects.get(pk=instance.pk)
|
||||||
if old_team.trigram != instance.trigram:
|
if old_team.trigram != instance.trigram:
|
||||||
# TODO Rename Matrix room
|
|
||||||
# Delete old mailing list, create a new one
|
# Delete old mailing list, create a new one
|
||||||
old_team.delete_mailing_list()
|
old_team.delete_mailing_list()
|
||||||
instance.create_mailing_list()
|
instance.create_mailing_list()
|
||||||
|
|
|
@ -32,7 +32,6 @@ from odf.table import CoveredTableCell, Table, TableCell, TableColumn, TableRow
|
||||||
from odf.text import P
|
from odf.text import P
|
||||||
from registration.models import StudentRegistration, VolunteerRegistration
|
from registration.models import StudentRegistration, VolunteerRegistration
|
||||||
from tfjm.lists import get_sympa_client
|
from tfjm.lists import get_sympa_client
|
||||||
from tfjm.matrix import Matrix
|
|
||||||
from tfjm.views import AdminMixin, VolunteerMixin
|
from tfjm.views import AdminMixin, VolunteerMixin
|
||||||
|
|
||||||
from .forms import AddJuryForm, JoinTeamForm, MotivationLetterForm, NoteForm, ParticipationForm, PassageForm, \
|
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
|
When a team is about to be created, the user automatically
|
||||||
joins the team, a mailing list got created and the user is
|
joins the team, a mailing list got created and the user is
|
||||||
automatically subscribed to this mailing list, and finally
|
automatically subscribed to this mailing list.
|
||||||
a Matrix room is created and the user is invited in this room.
|
|
||||||
"""
|
"""
|
||||||
ret = super().form_valid(form)
|
ret = super().form_valid(form)
|
||||||
# The user joins the team
|
# 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,
|
get_sympa_client().subscribe(user.email, f"equipe-{form.instance.trigram.lower()}", False,
|
||||||
f"{user.first_name} {user.last_name}")
|
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
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
@ -112,7 +107,7 @@ class JoinTeamView(LoginRequiredMixin, FormView):
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
"""
|
"""
|
||||||
When a user joins a team, the user is automatically subscribed to
|
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
|
self.object = form.instance
|
||||||
ret = super().form_valid(form)
|
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,
|
get_sympa_client().subscribe(user.email, f"equipe-{form.instance.trigram.lower()}", False,
|
||||||
f"{user.first_name} {user.last_name}")
|
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
|
return ret
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
|
@ -468,9 +460,6 @@ class TeamLeaveView(LoginRequiredMixin, TemplateView):
|
||||||
request.user.registration.team = None
|
request.user.registration.team = None
|
||||||
request.user.registration.save()
|
request.user.registration.save()
|
||||||
get_sympa_client().unsubscribe(request.user.email, f"equipe-{team.trigram.lower()}", False)
|
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:
|
if team.students.count() + team.coaches.count() == 0:
|
||||||
team.delete()
|
team.delete()
|
||||||
return redirect(reverse_lazy("index"))
|
return redirect(reverse_lazy("index"))
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
|
@ -85,10 +85,6 @@ class Registration(PolymorphicModel):
|
||||||
def is_volunteer(self):
|
def is_volunteer(self):
|
||||||
return isinstance(self, VolunteerRegistration)
|
return isinstance(self, VolunteerRegistration)
|
||||||
|
|
||||||
@property
|
|
||||||
def matrix_username(self):
|
|
||||||
return f"tfjm_{self.user.pk}"
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse_lazy("registration:user_detail", args=(self.user_id,))
|
return reverse_lazy("registration:user_detail", args=(self.user_id,))
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ channels[daphne]~=4.0.0
|
||||||
channels-redis~=4.0.0
|
channels-redis~=4.0.0
|
||||||
crispy-bootstrap5~=0.7
|
crispy-bootstrap5~=0.7
|
||||||
Django>=4.1,<5.0
|
Django>=4.1,<5.0
|
||||||
django-cas-server~=2.0
|
|
||||||
django-crispy-forms~=1.14
|
django-crispy-forms~=1.14
|
||||||
django-extensions~=3.2
|
django-extensions~=3.2
|
||||||
django-filter~=22.1
|
django-filter~=22.1
|
||||||
|
@ -14,7 +13,6 @@ django-tables2~=2.4
|
||||||
djangorestframework~=3.14
|
djangorestframework~=3.14
|
||||||
django-rest-polymorphic~=0.1
|
django-rest-polymorphic~=0.1
|
||||||
gunicorn~=20.1
|
gunicorn~=20.1
|
||||||
matrix-nio~=0.20
|
|
||||||
odfpy~=1.4.1
|
odfpy~=1.4.1
|
||||||
phonenumbers~=8.12.57
|
phonenumbers~=8.12.57
|
||||||
psycopg2-binary~=2.9.5
|
psycopg2-binary~=2.9.5
|
||||||
|
|
|
@ -10,9 +10,6 @@
|
||||||
# Recreate sympa lists
|
# Recreate sympa lists
|
||||||
*/2 * * * * cd /code && python manage.py fix_sympa_lists &> /dev/null
|
*/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
|
# Check payments from Hello Asso
|
||||||
*/6 * * * * cd /code && python manage.py check_hello_asso &> /dev/null
|
*/6 * * * * cd /code && python manage.py check_hello_asso &> /dev/null
|
||||||
|
|
||||||
|
|
432
tfjm/matrix.py
432
tfjm/matrix.py
|
@ -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
|
|
|
@ -74,7 +74,6 @@ INSTALLED_APPS = [
|
||||||
|
|
||||||
if "test" not in sys.argv: # pragma: no cover
|
if "test" not in sys.argv: # pragma: no cover
|
||||||
INSTALLED_APPS += [
|
INSTALLED_APPS += [
|
||||||
'cas_server',
|
|
||||||
'django_extensions',
|
'django_extensions',
|
||||||
'mailer',
|
'mailer',
|
||||||
]
|
]
|
||||||
|
@ -147,8 +146,6 @@ PASSWORD_HASHERS = [
|
||||||
'django.contrib.auth.hashers.BCryptPasswordHasher',
|
'django.contrib.auth.hashers.BCryptPasswordHasher',
|
||||||
]
|
]
|
||||||
|
|
||||||
CAS_AUTH_CLASS = 'registration.auth.CustomAuthUser'
|
|
||||||
|
|
||||||
REST_FRAMEWORK = {
|
REST_FRAMEWORK = {
|
||||||
'DEFAULT_PERMISSION_CLASSES': [
|
'DEFAULT_PERMISSION_CLASSES': [
|
||||||
'rest_framework.permissions.IsAdminUser'
|
'rest_framework.permissions.IsAdminUser'
|
||||||
|
|
Loading…
Reference in New Issue