Skip to content

feat(seniority): recalcul de l'ancienneté à la demande - #399

Merged
AntoineJT merged 1 commit into
masterfrom
feat/seniority-recompute
Nov 10, 2026
Merged

AntoineJT merged 1 commit into
masterfrom
feat/seniority-recompute

Conversation

@AntoineJT

@AntoineJT AntoineJT commented Nov 10, 2026 •

Copy link
Copy Markdown
Member

🇫🇷 Français

🎯 Résumé

Ajoute la commande admin /seniority-admin recompute membre:. Elle fait partir l'ancienneté d'un membre de son premier message sur le serveur, quand celui-ci précède son arrivée actuelle. La date recalculée sert aux fêtes d'ancienneté et à la « plus ancienne présence » de la carte du serveur.

C'est le deuxième niveau d'une pile : elle s'appuie sur #401 (la recherche de messages en bouche-trou), et #402 ajoute par-dessus la conservation d'un an après le départ. B et C sortent dans la même release.

🤔 Motivation

Discord remet la date d'arrivée à zéro quand un membre quitte puis revient : un membre présent depuis 7 ans peut être fêté pour 1 an. Son premier message encore sur le serveur donne une date plus juste. Le recalcul se fait à la demande, membre par membre. On a écarté une recherche à chaque arrivée : sur un serveur de 13 000 membres, ce serait des milliers de recherches.

📦 Contenu

  • ✨ Recalcul. La date retenue est la plus ancienne entre l'arrivée, le premier message trouvé et un recalcul précédent. Le modèle SeniorityOverride et sa migration sont ajoutés. Réponses distinctes :

    • « pas prêt » : Discord indexe encore les messages, ce qui peut prendre des heures ;
    • « accès refusé » : l'accès Message Content est à activer ;
    • « résultat partiel » ;
    • échec.

    Le membre n'est refusé comme absent que si Discord le dit. Un bot n'est jamais recalculé.

  • 🗓️ Fêtes. Un membre recalculé est fêté, et classé dans les groupes d'années, selon la date de son premier message.

  • 🔒 Vie privée.

    • La date part avec /privacy delete, dont la confirmation le dit.
    • Elle part aussi quand on désactive le module ; /modules demande alors confirmation.
    • La politique de confidentialité indique sa base légale : l'intérêt légitime.
    • Elle précise aussi que la recherche renvoie des messages entiers, dont seule la date la plus ancienne sert ; le contenu n'est ni utilisé ni conservé.
  • 🔕 Aucun intent ajouté. La recherche dépend de l'accès Message Content de l'application, pas d'un intent de gateway.

  • 🔢 Le module passe en 1.1.0. Sans ce changement de version, /seniority-admin n'aurait jamais été enregistrée sur les serveurs où le module est déjà activé.

  • 📝 Doc : functional.md, politique de confidentialité (fr et en), guide d'installation (fr et en).

  • ✅ Validation :

    • plusieurs passes de revue corrective, avant et après le découpage ;
    • 2 317 tests ; build, oxlint, oxfmt et build de la doc OK ;
    • migration appliquée sur un Postgres jetable, sans écart avec le schéma.

🧭 À noter

  • ⚠️ Dans cette PR seule, la date est gardée sans limite après le départ. feat(seniority): ancienneté recalculée supprimée un an après le départ #402, qui sort dans la même release, la supprime un an après.
  • ⚠️ La limite d'une minute de la commande borne l'attente de l'indexation, mais pas les attentes de discord.js quand Discord limite le débit.
  • ⚠️ L'année du recalcul, un membre peut être fêté deux fois, ou pas du tout : le jour fêté devient celui de son premier message.

🇬🇧 English

🎯 Summary

Adds the admin command /seniority-admin recompute member:. It makes a member's seniority count from their first message on the server, when that message predates their current arrival. The recomputed date is used by the seniority celebrations and by the server card's "longest-standing member".

This is the second level of a stack: it builds on #401 (message search as a polyfill), and #402 adds on top the one-year retention after leaving. B and C ship in the same release.

🤔 Motivation

Discord resets the join date of a member who leaves and comes back: a member who has been around for 7 years can be celebrated for 1. Their first message still on the server gives a more accurate date. The recompute runs on demand, one member at a time. A search on every join was ruled out: on a 13,000-member server, that would mean thousands of searches.

📦 What's included

  • ✨ Recompute. The date kept is the earliest of the arrival, the first message found and a previous recompute. The SeniorityOverride model and its migration are added. Distinct replies:

    • "not ready": Discord is still indexing the messages, which can take hours;
    • "access denied": Message Content access needs to be enabled;
    • "partial result";
    • failure.

    A member is refused as absent only when Discord says so. A bot is never recomputed.

  • 🗓️ Celebrations. A recomputed member is celebrated, and grouped by years, according to the date of their first message.

  • 🔒 Privacy.

    • The date is deleted by /privacy delete, whose confirmation now says so.
    • It is also deleted when the module is disabled; /modules asks for confirmation first.
    • The privacy policy states its legal basis: legitimate interest.
    • It also says the search returns whole messages, of which only the earliest date is used; the content is neither used nor kept.
  • 🔕 No intent added. The search depends on the application's Message Content access, not on a gateway intent.

  • 🔢 The module moves to 1.1.0. Without the version change, /seniority-admin would never have been registered on servers where the module is already enabled.

  • 📝 Docs: functional.md, privacy policy (fr and en), getting-started guide (fr and en).

  • ✅ Validation:

    • several corrective review passes, before and after the split;
    • 2,317 tests; build, oxlint, oxfmt and the docs build pass;
    • migration applied on a throwaway Postgres, with no drift from the schema.

🧭 Notes

  • ⚠️ In this PR alone, the date is kept with no limit after leaving. feat(seniority): ancienneté recalculée supprimée un an après le départ #402, which ships in the same release, deletes it a year later.
  • ⚠️ The command's one-minute limit bounds the wait for indexing, but not discord.js's waits when Discord rate-limits.
  • ⚠️ In the year of a recompute, a member can be celebrated twice, or not at all: the celebrated day becomes that of their first message.

📋 Checklist

  • 🇫🇷 Sur le serveur de test, lancer /seniority-admin recompute sur un membre parti puis revenu / 🇬🇧 On the test server, run /seniority-admin recompute on a member who left and came back
  • 🇫🇷 Vérifier que l'accès Message Content est activé pour l'application de production / 🇬🇧 Check that Message Content access is enabled for the production application

@AntoineJT AntoineJT added type: module Add a new module or edit an existing one scope: add features Add new features labels Nov 10, 2026
@AntoineJT
AntoineJT force-pushed the feat/seniority-recompute branch 2 times, most recently from 6a67629 to e1a44d4 Compare November 10, 2026 03:27
@AntoineJT
AntoineJT changed the base branch from master to feat/discord-search-polyfill November 10, 2026 03:29
@AntoineJT
AntoineJT force-pushed the feat/seniority-recompute branch from e1a44d4 to 32c8db9 Compare November 10, 2026 03:32
@AntoineJT
AntoineJT added this pull request to stack #403 November 10, 2026 03:38
@AntoineJT
AntoineJT force-pushed the feat/seniority-recompute branch from 32c8db9 to 0da4a35 Compare November 10, 2026 03:39
Base automatically changed from feat/discord-search-polyfill to master November 10, 2026 03:44
/seniority-admin recompute fait partir l'ancienneté d'un membre de son
premier message quand il précède son arrivée actuelle, que Discord
remet à zéro au retour d'un membre parti. La date retenue est la plus
ancienne entre l'arrivée, ce premier message et un recalcul précédent.
Elle sert aux fêtes d'ancienneté et à la plus ancienne présence de la
carte du serveur.

Un membre à la fois, sur demande d'un administrateur : un recalcul de
tout le serveur ferait des milliers de recherches et ressemblerait à
une collecte de son historique. La date est gardée après un départ,
pour rester juste au retour ; /privacy delete et la désinstallation la
suppriment, cette dernière après confirmation dans /modules. Aucun
intent de plus : la recherche dépend de l'accès Message Content de
l'application.

Le module passe en 1.1.0 : sans changement de version, la commande ne
serait pas enregistrée sur les serveurs où il est déjà activé.
@AntoineJT
AntoineJT force-pushed the feat/seniority-recompute branch from 0da4a35 to d1089ec Compare November 10, 2026 03:44
@AntoineJT
AntoineJT merged commit 4013416 into master Nov 10, 2026
17 checks passed
@AntoineJT
AntoineJT deleted the feat/seniority-recompute branch November 10, 2026 03:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: add features Add new features type: module Add a new module or edit an existing one

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant