Aller au contenu

User

UserService

Source code in etuutt_bot/services/user.py
class UserService:
    NICKNAME_MAX_LEN = 32

    def __init__(self, bot: EtuUTTBot):
        self._bot = bot

    def get_server_nickname(self, user: ApiUserSchema) -> str:
        """Retourne le nom d'utilisateur qui va être attribué à l'utilisateur donné.

        Le nom d'utilisateur retourné est composé de 32 caractères au maximum,
        limitation imposée par Discord.

        Args:
            user: Les données utilisateur, telles que retournées par l'API du site étu

        Returns:
            Le nom d'utilisateur composé du prénom, du nom et du statut de l'utilisateur,
            soit sa branche, soit "Ancien étu", soit "Enseignant".
        """
        pseudo = f"{user.first_name.title()} {user.last_name.upper()}"
        member_type = user.member_type
        if member_type == MemberType.Student:
            pseudo += " - " + "/".join(user.branch_levels)
        elif member_type == MemberType.FormerStudent:
            pseudo += " - Ancien étu"
        elif member_type == MemberType.Teacher:
            pseudo += " - Enseignant"
        else:
            assert_never(member_type)
        if len(pseudo) > self.NICKNAME_MAX_LEN and " " in user.first_name:
            # If he has several first names we only keep the first one
            pseudo = f"{user.first_name.split(' ')[0]} {pseudo.removeprefix(user.first_name)}"
        if len(pseudo) > self.NICKNAME_MAX_LEN:
            # if there is no other way to shorten the nickname, slice it (not ideal)
            logging.warning(f"Le nom de l'utilisateur {pseudo} est trop long. Vérifiez son pseudo")
            pseudo = pseudo[: self.NICKNAME_MAX_LEN]
        return pseudo

    def get_member_roles(self, user: ApiUserSchema) -> set[Role]:
        """Retourne les rôles qui devraient être attribués à l'utilisateur donné.

        Args:
            user: Les données utilisateur, telles que retournées par l'API du site étu

        Returns:
            L'ensemble des rôles par défaut à donner à l'utilisateur donné.
            C'est-à-dire :
            - Si c'est un étudiant :
                - le rôle `étudiant`
                - le(s) rôle(s) de la ou des branches de sa formation.
                - les rôles correspondant à ses UEs du semestre (s'il en a)
            - Si c'est un ancien étudiant : le rôle `ancien étudiant`
            - Si c'est un enseignant : le rôle `enseignant`
        """
        member_type = user.member_type
        special_ids = self._bot.settings.guild.special_roles
        guild = self._bot.watched_guild
        if member_type == MemberType.Student:
            branches = {r for r in guild.roles if r.name.upper() in user.branches}
            ues = {r for r in guild.roles if r.name.upper() in user.ues}
            return {guild.get_role(special_ids.student)} | branches | ues
        if member_type == MemberType.FormerStudent:
            return {guild.get_role(special_ids.former_student)}
        if member_type == MemberType.Teacher:
            return {guild.get_role(special_ids.teacher)}
        assert_never(member_type)

    async def sync(self, member: discord.Member, user: ApiUserSchema):
        """Synchronise le membre du serveur avec les données de l'api du site étu.

        Args:
            member: Le membre du serveur Discord à synchroniser
            user: Les données de l'API
        """
        nickname = self.get_server_nickname(user)
        roles = self.get_member_roles(user)
        if nickname == member.nick and roles <= set(member.roles):
            # user already synced
            return

        upmost_role: Role = max(self._bot.watched_guild.get_member(self._bot.user.id).roles)
        reason_msg = f"Authentification étu de : {member.global_name}"
        if any(r >= upmost_role for r in member.roles):
            # if the bot try to deal with a role higher
            # than its own highest role, discord returns a 403.
            # Thus, those must be given without using the `edit` method.
            # Moreover, the bot cannot edit the nicknames of users with higher roles.
            roles -= set(member.roles[1:])
            await member.add_roles(*roles, reason=reason_msg)
            return
        roles |= set(member.roles)
        await member.edit(nick=nickname, roles=roles, reason=reason_msg)

get_member_roles(user)

Retourne les rôles qui devraient être attribués à l'utilisateur donné.

Parameters:

Name Type Description Default
user ApiUserSchema

Les données utilisateur, telles que retournées par l'API du site étu

required

Returns:

Type Description
set[Role]

L'ensemble des rôles par défaut à donner à l'utilisateur donné.

set[Role]

C'est-à-dire :

set[Role]
  • Si c'est un étudiant :
  • le rôle étudiant
  • le(s) rôle(s) de la ou des branches de sa formation.
  • les rôles correspondant à ses UEs du semestre (s'il en a)
set[Role]
  • Si c'est un ancien étudiant : le rôle ancien étudiant
set[Role]
  • Si c'est un enseignant : le rôle enseignant
Source code in etuutt_bot/services/user.py
def get_member_roles(self, user: ApiUserSchema) -> set[Role]:
    """Retourne les rôles qui devraient être attribués à l'utilisateur donné.

    Args:
        user: Les données utilisateur, telles que retournées par l'API du site étu

    Returns:
        L'ensemble des rôles par défaut à donner à l'utilisateur donné.
        C'est-à-dire :
        - Si c'est un étudiant :
            - le rôle `étudiant`
            - le(s) rôle(s) de la ou des branches de sa formation.
            - les rôles correspondant à ses UEs du semestre (s'il en a)
        - Si c'est un ancien étudiant : le rôle `ancien étudiant`
        - Si c'est un enseignant : le rôle `enseignant`
    """
    member_type = user.member_type
    special_ids = self._bot.settings.guild.special_roles
    guild = self._bot.watched_guild
    if member_type == MemberType.Student:
        branches = {r for r in guild.roles if r.name.upper() in user.branches}
        ues = {r for r in guild.roles if r.name.upper() in user.ues}
        return {guild.get_role(special_ids.student)} | branches | ues
    if member_type == MemberType.FormerStudent:
        return {guild.get_role(special_ids.former_student)}
    if member_type == MemberType.Teacher:
        return {guild.get_role(special_ids.teacher)}
    assert_never(member_type)

get_server_nickname(user)

Retourne le nom d'utilisateur qui va être attribué à l'utilisateur donné.

Le nom d'utilisateur retourné est composé de 32 caractères au maximum, limitation imposée par Discord.

Parameters:

Name Type Description Default
user ApiUserSchema

Les données utilisateur, telles que retournées par l'API du site étu

required

Returns:

Type Description
str

Le nom d'utilisateur composé du prénom, du nom et du statut de l'utilisateur,

str

soit sa branche, soit "Ancien étu", soit "Enseignant".

Source code in etuutt_bot/services/user.py
def get_server_nickname(self, user: ApiUserSchema) -> str:
    """Retourne le nom d'utilisateur qui va être attribué à l'utilisateur donné.

    Le nom d'utilisateur retourné est composé de 32 caractères au maximum,
    limitation imposée par Discord.

    Args:
        user: Les données utilisateur, telles que retournées par l'API du site étu

    Returns:
        Le nom d'utilisateur composé du prénom, du nom et du statut de l'utilisateur,
        soit sa branche, soit "Ancien étu", soit "Enseignant".
    """
    pseudo = f"{user.first_name.title()} {user.last_name.upper()}"
    member_type = user.member_type
    if member_type == MemberType.Student:
        pseudo += " - " + "/".join(user.branch_levels)
    elif member_type == MemberType.FormerStudent:
        pseudo += " - Ancien étu"
    elif member_type == MemberType.Teacher:
        pseudo += " - Enseignant"
    else:
        assert_never(member_type)
    if len(pseudo) > self.NICKNAME_MAX_LEN and " " in user.first_name:
        # If he has several first names we only keep the first one
        pseudo = f"{user.first_name.split(' ')[0]} {pseudo.removeprefix(user.first_name)}"
    if len(pseudo) > self.NICKNAME_MAX_LEN:
        # if there is no other way to shorten the nickname, slice it (not ideal)
        logging.warning(f"Le nom de l'utilisateur {pseudo} est trop long. Vérifiez son pseudo")
        pseudo = pseudo[: self.NICKNAME_MAX_LEN]
    return pseudo

sync(member, user) async

Synchronise le membre du serveur avec les données de l'api du site étu.

Parameters:

Name Type Description Default
member Member

Le membre du serveur Discord à synchroniser

required
user ApiUserSchema

Les données de l'API

required
Source code in etuutt_bot/services/user.py
async def sync(self, member: discord.Member, user: ApiUserSchema):
    """Synchronise le membre du serveur avec les données de l'api du site étu.

    Args:
        member: Le membre du serveur Discord à synchroniser
        user: Les données de l'API
    """
    nickname = self.get_server_nickname(user)
    roles = self.get_member_roles(user)
    if nickname == member.nick and roles <= set(member.roles):
        # user already synced
        return

    upmost_role: Role = max(self._bot.watched_guild.get_member(self._bot.user.id).roles)
    reason_msg = f"Authentification étu de : {member.global_name}"
    if any(r >= upmost_role for r in member.roles):
        # if the bot try to deal with a role higher
        # than its own highest role, discord returns a 403.
        # Thus, those must be given without using the `edit` method.
        # Moreover, the bot cannot edit the nicknames of users with higher roles.
        roles -= set(member.roles[1:])
        await member.add_roles(*roles, reason=reason_msg)
        return
    roles |= set(member.roles)
    await member.edit(nick=nickname, roles=roles, reason=reason_msg)