Conversation
* Fix(lang/help): wrong pronoun used #102 * refactor(crownicles): dedupe data loaders via cachedPromise helper Replace the promise-cache-and-evict boilerplate, copy-pasted across 6 loaders, with a single cachedPromise() helper in data/source.ts. The five single-value loaders (loadNames, loadItemIcons, loadMapTypeIcons, getCrowniclesMapGraph, getContinentGraph) memoize through it; getItems keeps a per-category Map of memoized loaders. Caching, deduplication of concurrent calls and eviction-on-failure now live in one place. Also drops the now-dead module-level promise variables, restores the export accidentally lost on loadItemIcons, and documents the helper. Closes #58 * refactor(crownicles): share the CrowniclesIcons parser between loaders Extract crownicles-icons-source.ts (ICONS_SOURCE_PATH, fetchIconsSource, extractIconBlock) so the item and map-type icon loaders stop each re-implementing the `} = {` split + brace-matching + pair regex. The key pattern is parameterized (\d+ for item ids, default \w+ for map codes), so behaviour is unchanged. Closes #61 * docs: tidy comments and fill JSDoc gaps The codebase was already well-commented, so this is a focused pass: - document computeLeagueBonusScore (as a Crownicles-formula mirror) and CROWNICLES_LEAGUES / CrowniclesLeague - rewrite the terse CrowniclesMap* interface docs and the HelpState path* field comments (incl. the "choosed" typo); drop the redundant `// BFS` marker - add concise JSDoc to ItemCategory, TitleSize, TextInputStyleName, the /mail modal ids, the reminder result types, pathfinderPage and the mail-repository helpers; move the misplaced homePage doc onto the export Closes #79 Closes #80 Closes #81 Closes #82 Closes #83
Adds the data command (slash + prefix), backed by a new gdpr-repository exposing GdprExport and a 31-day request cooldown. Ban hashes and the cooldown are now keyed by a salted hash of the Discord id (shared via core/security/hash-user-id) instead of pseudoId/userId, so both survive data erasure. Updates the privacy policy to match what is actually collected, and factors the register/rank command duplication between the slash and prefix fronts using the pipeline's gate mechanism.
- Remove the command gate/onGateDenied hook; inline register's "already registered" check without the non-null assertion / TOCTOU - Share the denial-handler wiring between the slash and prefix fronts - Wrap the whole pipeline so a failed denial reply also surfaces through onUnexpectedError instead of being lost - Add owner-only maintenance command (slash + prefix) to drive maintenance mode, which had no runtime toggle - Restrict data to dm scope so the prefix front stops publishing personal GDPR data in public channels - Make bans idempotent and drop the duplicate rank write in banUser - Read a user's language and rank in a single query (getUserProfile) - Remove dead code (safeReply*, delete-data exemption) and unify factory naming, barrel imports, prefix exports and JSDoc
- Add a generic TtlCache: LRU eviction (bounded memory) and a per-entry TTL (stale rows self-heal after out-of-band edits) - Replace the per-field user maps with a single User-row cache so language and rank share one entry and one invalidation - Route the legal-acceptance check through it, caching hits and misses
…110 Rank, RANK, isRank, NotBannedRank, AssignableRank, ASSIGNABLE_RANKS and isAssignableRank were declarative types with no authorization decision attached, but lived in core/permissions/rank.ts and leaked into repositories, context and pipeline layers that only ever used them as plain data - mirroring how SupportedLocale already lives in core/types.ts. Authorization is now an alias of NotBannedRank instead of a duplicate Exclude<Rank, 'banned'>. hasRank stays the only export left in core/permissions/rank.ts, since it is the one function actually used to make an authorization decision.
usecases/ and presentations/ scattered each command's logic across two parallel flat directories, and adding a command meant touching a barrel file in each. Each command now owns a discord/features/<name>/ folder with <name>.service.ts (was usecases/) and <name>.ui.ts (was presentations/); legal/ has no .ui.ts since it reuses register's. commands/slash/ and commands/prefix/ stay flat since the runtime loader discovers commands via a directory scan, not static imports. lang/en/ and lang/fr/ stay in place too, since grouping strings by locale is what you actually want when reviewing translations - but adding a command previously meant editing lang/en/index.ts AND lang/fr/index.ts separately, and forgetting one only fails silently for that locale. lang/packs.ts now lists both locales for a command side by side, so there is exactly one place to touch and no way to add one locale without the other.
French and English disagree on which count is singular - French treats
0 as singular ("0 jour") while English treats it as plural ("0 days") -
so a naive count === 1 check is wrong for French. plural() delegates to
Intl.PluralRules, which already encodes each locale's real CLDR rule.
Not wired into any message yet; ready for the first command that needs
it.
Latency was interpolated as a raw number ("1234ms"), the one spot in the
bot that displays a runtime number to users. formatNumber() delegates
to Intl.NumberFormat so it reads "1,234ms" in English and "1 234ms" in
French, matching each locale's convention.
buildGdprExport went through getUserProfile, whose cache fabricates and stores en/normal for anyone with no User row - fine for permission checks, but wrong for a GDPR disclosure, which must report what is truly stored. findStoredUserProfile reads the row directly, uncached, and returns null instead of a default. GdprExport.language/rank are now nullable; the export shows a single "nothing stored" notice when there is truly nothing (row, legal acceptance, ban hash all absent) - reachable since data is exempt from the legal gate.
/data and c!data take an optional target now, owner-only: a Discord user option on the slash side, a mention/id argument or an interactive UserSelectMenu picker on the prefix side (which has neither). Looking up someone else's data never touches their own 31-day cooldown - it isn't their request. Delivery matches what each path can do: the slash option and the picker's ephemeral component-interaction reply need nothing extra; the mention/id argument goes through the prefix front's existing DM relay, since a plain message reply can't be ephemeral. sendResponseToInteraction now takes any RepliableInteraction instead of just ChatInputCommandInteraction, so the picker can reuse it.
…110 When reduce() called both markHandled() and stop(), neither re-render happened: the inline update() was skipped because the interaction was already handled, and the end handler assumed stop() had already rendered the disabled state, so it skipped too. disabledRendered now tracks whether that render actually reached the message, so the end handler only skips when it's true. Also: unauthorized clicks now go through the collector's own filter instead of being checked inside the 'collect' handler, so they no longer reset the idle timer or count toward the collected items - discord.js only does either for interactions the filter accepts. The refusal reply moves to the 'ignore' event, which fires for whatever the filter rejects.
Let a command response be an ordered list of top-level Components V2 entries (containers, action rows, sections, text, separators) instead of a single container, so a message can mix them in any order. Every fluent component now exposes build(), CommandResponse gains a components list beside the single-container sugar, and the interactive-message runtime renders either shape. Rename EmbedColor to ColorName to match its use as a component accent, not just an embed colour.
Render the "DRING" message as a container (content + owner mention) plus a separate relaunch action row below it, using the new top-level component API. Rename buildReminderTriggeredContainer to buildReminderTriggeredComponents, give the relaunch button its own icon, and let delivery forward whichever shape render returns.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.