diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml new file mode 100644 index 0000000..73cc0f0 --- /dev/null +++ b/.github/workflows/docs.yaml @@ -0,0 +1,31 @@ +name: deploy_docs +on: + push: + branches: + - main + +permissions: + contents: write + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + with: + python-version: 3.12 + - name: Cache virtualenv + uses: actions/cache@v3 + with: + key: venv-${{ runner.os }}-${{ steps.setup_python.outputs.python-version}}-${{ hashFiles('requirements.txt') }} + path: .venv + - name: Install dependencies + run: | + python -m venv .venv + source .venv/bin/activate + python -m pip install --upgrade pip + python -m pip install -r requirements-dev.txt + echo "$VIRTUAL_ENV/bin" >> $GITHUB_PATH + echo "VIRTUAL_ENV=$VIRTUAL_ENV" >> $GITHUB_ENV + - run: mkdocs gh-deploy --force diff --git a/.gitignore b/.gitignore index 3cbb60b..bc7b730 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,9 @@ __pycache__/ # Logs folder logs/ +# Compiled documentation +site/ + # Data files /data/* !/data/*.example.* diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8de793d..1acb7b9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,6 +4,10 @@ repos: rev: v4.6.0 hooks: - id: check-yaml + exclude: mkdocs.yaml + - id: check-yaml + args: [ --unsafe ] + files: mkdocs.yaml - id: end-of-file-fixer - id: requirements-txt-fixer files: requirements.*\.txt diff --git a/docs/explanation/conventions.md b/docs/explanation/conventions.md new file mode 100644 index 0000000..8c89a4b --- /dev/null +++ b/docs/explanation/conventions.md @@ -0,0 +1,270 @@ +Cette page traite des conventions utilisées pour le développement du bot étu. + +## Langue + +Les noms, de fonctions, de classe, de fichiers et de dossiers sont en anglais. + +Les docstrings et la documentation sont en français. + +De manière générale, demandez-vous juste à qui vous êtes en train d'écrire : + +- si vous écrivez pour la machine, c'est en anglais +- si vous écrivez pour des êtres humains, c'est en français + +## Gestion de version + +Le projet utilise Git pour gérer les versions et GitHub pour héberger le dépôt distant. + +La branche de référence est la branche `main`. +Aucun `push` direct n'est autorisé sur cette branche. +Aucune fusion de code dont la CI échoue n'est autorisée non plus. + +L'envoi de modifications sur la branche `main` doit se faire uniquement +par Pull Requests. + +### Nommage des commits + +Les noms des commits doivent être en anglais. +Si un commit effectue une action en particulier, +le type de cette action doit être indiqué en début de message : + +- Les commits qui résolvent un bug commencent par "fix:" + suivi de la description du bug ; exemple : `fix: spanish inquisition wasn't expected` +- Les commits qui ajoutent une nouvelle fonctionnalité commencent par "feat:" + suivi de la description de la fonctionnalité ; exemple : `feat: add a new silly walk` +- Les commits qui refactorent du code commencent par "refactor:", + suivi de la description du refactor ; + exemple : `refactor: more explicit separation from the Judean's People Front` +- Les commits qui ajoutent de la documentation commencent par "doc:", + suivi de la description de ce qui est documenté ; + exemple : `doc: explain how to use the Holy Hand Grenade of Antioche` + +Si un de vos commits n'accomplit pas exactement un type de tâche, +il est possible qu'il soit préférable de le découper en commits plus petits. +Mais ne partez pas dans le vice inverse ; +ne faites pas des micro-commits. + +Pour plus d'information, allez sur le site des +[Commits Conventionnels](https://www.conventionalcommits.org/fr/v1.0.0/). + +### Gestion des branches + +La branche `main` est destinée uniquement à recevoir des merge commits. +Elle doit recevoir, jamais donner. +Lorsqu'une de vos branches est en conflit avec la branche `main`, +vous devez donc `rebase`, jamais `merge`. + +En d'autres termes, vous devez respecter les deux règles suivantes : + +1. la branche `main` doit contenir seulement des merge commits +2. seule la branche `main` doit contenir des merge commits + +=== "Bien ✔️" + + ```mermaid + gitGraph: + commit id: "initial commit" + branch bar + checkout main + checkout bar + commit id: "baz" + checkout main + merge bar id: "Merge branch bar" + branch foo + commit id: "foo a" + commit id: "foo b" + commit id: "foo c" + checkout main + merge foo id: "Merge branch foo" + ``` + +=== "Pas bien ❌" + + ```mermaid + gitGraph: + commit + branch bar + branch foo + commit id: "foo a" + commit id: "foo b" + checkout main + checkout bar + commit id: "baz" + checkout main + merge bar id: "Merge branch bar" + checkout foo + merge main id: "Merge branch main" + commit id: "foo c" + checkout main + merge foo id: "Merge branch foo" + ``` + +## Style de code + +### Conventions de nommage + +Les conventions de nommage sont celles de la +[PEP8](https://peps.python.org/pep-0008/) : + +- les classes sont en PascalCase (ex: `class SacredGraal`) +- les constantes sont en MACRO_CASE (ex: `FAVOURITE_COLOUR = "blue"`) +- les fonctions et les variables sont en snake_case (ex: `swallow_origin = "african"`) +- les fichiers et dossiers contenant du code sont en snake_case +- les fichiers et les dossiers contenant de la documentation sont en kebab-case + +En parallèle de la casse, certaines règles doivent être suivies autant que possible : + +- un fichier doit contenir une seule classe contenant de la logique ; + on peut y rajouter des classes de données (`Enum`, `DataClass`, `pydantic.BaseModel`), + tout en prenant garde de ne pas en abuser. +- le nom d'une classe doit être suffixé par ce qu'elle représente : + - Les cogs : `Cog` (`RoleCog`) + - les services : `Service` (`UserService`) + - les schémas de données pydantic : `Schema` (`ApiUserSchema`) + - etc. +- les signatures des fonctions doivent systématiquement avoir des annotations de type ; + les variables dont le type n'est pas évident doivent aussi être annotées. + +Nous essayons aussi de suivre les conventions de nommage usuelles pour +chacun des languages (HTML, CSS et JavaScript) utilisés. + +### Format + +Le format du code est celui établi par +[le formateur de Ruff](https://docs.astral.sh/ruff/formatter/). + +Vous êtes encouragés à lire la documentation, +elle est instructive. +Mais elle n'est pas essentielle non plus, puisque Ruff +est là pour s'occuper de la question à votre place. + +Retenez simplement : + +- Si vous faites une PR avec du code qui ne respecte pas le format + attendu, la PR est bloquée. + Pensez bien à faire tourner Ruff avant de commit + (ou encore mieux, configurez pre-commit). +- Si Ruff modifie une partie de votre code et que vous trouvez que le + résultat n'est pas élégant, alors ça veut dire que le problème n'est + pas que dans la forme. Profitez-en pour revoir un peu la logique du code. + +### Qualité du code + +Pour s'assurer de la qualité du code, Ruff est également utilisé. + +Tout comme pour le format, Ruff doit tourner avant chaque commit. + +!!!note "to edit or not to edit" + + Vous constaterez sans doute que `ruff format` modifie votre code, + mais que `ruff check` vous signale juste une liste + d'erreurs sans rien modifier. + + En effet, `ruff format` ne s'occupe que de la forme du code, + alors que `ruff check` regarde la logique du code. + Si Ruff modifiait automatiquement la logique du code, + ça serait un coup à introduire plus de bugs que ça n'en résoud. + + Il existe cependant certaines catégories d'erreurs que Ruff + peut réparer de manière sûre. + Pour appliquer ces réparations, faites : + + ```bash + ruff check --fix + ``` + +## Documentation + +La documentation est écrite en markdown, avec les fonctionnalités +offertes par MkDocs, MkDocs-material et leurs extensions. + +La documentation est intégralement en français, à l'exception +des exemples, qui suivent les conventions données plus haut. + +### Découpage + +La séparation entre les différentes parties de la documentation se fait +en suivant la méthodologie [Diataxis](https://diataxis.fr/). +On compte quatre sections : + +1. Explications : parlez dans cette section de ce qui est bon à savoir + sans que ça touche aux détails précis de l'implémentation. + Si vous parlez de *pourquoi* un choix a été fait ou que vous montrez + grossièrement les contours d'une partie du projet, c'est une explication. +2. Tutoriels : parlez dans cette section d'étapes précises + ou de détails d'implémentation qu'un nouveau développeur + doit suivre pour commencer à travailler sur le projet. +3. Utilisation : parlez dans cette section de méthodes utiles + pour un développeur qui a déjà pris en main le projet. + Voyez cette partie comme un livre de recettes de cuisine. +4. Référence : parlez dans cette section des détails d'implémentation du projet. + En réalité, vous n'aurez pas besoin de beaucoup vous pencher dessus, + puisque cette partie est composée presque uniquement + des docstrings présents dans le code. + +Pour plus de détails, lisez directement la documentation de Diataxis, +qui expose ces concepts de manière beaucoup plus complète. + +### Style + +La documentation doit être écrite avec de courts paragraphes. +Un maximum de trois phrases par paragraphe est un bon objectif. + +Votre markdown doit être composé de lignes courtes ; +à partir de 88 caractères, c'est trop long. +Si une phrase est trop longue pour tenir sur une ligne, +vous pouvez l'écrire sur plusieurs. + +Une ligne ne peut pas contenir plus d'une seule phrase. +Dit autrement, quand vous finissez une phrase, +faites systématiquement un saut de ligne. + +=== "Bien ✔️" + + ```markdown linenums="1" + First shalt thou take out the Holy Pin, + then shalt thou count to three, no more, no less. + Three shalt be the number thou shalt count, + and the number of the counting shalt be three. + Four shalt thou not count, neither count thou two, + excepting that thou then proceed to three. + Five is right out. + Once the number three, being the third number, be reached, + then lobbest thou thy Holy Hand Grenade of Antioch towards thou foe, + who being naughty in My sight, shall snuff it. + ``` + +=== "Pas bien ❌" + + ```markdown linenums="1" + First shalt thou take out the Holy Pin, then shalt thou count to three, no more, no less. Three shalt be the number thou shalt count, and the number of the counting shalt be three. Four shalt thou not count, neither count thou two, excepting that thou then proceed to three. Five is right out. Once the number three, being the third number, be reached, then lobbest thou thy Holy Hand Grenade of Antioch towards thou foe, who being naughty in My sight, shall snuff it. + ``` + +À noter que ces deux exemples donnent le même résultat +dans la documentation générée. +Mais la version avec des lignes courtes est beaucoup plus facile à modifier. + +!!!warning "Grammaire et orthographe" + + Ca peut paraitre évident dit comme ça, mais c'est toujours bon à rappeler : + évitez de faire des fautes de français. + Relisez vous quand vous avez fini d'écrire. + +### Docstrings + +Les docstrings sont écrits en suivant la norme +[Google](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html) +et les fonctionnalités de [Griffe](https://mkdocstrings.github.io/griffe/docstrings/). + +Ils doivent être explicites sur ce que la fonction accomplit, +mais ne pas parler de comment elle le fait. +Un bon docstring est celui qui dit exactement +ce qu'il faut pour qu'on puisse savoir comment +utiliser la fonction ou la classe documentée sans avoir à lire son code. + +Voyez ça comme les pédales d'une voiture : +pour pouvoir conduire, vous avez juste besoin +de savoir ce qui se passe quand vous appuyez dessus. +La connaissance de la mécanique interne est inutile dans ce cadre. + +N'hésitez pas à mettre des examples dans vos docstrings. diff --git a/docs/explanation/etu-auth.md b/docs/explanation/etu-auth.md new file mode 100644 index 0000000..db94b1a --- /dev/null +++ b/docs/explanation/etu-auth.md @@ -0,0 +1,58 @@ +Les membres du serveur Discord peuvent (et doivent !) +s'authentifier au moyen de l'API du site étu. +Cette authentification permet automatiquement : + +- d'attribuer le pseudo utilisé sur le serveur ; + ce dernier est de la forme ` - `. +- d'attribuer les rôles du membre : + - Étudiant/Ancien étudiant/Enseignant + - S'il est étudiant, le(s) rôle(s) de sa ou ses formations + - S'il est étudiant, ses rôles d'UE. + +!!!note "Pseudo Discord" + + Comme un pseudo Discord ne peut faire plus de 32 caractères, + la sélection de celui-ci a quelques subtilités. + Pour plus de détails, voir + [UserService.get_server_nickname][etuutt_bot.services.user.UserService.get_server_nickname] + +## Déroulement de l'authentification + +Pour l'authentification, plusieurs acteurs sont nécessaires : + +- l'API du site étu ; celle-ci fonctionne indépendamment du bot (tant que le site tourne) +- le serveur du bot ; celui-ci est géré par le même processus que le bot +- le bot +- Et bien sûr, l'utilisateur + +Pour accomplir l'opération, l'utilisateur va se rendre +sur un formulaire fourni par le serveur du bot. +Le serveur va ensuite interagir avec l'API du site +étu pour récupérer les informations utilisateur +puis utiliser les services du bot pour attribuer le pseudo et les rôles. + +```mermaid +sequenceDiagram + actor User as Membre du serveur + participant bot + participant server + participant api as API du site étu + + User->>server: GET / + server->>api: Redirect /oauth/authorize + alt Si l'utilisateur n'est pas encore connecté + api->>User: Formulaire de connexion + User-->>api: POST /user + end + api-->>server: Code d'autorisation + server->>api: POST /oauth/token + api-->>server: token d'accès + server->>User: Formulaire de saisie
des infos sur le compte Discord + User-->>server: POST /role + server->>api: GET /public/user/account + api-->>server: Informations sur l'utilisateur + server->>bot: Transmission des informations + bot->>bot: Attribution des rôles + bot->>server: Résultat de l'attribution + server->>User: Affichage de la page de réussite ou d'une page d'erreur +``` diff --git a/docs/explanation/index.md b/docs/explanation/index.md new file mode 100644 index 0000000..2671523 --- /dev/null +++ b/docs/explanation/index.md @@ -0,0 +1,57 @@ +## Motifs du projet + +Ce projet est une réécriture du +[Bot du serveur Discord UTT](https://github.com/ungdev/discord_bot_firewall). + +Ce dernier a été écrit par Ivann Laruelle en mars 2020, au tout début +du confinement. +La chose s'est faite plus ou moins dans l'urgence, +à cause de la nécessité soudaine de maintenir un contact entre +les étudiants en ces temps covidés. + +Le plus gros du code a été écrit en une nuit. +Il y a bien eu quelques ajouts de fonctionnalité par la suite, +mais la plus grande partie n'a pour ainsi dire jamais été refactor. + +En plus de cette urgence, le bot a été écrit avec la librairie `Discord.js`, +qu'Ivann n'avait jamais utilisé auparavant. +Cette dernière est conçue en Javascript, qui n'était pas +le langage avec lequel Ivann était le plus à l'aise. + +Tout cela combiné a entrainé beaucoup d'antipatterns, +de répétitions et de mauvais usages des fonctionnalités de la librairie. + +Signalons également que depuis cette époque, l'API de Discord +a beaucoup évolué. +Les commandes slash ont fait leur apparition et +plusieurs nouvelles versions de l'API ont eu le temps +d'être mises à disposition et d'être dépréciées. + +Enfin, Ivann a été diplômé en 2022. + +Bref, le code commence à dater et n'est pas incroyablement bien écrit. +De plus son créateur et ses mainteneurs ne sont plus là. + +Donc quitte à reprendre le projet, autant le refaire intégralement, +en le faisant plus sereinement, avec des technologies mieux +maitrisées et en mettant mieux à profit les fonctionnalités +offertes par Discord et par les librairies utilisées. + +## Philosophie du projet + +En partant de tout ce que nous avons vu ci-dessus, +il apparait clair que la principale ligne directrice +doit être de prendre notre temps pour pleinement +maitriser nos outils et concevoir un bot plus performant +et plus ergonomique, en utilisant pour cela moins de code. + +Le nouveau bot utilisera autant que possible +les commandes slash de Discord. + +À partir de ça, il est également recherché la réduction +de la verbosité et des répétitions du code. +À terme, on vise une parité en termes de fonctionnalité, +mais avec beaucoup moins de lignes. +Le programme actuel contient 6599 lignes de Javascript ; +concevoir le nouveau en 3000 lignes environ est un +objectif à la fois souhaitable et réalisable. diff --git a/docs/explanation/technos.md b/docs/explanation/technos.md new file mode 100644 index 0000000..32ea28c --- /dev/null +++ b/docs/explanation/technos.md @@ -0,0 +1,146 @@ +Nous tenterons ici d'expliquer les motifs +qui ont poussé à choisir les technologies +utilisées dans ce projet. + +!!! note + + Nous n'aborderons ici que les raisons + qui ont poussé à choisir ces technologies. + Si vous cherchez un guide d'installation, + veuillez consulter la section + [Installation](../tutorials/install.md). + +## Dépendances système + +### Git et GitHub + +Git est un logiciel de gestion de versions décentralisé. +C'est un outil léger, extrêmement puissant et très répandu. +Il est utilisé pour gérer le code source du projet. + +GitHub est un service web d'hébergement et +de gestion de développement de logiciels, +utilisant le logiciel de gestion de versions Git. + +Si vous n'êtes pas familiers avec Git et GitHub, +n'hésitez pas à chercher des tutoriels sur Internet ; +il y en a beaucoup et de très bonne qualité. +Si vous ne comprenez pas bien malgré tout, +n'hésitez pas à demander de l'aide à un membre du projet +ou à un membre de l'UNG. + +Pour faciliter l'usage de Git, vous pouvez également +utiliser une interface graphique. Il en existe +de très bons, comme [GitKraken](https://www.gitkraken.com/), +[Sublime Merge](https://www.sublimemerge.com/) ou encore +l'interface Git intégrée à [PyCharm](https://www.jetbrains.com/pycharm/). + +!!!tip + + Git est le standard de facto pour la gestion de version, + autant dans le monde de l'entreprise que dans celui du logiciel libre. + Si vous ne savez pas encore comment l'utiliser, apprenez. + Même si vous ne comptez pas contribuer au projet, ça vous sera + incroyablement utile. + +### Python + +Le code est écrit en Python. +C'est un langage simple à apprendre et à écrire. + +Depuis quelques années, le langage comprend un modèle +asynchrone qui rend son utilisation très pertinente dans le +cadre d'un bot Discord. + +Son support croissant pour les indications de type +le rendent également de plus en plus expressif, +et permettent de concevoir des librairies utilisant +le système de type pour effectuer des conversions et des vérifications +à l'utilisation (ce que Typescript ne permet pas). + +Signalons aussi que Javascript et Typescript sont déjà utilisés +pour la plupart des projets de l'UNG en cours de développement. +TS eut aussi été un choix pertinent, mais c'est bien de varier +un peu les technologies utilisées. + +## Dépendances Python + +### discord.py + +Discord.py est une librairie pour l'écriture de bots Discord. +Elle est facile d'usage, entièrement asynchrone +et offre un grand nombre de fonctionnalités. + +Ses performances sont très bonnes pour du Python : +sa conception entièrement asynchrone et sa mise en cache +des données lui permettent des temps de réponses très rapides +en utilisant aussi peu d'appels à l'API Discord que possible +tout en gérant le rate limit automatiquement. + +En outre, les fonctionnalités que la librairie met +à disposition permettent d'écrire des commandes concises et lisibles. + +C'est sans doute la meilleure librairie pour la conception +de bots Discord dans l'écosystème Python. + +### aiohttp + +Pour la connexion depuis le site étu, +le programme du bot fait tourner en parallèle un serveur +web chargé d'intéragir avec l'API du site étu. +Ce serveur tourne avec aiohttp. + +C'est une librairie qui n'est pas aussi complète que FastAPI, +mais elle est très performante et très légère. +C'est une qualité non négligeable, +sachant que le serveur web est assez simple +(3 routes au total, dont une qui est juste une redirection) +et que dans l'idéal, on veut que l'image Docker du bot +soit aussi petite que possible. + +De plus, discord.py s'appuie sur aiohttp pour faire ses requêtes +à l'API Discord ainsi qu'établir la connexion Websocket donc la +librairie était présente de toute manière donc autant l'utiliser à fond. + +### pydantic + +Pour la gestion de la configuration et pour parser +les requêtes et les réponses du serveur web, +on utilise Pydantic. + +La librairie s'interface extrêmement bien +avec les annotations de type de Python. +Elle permet une validation des données intuitive, +extrêmement performante et nécessitant assez peu de code. + +## Qualité de code + +### Ruff + +Ruff est un linter et un formateur de code. + +Il est extrêmement complet et fiable. +Et surtout, sa vitesse d'exécution est absurdement rapide. + +### pre-commit + +C'est un framework qui permet d'ajouter des « hooks » qui vont s'exécuter +automatiquement juste avant de réaliser un commit Git. +Ceux-ci vont vérifier et corriger les fichiers selon la configuration. +Il y a notamment le hook de Ruff qui exécute le linter et le formateur. + +### Tests + +Dans l'écosystème Python, il existe deux méthodes principales pour tester un code : + +- `unittest` +- `pytest` + +Les deux méthodes ont leurs avantages. +Et ce projet utilise... ni l'une ni l'autre. + +En effet, la presque totalité des opérations effectuées +consistent en des manipulations directes du serveur Discord. +Et tester ce genre de choses est pratiquement mission impossible. + +Le projet n'est donc malheureusement pas testé. diff --git a/docs/howto/index.md b/docs/howto/index.md new file mode 100644 index 0000000..1333ed7 --- /dev/null +++ b/docs/howto/index.md @@ -0,0 +1 @@ +TODO diff --git a/docs/img/favicon.png b/docs/img/favicon.png new file mode 100644 index 0000000..6c7dc9b Binary files /dev/null and b/docs/img/favicon.png differ diff --git a/docs/img/tutorials/discord_developer_portal.jpg b/docs/img/tutorials/discord_developer_portal.jpg new file mode 100644 index 0000000..faaa5d6 Binary files /dev/null and b/docs/img/tutorials/discord_developer_portal.jpg differ diff --git a/docs/img/tutorials/intents.png b/docs/img/tutorials/intents.png new file mode 100644 index 0000000..d7f8d6f Binary files /dev/null and b/docs/img/tutorials/intents.png differ diff --git a/docs/img/tutorials/scopes_and_perms.png b/docs/img/tutorials/scopes_and_perms.png new file mode 100644 index 0000000..2a5815c Binary files /dev/null and b/docs/img/tutorials/scopes_and_perms.png differ diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..81dee0d --- /dev/null +++ b/docs/index.md @@ -0,0 +1,7 @@ +# EtuUTT bot Discord + +Ceci est la documentation du bot Discord développé et hébergé par l'UNG, +utilisé pour gérer le serveur des étudiants de l'UTT. + +Si c'est la première fois que vous ouvrez cette documentation, +nous vous recommendons de commencer par la partie [Explications](explanation/index.md). diff --git a/docs/reference/commands/admin.md b/docs/reference/commands/admin.md new file mode 100644 index 0000000..2fac98a --- /dev/null +++ b/docs/reference/commands/admin.md @@ -0,0 +1,4 @@ +::: etuutt_bot.commands.admin + options: + members: + - Admin diff --git a/docs/reference/commands/misc.md b/docs/reference/commands/misc.md new file mode 100644 index 0000000..af3c9bc --- /dev/null +++ b/docs/reference/commands/misc.md @@ -0,0 +1,4 @@ +::: etuutt_bot.commands.misc + options: + members: + - Misc diff --git a/docs/reference/commands/role.md b/docs/reference/commands/role.md new file mode 100644 index 0000000..c127944 --- /dev/null +++ b/docs/reference/commands/role.md @@ -0,0 +1,4 @@ +::: etuutt_bot.commands.role + options: + members: + - Role diff --git a/docs/reference/services/channel.md b/docs/reference/services/channel.md new file mode 100644 index 0000000..717327a --- /dev/null +++ b/docs/reference/services/channel.md @@ -0,0 +1,4 @@ +::: etuutt_bot.services.channel + options: + members: + - ChannelService diff --git a/docs/reference/services/user.md b/docs/reference/services/user.md new file mode 100644 index 0000000..ef7b33f --- /dev/null +++ b/docs/reference/services/user.md @@ -0,0 +1,4 @@ +::: etuutt_bot.services.user + options: + members: + - UserService diff --git a/docs/reference/web/routes.md b/docs/reference/web/routes.md new file mode 100644 index 0000000..a203366 --- /dev/null +++ b/docs/reference/web/routes.md @@ -0,0 +1,15 @@ +::: etuutt_bot.routes.home + options: + members: + - handler + +::: etuutt_bot.routes.login + options: + members: + - handler + +::: etuutt_bot.routes.role + options: + members: + - ApiStudent + - handler diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css new file mode 100644 index 0000000..bfe4cb3 --- /dev/null +++ b/docs/stylesheets/extra.css @@ -0,0 +1,3 @@ +.mermaid { + text-align: center; +} diff --git a/docs/tutorials/doc.md b/docs/tutorials/doc.md new file mode 100644 index 0000000..5410ccd --- /dev/null +++ b/docs/tutorials/doc.md @@ -0,0 +1,69 @@ +## Compiler la documentation localement + +Si le projet est installé suivant les étapes décrite dans la section [Installation](install.md), +vous pouvez compiler la documentation localement en exécutant la commande suivante : + +```bash +mkdocs build +``` + +La documentation sera générée dans le dossier `site`. +Vous pouvez l'explorer dans votre navigateur en ouvrant le fichier `index.html`. + +## Editer la documentation + +La documentation est écrite en Markdown et est générée à l'aide de [MkDocs](https://www.mkdocs.org/). +Lorsque vous voulez travailler sur la documentation, vous pouvez lancer +le serveur de développement de MkDocs : + +```bash +mkdocs serve +``` + +Les changements apportés à la documentation seront automatiquement détectés et la page web +sera rechargée. + +Les fichiers de documentation sont situés dans le dossier `docs`. + +La documentation est écrite en Markdown, avec MkdDocs et l'extension Material. +Si vous n'êtes pas familiers avec ces technologies, veuillez consulter les documentations officielles : + +- [Markdown](https://www.markdownguide.org/) +- [MkDocs](https://www.mkdocs.org/) +- [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/) + +### Ajouter ou retirer un fichier + +Pour ajouter un fichier, créez un fichier Markdown dans le dossier `docs`. +Pour retirer un fichier, supprimez le fichier Markdown correspondant. +Après toute opération d'ajout ou de suppression, il est nécessaire +d'éditer la table des matières du fichier `mkdocs.yaml`. + +La partie à modifier dans le fichier `mkdocs.yaml` est la section `nav`. +Cette section contient une liste de liens vers les fichiers de documentation +avec les titres à afficher dans la table des matières. + +Par exemple, si vous voulez ajouter un fichier `foo.md` dans la section `Tutoriel`, +Votre section `nav` devra ressembler à ceci : + +```yaml +nav: + - Accueil: index.md + - Tutoriel: + - Installation: tutoriel/install.md + - foo: tutoriel/foo.md + - # ... +``` + +### Norme de documentation + +La documentation est écrite selon la méthode [Diataxis](https://diataxis.fr/). +Si vous n'êtes pas familiers avec, consultez-la. + +## Déployer la documentation + +Le déploiement de la documentation est automatique. + +Le site est déployé sur GitHub Pages à chaque push sur la branche `main`. + +Le workflow de déploiement est défini dans le fichier `.github/workflows/docs.yaml`. diff --git a/docs/tutorials/install.md b/docs/tutorials/install.md new file mode 100644 index 0000000..1fab839 --- /dev/null +++ b/docs/tutorials/install.md @@ -0,0 +1,361 @@ +## Prérequis + +Pour installer le projet, vous aurez besoin de : + +- git ([documentation officielle](https://git-scm.com/book/fr/v2/D%C3%A9marrage-rapide-Installation-de-Git)) +- Python >=3.10 ([documentation officielle](https://docs.python.org/3/)) + +Si une ou plusieurs de ces dépendances ne sont pas présentes sur votre +ordinateur, référez-vous aux instructions d'installation données ci-dessous. +Si toutes les dépendances sont présentes et à jour, vous pouvez passer +directement à [la partie suivante](#mise-en-place-du-projet). + +### Python + +Le projet est conçu pour fonctionner avec Python 3.10 ou au-dessus. + +Pour vérifier votre version de Python, exécutez la commande suivante dans votre terminal : + +```bash +python --version +``` + +Si la commande renvoie une version inférieure à 3.10 +ou si la commande n'est pas reconnue, +vous devez installer Python. + +=== "Windows" + + === "Avec l'exécutable" + + Rendez-vous sur le [site officiel de Python](https://www.python.org/downloads/), + téléchargez le programme d'installation et exécutez-le. + + !!!PATH + + Pensez à cocher la case `Add Python to PATH` lors de l'installation. + + === "Avec Winget" + + dans Powershell avec accès admins : + + ```powershell + winget install Python.Python + ``` + + === "Avec Scoop" + + dans Powershell : + + ```powershell + scoop install python + ``` + +=== "Linux" + + Sur Linux, vous pouvez installer Python à l'aide de votre gestionnaire de paquets. + + === "Debian/Ubuntu" + + ```bash + sudo apt install python3 + # on sait jamais + sudo apt install python-is-python3 + ``` + + Par défaut, Ubuntu 22.04 utilise Python 3.10. + Ce n'est pas la plus récente, mais elle est compatible + avec le projet. + + !!!note + + Comme le gestionnaire APT est ~~souvent en retard~~ stable, + il est conseillé d'utiliser `pyenv` si vous voulez utiliser + la version la plus à jour de Python. + Voir sa [documentation](https://github.com/pyenv/pyenv). + + Alternativement, il est possible d'ajouter le PPA + [deadsnakes](https://launchpad.net/~deadsnakes/+archive/ubuntu/ppa) + pour avoir un choix de versions de Python sur Ubuntu : + + ```bash + sudo add-apt-repository ppa:deadsnakes/ppa + ``` + + === "Fedora" + + ```bash + sudo dnf install python3 + ``` + + === "Arch Linux" + + ```bash + sudo pacman -Syu python + ``` + +=== "macOS" + + Sur macOS, vous pouvez installer Python à l'aide de + [Homebrew](https://brew.sh/). + + ```bash + brew install python + ``` + +### Git + +Git est un logiciel de gestion de versions décentralisé. +Il est utilisé pour gérer le code source du projet. + +Si Git n'est pas déjà installé, suivez les instructions suivantes : + +=== "Windows" + + === "Avec l'exécutable" + + Rendez-vous sur le + [site officiel de Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git), + téléchargez le programme d'installation et exécutez-le. + + === "Avec Winget" + + Dans Powershell avec accès admins : + + ```powershell + winget install Git.Git + ``` + + === "Avec Scoop" + + Dans Powershell : + + ```powershell + scoop install git + ``` + +=== "Linux" + + Sur Linux, vous pouvez installer Git à l'aide de votre gestionnaire de paquets. + + === "Debian/Ubuntu" + + ```bash + sudo apt install git + ``` + + === "Fedora" + + ```bash + sudo dnf install git + ``` + + === "Arch Linux" + + ```bash + sudo pacman -Syu git + ``` + +=== "macOS" + + Sur macOS, vous pouvez installer Git à l'aide de + [Homebrew](https://brew.sh/). + + ```bash + brew install git + ``` + +Pour vérifier que l'installation a réussi, +exécutez la commande suivante dans votre terminal : + +```bash +git --version +``` + +## Mise en place du projet + +Clonez le dépôt Git du projet : + +```bash +git clone https://github.com/ungdev/EtuUTT-Discord-Bot.git +``` + +Puis, placez-vous dans le répertoire : + +```bash +cd EtuUTT-Discord-Bot +``` + +Assurez-vous que vous êtes bien sur la branche `main` + +```bash +git checkout main +``` + +Puis installez les dépendances (de préférence dans un +[venv](https://docs.python.org/3/library/venv.html)) : + +``` +pip install -U -r requirements-dev.txt +``` + +## Configuration du projet + +Pour bien fonctionner, le bot a besoin d'un peu de configuration. +Deux fichiers contiennent des exemples de ce qui est attendu comme +configuration. +Copiez ces fichiers : + +```bash +cp .env.example .env +cp data/discord.example.toml data/discord.toml +``` + +### Où est la configuration ? + +La configuration est répartie entre les deux fichiers ainsi créés, +en suivant cet esprit : + +- Le `.env` contient spécifiquement les variables d'environnement. + On y met les variables qui doivent être les plus aisément accessibles + et modifiables par le SIA (Service d'Information des Associations). + Par exemple, les identifiants et mots de passe, le niveau de log et le token du bot. +- le `discord.toml` contient les variables relatives au bot et au serveur + qui n'ont pas un besoin impératif d'être modifiées par le SIA. + Par exemple, l'id du serveur, les ids des rôles spéciaux + où les catégories destinées à avoir des salons d'UE. + +Pour savoir où une variable doit se trouver, imaginez-vous en tant qu'admin du serveur, +puis imaginez-vous en tant que respo SIA. +Ensuite, demandez-vous dans lequel de ces rôles, +vous auriez le plus besoin d'accéder à cette variable. + +Si c'est en tant qu'admin Discord, la variable va dans le `discord.toml`. +Si c'est en tant que respo SIA, la variable va dans le `.env`. + +### Créer un bot + +Une grande partie de la configuration implique de conserver en dur +des variables relatives directement à Discord. + +Nous vous conseillons donc de créer un bot et un serveur rien qu'à vous, +qui vous serviront uniquement à faire vos tests. +La création du serveur est une tâche facile et laissée comme +exercice au lecteur. +Pour la création du bot, rendez-vous sur +[le portail des développeurs](https://discord.com/developers/applications). + +Ça devrait ressembler à ça : + +
+ ![image](../img/tutorials/discord_developer_portal.jpg) +
Portail des applications de Discord
+
+ +Cliquez sur le bouton `New Application` en haut à droite. +Rentrez le nom de votre bot, acceptez les conditions de Discord et validez. + +Sur la page de votre application, rendez-vous dans l'onglet `Bot`. +Si vous voulez, choisissez une icône pour votre bot. +Générez un token et copiez-le dans la variable `BOT__TOKEN` du `.env` + +!!!danger + + Le token de votre bot ne doit **JAMAIS** être visible par quelqu'un d'autre. + Si votre bot est récupéré une personne mal intentionnée, ça peut être + extrêmement dangereux. + Faites bien attention à ça. + + Rassurez-vous cependant, si jamais vous faites une erreur + et que votre token se retrouve sur un repo GitHub ou dans un message + Discord, Discord le détectera immédiatement et révoquera le token + de votre bot sans attendre. + Votre bot sera toujours utilisable, mais à condition de générer un nouveau token. + +Une fois le token de votre bot renseigné, +configurez les *Intents*. +Le bot EtuUTT a besoin de tous les *Intents*. + +
+ ![intents](../img/tutorials/intents.png) +
*Intents* requis
+
+ +!!!note + + Vous pouvez décocher la case `Public Bot` afin d'éviter que + d'autres personnes que vous puissent ajouter votre bot de test + dans des serveurs. + +Maintenant, rendez-vous dans l'onglet `Oauth2`, +à la section `URL Generator`. +Renseignez le scope de votre application, +puis les permissions requises. +Le scope `bot` est nécessaire. +Pour les permissions, vous pouvez lui donner la permission `admin`, +qui inclue toutes les autres. + +
+ ![scopes et permissions](../img/tutorials/scopes_and_perms.png) +
Scope et permissions
+
+ +!!!note + + Si jamais vous créez un jour un bot destiné à être réellement utilisé, + évitez de lui donner la permission administrateur. + Préférez donner exactement les permissions nécessaires et pas une de plus. + + Mais ici, on crée un bot de test, donc ça ne pose pas trop + de problème et ça simplifie la configuration. + +Copiez-collez l'URL générée dans un onglet de votre navigateur. +Vous serez redirigé vers la page d'invitation du bot sur un serveur. +Sélectionnez votre serveur de test. + +Et voilà, votre bot est prêt à fonctionner. + +### Configurer le serveur + +Maintenant que vous avez votre bot et votre serveur, +vous allez pouvoir remplir le `discord.toml`. + +Pour cela, vous devez faire les actions suivantes sur votre serveur : + +1. Copiez-collez l'id du serveur dans `guild.id` +2. Créez un salon textuel et copiez-collez son id dans `guild.channel_admin_id` +3. Générez une invitation et copiez-collez la dans `guild.invite_link` +4. Créez cinq rôles, et copiez-collez leur id dans les cinq variables + de la catégorie `guild.special_roles` +5. Créez deux catégories, nommées respectivement `ME` et `TC` + et copiez-collez leur id dans les variables `id` des deux `[[categories]]` correspondantes +6. Créez deux nouveaux rôles, et copiez-collez leur id dans les variables + `elected_role` des deux `[[categories]]` + +Si vous voulez, vous pouvez rajouter plus de salons et de catégories, mais +ce n'est pas nécessaire pour le bon fonctionnement du bot. + +!!!question "Je ne trouve pas les ids" + + L'id d'un salon, d'une catégorie, d'un utilisateur et de presque tout sur Discord + se trouve en faisant un clic droit sur l'objet et en cliquant sur + "Copier l'identifiant du [message/salon/utilisateur/...]" dans le menu contextuel. + Cette option devrait être celle tout en bas. + + Si vous ne la voyez pas, vous devez vous rendre dans vos paramètres utilisateur, + puis `Paramètres de l'appli` > `Avancés` et activer le mode développeur. + +## Lancement du bot + +Maintenant que tout est configuré, vous pouvez lancer le bot. +Pour cela, exécutez la commande suivante dans votre terminal : + +```bash +python -m etuutt_bot +``` + +Rendez-vous dans le salon que vous avez configuré comme étant celui +dédié à l'administration du serveur. +Le bot devrait y avoir posté un message pour signaler sa mise en ligne. + +Si ça ne marche pas, vous pouvez trouver les logs dans le fichier `logs/log`. diff --git a/etuutt_bot/commands/admin.py b/etuutt_bot/commands/admin.py index 5d36b7e..8257922 100644 --- a/etuutt_bot/commands/admin.py +++ b/etuutt_bot/commands/admin.py @@ -9,7 +9,7 @@ from etuutt_bot.bot import EtuUTTBot -class Admin(commands.Cog): +class AdminCog(commands.Cog): def __init__(self, bot: EtuUTTBot) -> None: self.bot = bot diff --git a/etuutt_bot/commands/misc.py b/etuutt_bot/commands/misc.py index 422fc58..4ee7891 100644 --- a/etuutt_bot/commands/misc.py +++ b/etuutt_bot/commands/misc.py @@ -11,7 +11,7 @@ from etuutt_bot.bot import EtuUTTBot -class Misc(commands.Cog): +class MiscCog(commands.Cog): def __init__(self, bot: EtuUTTBot) -> None: self.bot = bot # Circumvent impossibility to add context menu commands in cogs diff --git a/etuutt_bot/commands/role.py b/etuutt_bot/commands/role.py index 09b64d8..c40a80d 100644 --- a/etuutt_bot/commands/role.py +++ b/etuutt_bot/commands/role.py @@ -16,7 +16,7 @@ # define command group based on the Group class @app_commands.default_permissions(administrator=True) -class Role( +class RoleCog( commands.GroupCog, name="role", description="Commandes liées à la gestion des rôles (et des salons associés)", diff --git a/etuutt_bot/commands_list.py b/etuutt_bot/commands_list.py index 5c75e39..5dba738 100644 --- a/etuutt_bot/commands_list.py +++ b/etuutt_bot/commands_list.py @@ -4,9 +4,9 @@ import discord -from etuutt_bot.commands.admin import Admin -from etuutt_bot.commands.misc import Misc -from etuutt_bot.commands.role import Role +from etuutt_bot.commands.admin import AdminCog +from etuutt_bot.commands.misc import MiscCog +from etuutt_bot.commands.role import RoleCog if TYPE_CHECKING: from etuutt_bot.bot import EtuUTTBot @@ -17,12 +17,12 @@ # List of commands to add to the command tree async def commands_list(bot: EtuUTTBot): # List the commands and commands groups - cogs: tuple = (Admin(bot), Misc(bot)) + cogs: tuple = (AdminCog(bot), MiscCog(bot)) # Add the cogs to the bot for cog in cogs: await bot.add_cog(cog) - await bot.add_cog(Role(bot), guild=bot.watched_guild) + await bot.add_cog(RoleCog(bot), guild=bot.watched_guild) # Create a global commands error handler @bot.tree.error diff --git a/etuutt_bot/routes/role.py b/etuutt_bot/routes/role.py index 1d54576..d379c53 100644 --- a/etuutt_bot/routes/role.py +++ b/etuutt_bot/routes/role.py @@ -11,7 +11,7 @@ from etuutt_bot.bot import EtuUTTBot -class ApiUser(BaseModel): +class ApiUserSchema(BaseModel): is_student: bool = Field(alias="isStudent") first_name: str = Field(alias="firstName") last_name: str = Field(alias="lastName") @@ -56,7 +56,7 @@ async def handler(req: web.Request) -> web.Response: return web.Response(status=response.status) try: resp = (await response.json()).get("data") - api_student = ApiUser.model_validate(resp) + api_student = ApiUserSchema.model_validate(resp) except ValidationError: return web.HTTPBadRequest() diff --git a/etuutt_bot/services/user.py b/etuutt_bot/services/user.py index ff2c541..0884084 100644 --- a/etuutt_bot/services/user.py +++ b/etuutt_bot/services/user.py @@ -11,7 +11,7 @@ from discord import Role from etuutt_bot.bot import EtuUTTBot - from etuutt_bot.routes.role import ApiUser + from etuutt_bot.routes.role import ApiUserSchema class UserService: @@ -20,7 +20,7 @@ class UserService: def __init__(self, bot: EtuUTTBot): self._bot = bot - def get_server_nickname(self, user: ApiUser) -> str: + def get_server_nickname(self, user: ApiUserSchema) -> str: pseudo = f"{user.first_name.title()} {user.last_name.upper()}" member_type = user.member_type if member_type == MemberType.Student: @@ -40,7 +40,7 @@ def get_server_nickname(self, user: ApiUser) -> str: pseudo = pseudo[: self.NICKNAME_MAX_LEN] return pseudo - def get_member_roles(self, user: ApiUser) -> set[Role]: + def get_member_roles(self, user: ApiUserSchema) -> set[Role]: """Retourne les rôles qui devraient être attribués à l'utilisateur donné. Args: @@ -69,7 +69,7 @@ def get_member_roles(self, user: ApiUser) -> set[Role]: return {guild.get_role(special_ids.teacher)} assert_never(member_type) - async def sync(self, member: discord.Member, user: ApiUser): + async def sync(self, member: discord.Member, user: ApiUserSchema): """Synchronise le membre du serveur avec les données de l'api du site etu. Args: diff --git a/mkdocs.yaml b/mkdocs.yaml new file mode 100644 index 0000000..f88ad70 --- /dev/null +++ b/mkdocs.yaml @@ -0,0 +1,99 @@ +site_name: EtuUTT bot Discord +site_description: Le bot du serveur Discord des étudiants de l'UTT + +repo_name: EtuUTT-Discord-Bot + +theme: + features: + - navigation.footer + - content.code.annotate + - content.code.copy + - content.tabs.link + name: material + palette: + - media: "(prefers-color-scheme: light)" + scheme: default + primary: deeppurple + accent: deeppurple + toggle: + icon: material/toggle-switch + name: Switch to dark mode + + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: blue + accent: blue + toggle: + icon: material/toggle-switch-off-outline + name: Switch to light mode + + language: fr + icon: + repo: fontawesome/brands/git-alt + favicon: img/favicon.png + + +plugins: + - mkdocstrings: + default_handler: python + handlers: + python: + options: + members: true + members_order: source + show_source: true + show_inherited_members: true + merge_init_into_class: true + show_root_toc_entry: false + - search +nav: + - Accueil: index.md + - Explications: + - Accueil: explanation/index.md + - Technologies utilisées: explanation/technos.md + - Conventions: explanation/conventions.md + - Authentification par le site etu: explanation/etu-auth.md + - Tutoriels: + - Installer le projet: tutorials/install.md + - Utiliser la documentation: tutorials/doc.md + - Utilisation: howto/index.md + - Reference: + - Services: + - reference/services/channel.md + - reference/services/user.md + - Commandes: + - reference/commands/admin.md + - reference/commands/misc.md + - reference/commands/role.md + - Serveur web: reference/web/routes.md + + +markdown_extensions: + - pymdownx.highlight: + anchor_linenums: true + line_spans: __span + pygments_lang_class: true + - admonition + - attr_list + - def_list + - md_in_html + - pymdownx.details + - pymdownx.inlinehilite + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format + - pymdownx.tabbed: + alternate_style: true + - pymdownx.tasklist: + clickable_checkbox: true + - pymdownx.emoji: + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg + - toc: + permalink: true + toc_depth: 3 + +extra_css: + - stylesheets/extra.css diff --git a/requirements-dev.txt b/requirements-dev.txt index 1a9c933..3fadab9 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,3 +1,4 @@ -r requirements.txt +-r requirements-docs.txt pre-commit ~= 3.7 ruff == 0.3.7 diff --git a/requirements-docs.txt b/requirements-docs.txt new file mode 100644 index 0000000..ffcfabf --- /dev/null +++ b/requirements-docs.txt @@ -0,0 +1,4 @@ +mkdocs ~= 1.5.3 +mkdocs-material ~= 9.5.17 +mkdocstrings ~= 0.24.3 +mkdocstrings-python ~= 1.9.2