@azurioh/discord-kernel 0.1.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +318 -1
- package/dist/authz/require-level-guard.d.ts +1 -1
- package/dist/authz/require-level-guard.d.ts.map +1 -1
- package/dist/color.d.ts +42 -0
- package/dist/color.d.ts.map +1 -0
- package/dist/color.js +130 -0
- package/dist/color.js.map +1 -0
- package/dist/discord/command/all-required.d.ts +8 -0
- package/dist/discord/command/all-required.d.ts.map +1 -0
- package/dist/discord/command/all-required.js +12 -0
- package/dist/discord/command/all-required.js.map +1 -0
- package/dist/discord/command/autocomplete-dispatcher.d.ts.map +1 -1
- package/dist/discord/command/autocomplete-dispatcher.js +2 -1
- package/dist/discord/command/autocomplete-dispatcher.js.map +1 -1
- package/dist/discord/command/autocomplete-limits.d.ts +3 -0
- package/dist/discord/command/autocomplete-limits.d.ts.map +1 -0
- package/dist/discord/command/autocomplete-limits.js +6 -0
- package/dist/discord/command/autocomplete-limits.js.map +1 -0
- package/dist/discord/command/autocomplete.d.ts +6 -1
- package/dist/discord/command/autocomplete.d.ts.map +1 -1
- package/dist/discord/command/autocomplete.js +6 -6
- package/dist/discord/command/autocomplete.js.map +1 -1
- package/dist/discord/command/command-responders.d.ts +26 -0
- package/dist/discord/command/command-responders.d.ts.map +1 -0
- package/dist/discord/command/command-responders.js +16 -0
- package/dist/discord/command/command-responders.js.map +1 -0
- package/dist/discord/command/context.d.ts +8 -17
- package/dist/discord/command/context.d.ts.map +1 -1
- package/dist/discord/command/context.js +8 -37
- package/dist/discord/command/context.js.map +1 -1
- package/dist/discord/command/cooldown-guard.d.ts +1 -1
- package/dist/discord/command/cooldown-guard.d.ts.map +1 -1
- package/dist/discord/command/create-command.d.ts +1 -8
- package/dist/discord/command/create-command.d.ts.map +1 -1
- package/dist/discord/command/create-command.js +17 -65
- package/dist/discord/command/create-command.js.map +1 -1
- package/dist/discord/command/create-context-menu-command.d.ts +3 -9
- package/dist/discord/command/create-context-menu-command.d.ts.map +1 -1
- package/dist/discord/command/create-context-menu-command.js +14 -65
- package/dist/discord/command/create-context-menu-command.js.map +1 -1
- package/dist/discord/command/errors.d.ts +1 -1
- package/dist/discord/command/errors.d.ts.map +1 -1
- package/dist/discord/command/errors.js +2 -2
- package/dist/discord/command/errors.js.map +1 -1
- package/dist/discord/command/guard.d.ts +15 -8
- package/dist/discord/command/guard.d.ts.map +1 -1
- package/dist/discord/command/guard.js +4 -4
- package/dist/discord/command/guard.js.map +1 -1
- package/dist/discord/command/options.d.ts +1 -10
- package/dist/discord/command/options.d.ts.map +1 -1
- package/dist/discord/command/options.js +127 -320
- package/dist/discord/command/options.js.map +1 -1
- package/dist/discord/command/passes-guard.d.ts +14 -0
- package/dist/discord/command/passes-guard.d.ts.map +1 -0
- package/dist/discord/command/passes-guard.js +27 -0
- package/dist/discord/command/passes-guard.js.map +1 -0
- package/dist/discord/command/permission-guard.d.ts +1 -1
- package/dist/discord/command/permission-guard.d.ts.map +1 -1
- package/dist/discord/command/render-command-failure.d.ts +23 -0
- package/dist/discord/command/render-command-failure.d.ts.map +1 -0
- package/dist/discord/command/render-command-failure.js +40 -0
- package/dist/discord/command/render-command-failure.js.map +1 -0
- package/dist/discord/command/route-key.d.ts +8 -0
- package/dist/discord/command/route-key.d.ts.map +1 -0
- package/dist/discord/command/route-key.js +13 -0
- package/dist/discord/command/route-key.js.map +1 -0
- package/dist/discord/command/router.d.ts +32 -7
- package/dist/discord/command/router.d.ts.map +1 -1
- package/dist/discord/command/router.js +48 -10
- package/dist/discord/command/router.js.map +1 -1
- package/dist/discord/command/send-embed.d.ts +14 -0
- package/dist/discord/command/send-embed.d.ts.map +1 -0
- package/dist/discord/command/send-embed.js +37 -0
- package/dist/discord/command/send-embed.js.map +1 -0
- package/dist/discord/command/types.d.ts +7 -1
- package/dist/discord/command/types.d.ts.map +1 -1
- package/dist/discord/components/component-access.d.ts +12 -0
- package/dist/discord/components/component-access.d.ts.map +1 -0
- package/dist/discord/components/component-access.js +21 -0
- package/dist/discord/components/component-access.js.map +1 -0
- package/dist/discord/components/component-router.d.ts +37 -15
- package/dist/discord/components/component-router.d.ts.map +1 -1
- package/dist/discord/components/component-router.js +44 -32
- package/dist/discord/components/component-router.js.map +1 -1
- package/dist/discord/components/interactive-message/index.d.ts +3 -1
- package/dist/discord/components/interactive-message/index.d.ts.map +1 -1
- package/dist/discord/components/interactive-message/index.js +5 -3
- package/dist/discord/components/interactive-message/index.js.map +1 -1
- package/dist/discord/components/interactive-message/interactive-message-collector.d.ts +3 -28
- package/dist/discord/components/interactive-message/interactive-message-collector.d.ts.map +1 -1
- package/dist/discord/components/interactive-message/interactive-message-collector.js +6 -53
- package/dist/discord/components/interactive-message/interactive-message-collector.js.map +1 -1
- package/dist/discord/components/interactive-message/message-edit-options.d.ts +18 -0
- package/dist/discord/components/interactive-message/message-edit-options.d.ts.map +1 -0
- package/dist/discord/components/interactive-message/message-edit-options.js +40 -0
- package/dist/discord/components/interactive-message/message-edit-options.js.map +1 -0
- package/dist/discord/components/interactive-message/mount-interactive-message.d.ts +2 -1
- package/dist/discord/components/interactive-message/mount-interactive-message.d.ts.map +1 -1
- package/dist/discord/components/interactive-message/mount-interactive-message.js +4 -2
- package/dist/discord/components/interactive-message/mount-interactive-message.js.map +1 -1
- package/dist/discord/components/interactive-message/state-store.d.ts +12 -0
- package/dist/discord/components/interactive-message/state-store.d.ts.map +1 -0
- package/dist/discord/components/interactive-message/state-store.js +14 -0
- package/dist/discord/components/interactive-message/state-store.js.map +1 -0
- package/dist/discord/components/paginator/mount-paginator.js +7 -5
- package/dist/discord/components/paginator/mount-paginator.js.map +1 -1
- package/dist/discord/components/paginator/paginator-view.d.ts +1 -1
- package/dist/discord/components/paginator/paginator-view.d.ts.map +1 -1
- package/dist/discord/components/persistent-paginator.d.ts +1 -1
- package/dist/discord/components/persistent-paginator.d.ts.map +1 -1
- package/dist/discord/components/persistent-paginator.js +6 -6
- package/dist/discord/components/persistent-paginator.js.map +1 -1
- package/dist/discord/components/settings-editor/from-declaration-controls.d.ts +67 -0
- package/dist/discord/components/settings-editor/from-declaration-controls.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration-controls.js +245 -0
- package/dist/discord/components/settings-editor/from-declaration-controls.js.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration-display.d.ts +23 -0
- package/dist/discord/components/settings-editor/from-declaration-display.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration-display.js +114 -0
- package/dist/discord/components/settings-editor/from-declaration-display.js.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration-layout.d.ts +33 -0
- package/dist/discord/components/settings-editor/from-declaration-layout.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration-layout.js +178 -0
- package/dist/discord/components/settings-editor/from-declaration-layout.js.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration-search.d.ts +27 -0
- package/dist/discord/components/settings-editor/from-declaration-search.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration-search.js +58 -0
- package/dist/discord/components/settings-editor/from-declaration-search.js.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration-stored.d.ts +21 -0
- package/dist/discord/components/settings-editor/from-declaration-stored.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration-stored.js +37 -0
- package/dist/discord/components/settings-editor/from-declaration-stored.js.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration-subject.d.ts +32 -0
- package/dist/discord/components/settings-editor/from-declaration-subject.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration-subject.js +65 -0
- package/dist/discord/components/settings-editor/from-declaration-subject.js.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration-values.d.ts +57 -0
- package/dist/discord/components/settings-editor/from-declaration-values.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration-values.js +187 -0
- package/dist/discord/components/settings-editor/from-declaration-values.js.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration.d.ts +39 -0
- package/dist/discord/components/settings-editor/from-declaration.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration.js +132 -0
- package/dist/discord/components/settings-editor/from-declaration.js.map +1 -0
- package/dist/discord/components/settings-editor/index.d.ts +3 -1
- package/dist/discord/components/settings-editor/index.d.ts.map +1 -1
- package/dist/discord/components/settings-editor/index.js +6 -3
- package/dist/discord/components/settings-editor/index.js.map +1 -1
- package/dist/discord/components/settings-editor/mount-settings-editor.d.ts +16 -2
- package/dist/discord/components/settings-editor/mount-settings-editor.d.ts.map +1 -1
- package/dist/discord/components/settings-editor/mount-settings-editor.js +14 -10
- package/dist/discord/components/settings-editor/mount-settings-editor.js.map +1 -1
- package/dist/discord/components/settings-editor/navigation.d.ts +20 -0
- package/dist/discord/components/settings-editor/navigation.d.ts.map +1 -1
- package/dist/discord/components/settings-editor/navigation.js +22 -0
- package/dist/discord/components/settings-editor/navigation.js.map +1 -1
- package/dist/discord/components/settings-editor/settings-editor-card.view.d.ts +4 -3
- package/dist/discord/components/settings-editor/settings-editor-card.view.d.ts.map +1 -1
- package/dist/discord/components/settings-editor/settings-editor-card.view.js +60 -34
- package/dist/discord/components/settings-editor/settings-editor-card.view.js.map +1 -1
- package/dist/discord/components/settings-editor/settings-editor-field-modal.d.ts +2 -1
- package/dist/discord/components/settings-editor/settings-editor-field-modal.d.ts.map +1 -1
- package/dist/discord/components/settings-editor/settings-editor-field-modal.js +48 -72
- package/dist/discord/components/settings-editor/settings-editor-field-modal.js.map +1 -1
- package/dist/discord/components/settings-editor/settings-editor-field-values.d.ts +6 -0
- package/dist/discord/components/settings-editor/settings-editor-field-values.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/settings-editor-field-values.js +13 -0
- package/dist/discord/components/settings-editor/settings-editor-field-values.js.map +1 -0
- package/dist/discord/components/settings-editor/settings-editor-fields.d.ts +6 -5
- package/dist/discord/components/settings-editor/settings-editor-fields.d.ts.map +1 -1
- package/dist/discord/components/settings-editor/settings-editor-fields.js +0 -10
- package/dist/discord/components/settings-editor/settings-editor-fields.js.map +1 -1
- package/dist/discord/components/settings-editor/settings-editor.view.d.ts +3 -2
- package/dist/discord/components/settings-editor/settings-editor.view.d.ts.map +1 -1
- package/dist/discord/components/settings-editor/settings-editor.view.js +4 -21
- package/dist/discord/components/settings-editor/settings-editor.view.js.map +1 -1
- package/dist/discord/events/event-guild-id.d.ts +10 -0
- package/dist/discord/events/event-guild-id.d.ts.map +1 -0
- package/dist/discord/events/event-guild-id.js +37 -0
- package/dist/discord/events/event-guild-id.js.map +1 -0
- package/dist/discord/events/event-router.d.ts +17 -3
- package/dist/discord/events/event-router.d.ts.map +1 -1
- package/dist/discord/events/event-router.js +33 -9
- package/dist/discord/events/event-router.js.map +1 -1
- package/dist/discord/i18n.d.ts +17 -2
- package/dist/discord/i18n.d.ts.map +1 -1
- package/dist/discord/i18n.js +43 -6
- package/dist/discord/i18n.js.map +1 -1
- package/dist/discord/interaction/interaction-locale.d.ts +14 -0
- package/dist/discord/interaction/interaction-locale.d.ts.map +1 -0
- package/dist/discord/interaction/interaction-locale.js +12 -0
- package/dist/discord/interaction/interaction-locale.js.map +1 -0
- package/dist/discord/interaction/locale-resolver.d.ts +27 -0
- package/dist/discord/interaction/locale-resolver.d.ts.map +1 -0
- package/dist/discord/interaction/locale-resolver.js +3 -0
- package/dist/discord/interaction/locale-resolver.js.map +1 -0
- package/dist/discord/interaction/modal.d.ts.map +1 -1
- package/dist/discord/interaction/modal.js +5 -40
- package/dist/discord/interaction/modal.js.map +1 -1
- package/dist/discord/interaction/reply-locale.d.ts +18 -0
- package/dist/discord/interaction/reply-locale.d.ts.map +1 -0
- package/dist/discord/interaction/reply-locale.js +25 -0
- package/dist/discord/interaction/reply-locale.js.map +1 -0
- package/dist/discord/interaction/select-bounds.d.ts +19 -0
- package/dist/discord/interaction/select-bounds.d.ts.map +1 -0
- package/dist/discord/interaction/select-bounds.js +17 -0
- package/dist/discord/interaction/select-bounds.js.map +1 -0
- package/dist/discord/interaction/select-menu.d.ts.map +1 -1
- package/dist/discord/interaction/select-menu.js +2 -9
- package/dist/discord/interaction/select-menu.js.map +1 -1
- package/dist/discord/placeholder-registry.d.ts +1 -1
- package/dist/discord/placeholder-registry.d.ts.map +1 -1
- package/dist/discord/presenter.d.ts +1 -1
- package/dist/discord/presenter.d.ts.map +1 -1
- package/dist/discord/settings/discord-guild-directory.d.ts +28 -0
- package/dist/discord/settings/discord-guild-directory.d.ts.map +1 -0
- package/dist/discord/settings/discord-guild-directory.js +171 -0
- package/dist/discord/settings/discord-guild-directory.js.map +1 -0
- package/dist/discord/settings/guild-locale-resolver.d.ts +25 -0
- package/dist/discord/settings/guild-locale-resolver.d.ts.map +1 -0
- package/dist/discord/settings/guild-locale-resolver.js +33 -0
- package/dist/discord/settings/guild-locale-resolver.js.map +1 -0
- package/dist/discord/settings/is-module-disabled.d.ts +16 -0
- package/dist/discord/settings/is-module-disabled.d.ts.map +1 -0
- package/dist/discord/settings/is-module-disabled.js +20 -0
- package/dist/discord/settings/is-module-disabled.js.map +1 -0
- package/dist/discord/settings/module-disabled-embed.d.ts +13 -0
- package/dist/discord/settings/module-disabled-embed.d.ts.map +1 -0
- package/dist/discord/settings/module-disabled-embed.js +16 -0
- package/dist/discord/settings/module-disabled-embed.js.map +1 -0
- package/dist/discord/settings/require-configured.d.ts +26 -0
- package/dist/discord/settings/require-configured.d.ts.map +1 -0
- package/dist/discord/settings/require-configured.js +62 -0
- package/dist/discord/settings/require-configured.js.map +1 -0
- package/dist/discord/ui/color-aliases.d.ts +31 -0
- package/dist/discord/ui/color-aliases.d.ts.map +1 -0
- package/dist/discord/ui/color-aliases.js +66 -0
- package/dist/discord/ui/color-aliases.js.map +1 -0
- package/dist/discord/ui/color-input.d.ts +5 -32
- package/dist/discord/ui/color-input.d.ts.map +1 -1
- package/dist/discord/ui/color-input.js +8 -157
- package/dist/discord/ui/color-input.js.map +1 -1
- package/dist/discord/ui/colors.d.ts +0 -2
- package/dist/discord/ui/colors.d.ts.map +1 -1
- package/dist/discord/ui/colors.js +4 -5
- package/dist/discord/ui/colors.js.map +1 -1
- package/dist/discord/ui/embed.d.ts.map +1 -1
- package/dist/discord/ui/embed.js +3 -8
- package/dist/discord/ui/embed.js.map +1 -1
- package/dist/discord/ui/truncate-text.d.ts +11 -0
- package/dist/discord/ui/truncate-text.d.ts.map +1 -0
- package/dist/discord/ui/truncate-text.js +19 -0
- package/dist/discord/ui/truncate-text.js.map +1 -0
- package/dist/errors/error-message.d.ts +9 -0
- package/dist/errors/error-message.d.ts.map +1 -0
- package/dist/errors/error-message.js +14 -0
- package/dist/errors/error-message.js.map +1 -0
- package/dist/i18n/business-message.d.ts +1 -1
- package/dist/i18n/business-message.d.ts.map +1 -1
- package/dist/i18n/catalog.d.ts +9 -0
- package/dist/i18n/catalog.d.ts.map +1 -1
- package/dist/i18n/catalog.js +4 -0
- package/dist/i18n/catalog.js.map +1 -1
- package/dist/i18n/locale.d.ts +2 -0
- package/dist/i18n/locale.d.ts.map +1 -1
- package/dist/i18n/locale.js +3 -1
- package/dist/i18n/locale.js.map +1 -1
- package/dist/i18n/translator.d.ts +2 -0
- package/dist/i18n/translator.d.ts.map +1 -1
- package/dist/i18n/translator.js.map +1 -1
- package/dist/module/module.d.ts +15 -1
- package/dist/module/module.d.ts.map +1 -1
- package/dist/resilience/retry.js +2 -2
- package/dist/resilience/retry.js.map +1 -1
- package/dist/scheduler/scheduler.d.ts.map +1 -1
- package/dist/scheduler/scheduler.js +2 -4
- package/dist/scheduler/scheduler.js.map +1 -1
- package/dist/settings/cache.d.ts +29 -0
- package/dist/settings/cache.d.ts.map +1 -0
- package/dist/settings/cache.js +47 -0
- package/dist/settings/cache.js.map +1 -0
- package/dist/settings/choice.d.ts +10 -0
- package/dist/settings/choice.d.ts.map +1 -0
- package/dist/settings/choice.js +3 -0
- package/dist/settings/choice.js.map +1 -0
- package/dist/settings/decode-stored.d.ts +20 -0
- package/dist/settings/decode-stored.d.ts.map +1 -0
- package/dist/settings/decode-stored.js +35 -0
- package/dist/settings/decode-stored.js.map +1 -0
- package/dist/settings/define-settings.d.ts +52 -0
- package/dist/settings/define-settings.d.ts.map +1 -0
- package/dist/settings/define-settings.js +141 -0
- package/dist/settings/define-settings.js.map +1 -0
- package/dist/settings/describe-field.d.ts +19 -0
- package/dist/settings/describe-field.d.ts.map +1 -0
- package/dist/settings/describe-field.js +131 -0
- package/dist/settings/describe-field.js.map +1 -0
- package/dist/settings/describe.d.ts +27 -0
- package/dist/settings/describe.d.ts.map +1 -0
- package/dist/settings/describe.js +62 -0
- package/dist/settings/describe.js.map +1 -0
- package/dist/settings/duration.d.ts +16 -0
- package/dist/settings/duration.d.ts.map +1 -0
- package/dist/settings/duration.js +56 -0
- package/dist/settings/duration.js.map +1 -0
- package/dist/settings/field-order.d.ts +37 -0
- package/dist/settings/field-order.d.ts.map +1 -0
- package/dist/settings/field-order.js +100 -0
- package/dist/settings/field-order.js.map +1 -0
- package/dist/settings/field-search.d.ts +10 -0
- package/dist/settings/field-search.d.ts.map +1 -0
- package/dist/settings/field-search.js +18 -0
- package/dist/settings/field-search.js.map +1 -0
- package/dist/settings/fields/builders.d.ts +41 -0
- package/dist/settings/fields/builders.d.ts.map +1 -0
- package/dist/settings/fields/builders.js +83 -0
- package/dist/settings/fields/builders.js.map +1 -0
- package/dist/settings/fields/field.d.ts +199 -0
- package/dist/settings/fields/field.d.ts.map +1 -0
- package/dist/settings/fields/field.js +33 -0
- package/dist/settings/fields/field.js.map +1 -0
- package/dist/settings/fields/zod-schema.d.ts +67 -0
- package/dist/settings/fields/zod-schema.d.ts.map +1 -0
- package/dist/settings/fields/zod-schema.js +281 -0
- package/dist/settings/fields/zod-schema.js.map +1 -0
- package/dist/settings/in-memory/in-memory-guild-directory.d.ts +24 -0
- package/dist/settings/in-memory/in-memory-guild-directory.d.ts.map +1 -0
- package/dist/settings/in-memory/in-memory-guild-directory.js +42 -0
- package/dist/settings/in-memory/in-memory-guild-directory.js.map +1 -0
- package/dist/settings/in-memory/in-memory-settings-store.d.ts +8 -0
- package/dist/settings/in-memory/in-memory-settings-store.d.ts.map +1 -0
- package/dist/settings/in-memory/in-memory-settings-store.js +43 -0
- package/dist/settings/in-memory/in-memory-settings-store.js.map +1 -0
- package/dist/settings/in-memory/in-process-notifier.d.ts +9 -0
- package/dist/settings/in-memory/in-process-notifier.d.ts.map +1 -0
- package/dist/settings/in-memory/in-process-notifier.js +32 -0
- package/dist/settings/in-memory/in-process-notifier.js.map +1 -0
- package/dist/settings/index.d.ts +22 -0
- package/dist/settings/index.d.ts.map +1 -0
- package/dist/settings/index.js +31 -0
- package/dist/settings/index.js.map +1 -0
- package/dist/settings/is-plain-object.d.ts +6 -0
- package/dist/settings/is-plain-object.d.ts.map +1 -0
- package/dist/settings/is-plain-object.js +15 -0
- package/dist/settings/is-plain-object.js.map +1 -0
- package/dist/settings/messages.d.ts +61 -0
- package/dist/settings/messages.d.ts.map +1 -0
- package/dist/settings/messages.js +155 -0
- package/dist/settings/messages.js.map +1 -0
- package/dist/settings/migrate.d.ts +35 -0
- package/dist/settings/migrate.d.ts.map +1 -0
- package/dist/settings/migrate.js +42 -0
- package/dist/settings/migrate.js.map +1 -0
- package/dist/settings/ports/guild-directory.d.ts +61 -0
- package/dist/settings/ports/guild-directory.d.ts.map +1 -0
- package/dist/settings/ports/guild-directory.js +3 -0
- package/dist/settings/ports/guild-directory.js.map +1 -0
- package/dist/settings/ports/settings-changed-notifier.d.ts +20 -0
- package/dist/settings/ports/settings-changed-notifier.d.ts.map +1 -0
- package/dist/settings/ports/settings-changed-notifier.js +3 -0
- package/dist/settings/ports/settings-changed-notifier.js.map +1 -0
- package/dist/settings/ports/settings-store.d.ts +76 -0
- package/dist/settings/ports/settings-store.d.ts.map +1 -0
- package/dist/settings/ports/settings-store.js +3 -0
- package/dist/settings/ports/settings-store.js.map +1 -0
- package/dist/settings/registry.d.ts +31 -0
- package/dist/settings/registry.d.ts.map +1 -0
- package/dist/settings/registry.js +102 -0
- package/dist/settings/registry.js.map +1 -0
- package/dist/settings/settings-declaration-error.d.ts +13 -0
- package/dist/settings/settings-declaration-error.d.ts.map +1 -0
- package/dist/settings/settings-declaration-error.js +22 -0
- package/dist/settings/settings-declaration-error.js.map +1 -0
- package/dist/settings/settings-service.d.ts +148 -0
- package/dist/settings/settings-service.d.ts.map +1 -0
- package/dist/settings/settings-service.js +279 -0
- package/dist/settings/settings-service.js.map +1 -0
- package/dist/settings/settings-validation-error.d.ts +23 -0
- package/dist/settings/settings-validation-error.d.ts.map +1 -0
- package/dist/settings/settings-validation-error.js +17 -0
- package/dist/settings/settings-validation-error.js.map +1 -0
- package/dist/settings/status.d.ts +16 -0
- package/dist/settings/status.d.ts.map +1 -0
- package/dist/settings/status.js +18 -0
- package/dist/settings/status.js.map +1 -0
- package/dist/settings/suggest.d.ts +77 -0
- package/dist/settings/suggest.d.ts.map +1 -0
- package/dist/settings/suggest.js +192 -0
- package/dist/settings/suggest.js.map +1 -0
- package/dist/settings/system/kernel-settings.d.ts +43 -0
- package/dist/settings/system/kernel-settings.d.ts.map +1 -0
- package/dist/settings/system/kernel-settings.js +53 -0
- package/dist/settings/system/kernel-settings.js.map +1 -0
- package/dist/settings/system/module-gate.d.ts +31 -0
- package/dist/settings/system/module-gate.d.ts.map +1 -0
- package/dist/settings/system/module-gate.js +34 -0
- package/dist/settings/system/module-gate.js.map +1 -0
- package/dist/settings/testing/index.d.ts +2 -0
- package/dist/settings/testing/index.d.ts.map +1 -0
- package/dist/settings/testing/index.js +6 -0
- package/dist/settings/testing/index.js.map +1 -0
- package/dist/settings/testing/settings-store-contract.d.ts +29 -0
- package/dist/settings/testing/settings-store-contract.d.ts.map +1 -0
- package/dist/settings/testing/settings-store-contract.js +192 -0
- package/dist/settings/testing/settings-store-contract.js.map +1 -0
- package/dist/settings/timed-search.d.ts +26 -0
- package/dist/settings/timed-search.d.ts.map +1 -0
- package/dist/settings/timed-search.js +48 -0
- package/dist/settings/timed-search.js.map +1 -0
- package/dist/settings/types.d.ts +37 -0
- package/dist/settings/types.d.ts.map +1 -0
- package/dist/settings/types.js +3 -0
- package/dist/settings/types.js.map +1 -0
- package/dist/settings/validate.d.ts +55 -0
- package/dist/settings/validate.d.ts.map +1 -0
- package/dist/settings/validate.js +196 -0
- package/dist/settings/validate.js.map +1 -0
- package/package.json +463 -88
- package/src/authz/require-level-guard.ts +1 -1
- package/src/color.ts +134 -0
- package/src/discord/command/all-required.ts +12 -0
- package/src/discord/command/autocomplete-dispatcher.ts +2 -1
- package/src/discord/command/autocomplete-limits.ts +2 -0
- package/src/discord/command/autocomplete.ts +8 -7
- package/src/discord/command/command-responders.ts +39 -0
- package/src/discord/command/context.ts +14 -54
- package/src/discord/command/cooldown-guard.ts +1 -1
- package/src/discord/command/create-command.ts +19 -94
- package/src/discord/command/create-context-menu-command.ts +19 -97
- package/src/discord/command/errors.ts +1 -1
- package/src/discord/command/guard.ts +24 -12
- package/src/discord/command/options.ts +202 -412
- package/src/discord/command/passes-guard.ts +37 -0
- package/src/discord/command/permission-guard.ts +1 -1
- package/src/discord/command/render-command-failure.ts +71 -0
- package/src/discord/command/route-key.ts +9 -0
- package/src/discord/command/router.ts +73 -12
- package/src/discord/command/send-embed.ts +42 -0
- package/src/discord/command/types.ts +7 -1
- package/src/discord/components/component-access.ts +20 -0
- package/src/discord/components/component-router.ts +73 -36
- package/src/discord/components/interactive-message/index.ts +5 -3
- package/src/discord/components/interactive-message/interactive-message-collector.ts +4 -68
- package/src/discord/components/interactive-message/message-edit-options.ts +47 -0
- package/src/discord/components/interactive-message/mount-interactive-message.ts +5 -3
- package/src/discord/components/interactive-message/state-store.ts +20 -0
- package/src/discord/components/paginator/mount-paginator.ts +3 -3
- package/src/discord/components/paginator/paginator-view.ts +1 -1
- package/src/discord/components/persistent-paginator.ts +8 -9
- package/src/discord/components/settings-editor/from-declaration-controls.ts +371 -0
- package/src/discord/components/settings-editor/from-declaration-display.ts +155 -0
- package/src/discord/components/settings-editor/from-declaration-layout.ts +247 -0
- package/src/discord/components/settings-editor/from-declaration-search.ts +77 -0
- package/src/discord/components/settings-editor/from-declaration-stored.ts +39 -0
- package/src/discord/components/settings-editor/from-declaration-subject.ts +110 -0
- package/src/discord/components/settings-editor/from-declaration-values.ts +240 -0
- package/src/discord/components/settings-editor/from-declaration.ts +218 -0
- package/src/discord/components/settings-editor/index.ts +5 -2
- package/src/discord/components/settings-editor/mount-settings-editor.ts +28 -10
- package/src/discord/components/settings-editor/navigation.ts +41 -0
- package/src/discord/components/settings-editor/settings-editor-card.view.ts +79 -36
- package/src/discord/components/settings-editor/settings-editor-field-modal.ts +76 -68
- package/src/discord/components/settings-editor/settings-editor-field-values.ts +11 -0
- package/src/discord/components/settings-editor/settings-editor-fields.ts +6 -11
- package/src/discord/components/settings-editor/settings-editor.view.ts +11 -24
- package/src/discord/events/event-guild-id.ts +35 -0
- package/src/discord/events/event-router.ts +46 -13
- package/src/discord/i18n.ts +47 -8
- package/src/discord/interaction/interaction-locale.ts +19 -0
- package/src/discord/interaction/locale-resolver.ts +28 -0
- package/src/discord/interaction/modal.ts +5 -40
- package/src/discord/interaction/reply-locale.ts +35 -0
- package/src/discord/interaction/select-bounds.ts +33 -0
- package/src/discord/interaction/select-menu.ts +2 -9
- package/src/discord/placeholder-registry.ts +1 -1
- package/src/discord/presenter.ts +1 -1
- package/src/discord/settings/discord-guild-directory.ts +214 -0
- package/src/discord/settings/guild-locale-resolver.ts +38 -0
- package/src/discord/settings/is-module-disabled.ts +22 -0
- package/src/discord/settings/module-disabled-embed.ts +19 -0
- package/src/discord/settings/require-configured.ts +68 -0
- package/src/discord/ui/color-aliases.ts +70 -0
- package/src/discord/ui/color-input.ts +8 -173
- package/src/discord/ui/colors.ts +2 -3
- package/src/discord/ui/embed.ts +3 -9
- package/src/discord/ui/truncate-text.ts +15 -0
- package/src/errors/error-message.ts +10 -0
- package/src/i18n/business-message.ts +1 -1
- package/src/i18n/catalog.ts +14 -0
- package/src/i18n/locale.ts +3 -0
- package/src/i18n/translator.ts +3 -0
- package/src/module/module.ts +15 -1
- package/src/resilience/retry.ts +1 -1
- package/src/scheduler/scheduler.ts +2 -5
- package/src/settings/cache.ts +70 -0
- package/src/settings/choice.ts +9 -0
- package/src/settings/decode-stored.ts +40 -0
- package/src/settings/define-settings.ts +214 -0
- package/src/settings/describe-field.ts +155 -0
- package/src/settings/describe.ts +90 -0
- package/src/settings/duration.ts +54 -0
- package/src/settings/field-order.ts +128 -0
- package/src/settings/field-search.ts +16 -0
- package/src/settings/fields/builders.ts +161 -0
- package/src/settings/fields/field.ts +205 -0
- package/src/settings/fields/zod-schema.ts +300 -0
- package/src/settings/in-memory/in-memory-guild-directory.ts +69 -0
- package/src/settings/in-memory/in-memory-settings-store.ts +46 -0
- package/src/settings/in-memory/in-process-notifier.ts +40 -0
- package/src/settings/index.ts +72 -0
- package/src/settings/is-plain-object.ts +11 -0
- package/src/settings/messages.ts +161 -0
- package/src/settings/migrate.ts +58 -0
- package/src/settings/ports/guild-directory.ts +48 -0
- package/src/settings/ports/settings-changed-notifier.ts +20 -0
- package/src/settings/ports/settings-store.ts +74 -0
- package/src/settings/registry.ts +137 -0
- package/src/settings/settings-declaration-error.ts +16 -0
- package/src/settings/settings-service.ts +535 -0
- package/src/settings/settings-validation-error.ts +41 -0
- package/src/settings/status.ts +25 -0
- package/src/settings/suggest.ts +267 -0
- package/src/settings/system/kernel-settings.ts +74 -0
- package/src/settings/system/module-gate.ts +49 -0
- package/src/settings/testing/index.ts +7 -0
- package/src/settings/testing/settings-store-contract.ts +278 -0
- package/src/settings/timed-search.ts +56 -0
- package/src/settings/types.ts +42 -0
- package/src/settings/validate.ts +298 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { LocaleResolver } from "@/discord/interaction/locale-resolver";
|
|
2
|
+
import { normalizeLocale, resolveLocale } from "@/i18n/locale";
|
|
3
|
+
import type { Translator } from "@/i18n/translator";
|
|
4
|
+
import type { SettingsRegistry } from "@/settings/registry";
|
|
5
|
+
import type { SettingsService } from "@/settings/settings-service";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The default {@link LocaleResolver} of a bot with settings (FR-038): the
|
|
9
|
+
* member's locale if supported, then the guild's language setting
|
|
10
|
+
* (`registry.kernel`, read through the service's cache), then the guild's
|
|
11
|
+
* Discord locale if supported, then the translator's default.
|
|
12
|
+
*
|
|
13
|
+
* The setting is read only when the member's locale is not supported, and
|
|
14
|
+
* never outside a guild. A failed read rejects: the kernel's
|
|
15
|
+
* reply paths log it and fall back to the interaction's own locales.
|
|
16
|
+
*
|
|
17
|
+
* @param deps.service - reads the guild's kernel settings.
|
|
18
|
+
* @param deps.registry - holds the kernel declaration.
|
|
19
|
+
* @param deps.translator - gives the bot's default language.
|
|
20
|
+
* @returns the resolver to hand to the command and component routers.
|
|
21
|
+
*/
|
|
22
|
+
export function createGuildLocaleResolver(deps: {
|
|
23
|
+
service: SettingsService;
|
|
24
|
+
registry: SettingsRegistry;
|
|
25
|
+
translator: Translator;
|
|
26
|
+
}): LocaleResolver {
|
|
27
|
+
const { service, registry, translator } = deps;
|
|
28
|
+
return {
|
|
29
|
+
async resolve({ locale, guildLocale, guildId }) {
|
|
30
|
+
const member = normalizeLocale(locale);
|
|
31
|
+
if (member !== undefined || !guildId) {
|
|
32
|
+
return resolveLocale([member, guildLocale], translator.defaultLocale);
|
|
33
|
+
}
|
|
34
|
+
const setting = (await service.get(registry.kernel, guildId)).locale;
|
|
35
|
+
return resolveLocale([setting, guildLocale], translator.defaultLocale);
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ModuleGate } from "@/settings/system/module-gate";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Whether a router must skip a handler because its module is disabled on the
|
|
5
|
+
* guild it runs for. Nothing is gated without a gate, for a handler registered
|
|
6
|
+
* without a module, or outside a guild (FR-036).
|
|
7
|
+
*
|
|
8
|
+
* @param params.gate - the bot's module gate, if the router was given one.
|
|
9
|
+
* @param params.moduleName - the module that registered the handler.
|
|
10
|
+
* @param params.guildId - the guild the handler would run for, `null` outside one.
|
|
11
|
+
*/
|
|
12
|
+
export async function isModuleDisabled(params: {
|
|
13
|
+
gate: ModuleGate | undefined;
|
|
14
|
+
moduleName: string | undefined;
|
|
15
|
+
guildId: string | null | undefined;
|
|
16
|
+
}): Promise<boolean> {
|
|
17
|
+
const { gate, moduleName, guildId } = params;
|
|
18
|
+
if (gate === undefined || moduleName === undefined || !guildId) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
return !(await gate.isEnabled(moduleName, guildId));
|
|
22
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { EmbedBuilder } from "discord.js";
|
|
2
|
+
import type { LocaleSubject } from "@/discord/interaction/locale-resolver";
|
|
3
|
+
import { type ReplyLocaleDeps, replyLocale } from "@/discord/interaction/reply-locale";
|
|
4
|
+
import type { Presenter } from "@/discord/presenter";
|
|
5
|
+
import { SETTINGS_MESSAGES } from "@/settings/messages";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The answer to an interaction with a module disabled on its guild: the
|
|
9
|
+
* translated "disabled on this server" message, as a denial in the reply
|
|
10
|
+
* language the router's `LocaleResolver` gives (or the interaction's own).
|
|
11
|
+
*/
|
|
12
|
+
export async function moduleDisabledEmbed(
|
|
13
|
+
interaction: LocaleSubject,
|
|
14
|
+
deps: ReplyLocaleDeps & { readonly presenter: Presenter },
|
|
15
|
+
): Promise<EmbedBuilder> {
|
|
16
|
+
const locale = await replyLocale(interaction, deps);
|
|
17
|
+
const message = deps.translator.translate(locale, SETTINGS_MESSAGES.moduleDisabled);
|
|
18
|
+
return deps.presenter.denial(message, locale);
|
|
19
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type BaseInteraction, PermissionFlagsBits } from "discord.js";
|
|
2
|
+
import type { Guard, GuardResult } from "@/discord/command/guard";
|
|
3
|
+
import { CORE_MESSAGES } from "@/discord/i18n";
|
|
4
|
+
import { replyLocale } from "@/discord/interaction/reply-locale";
|
|
5
|
+
import type { SettingsDeclaration } from "@/settings/define-settings";
|
|
6
|
+
import { SETTINGS_MESSAGES } from "@/settings/messages";
|
|
7
|
+
import type { SettingsService } from "@/settings/settings-service";
|
|
8
|
+
|
|
9
|
+
const ALLOW: GuardResult = { ok: true };
|
|
10
|
+
|
|
11
|
+
const NOT_CONFIGURED: GuardResult = {
|
|
12
|
+
ok: false,
|
|
13
|
+
message: { key: SETTINGS_MESSAGES.moduleNotConfigured },
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Block a command or a component until its module is configured on the guild
|
|
18
|
+
* (FR-041): until `service.status` lists no missing field, the member gets the
|
|
19
|
+
* translated "not configured" message instead of the handler.
|
|
20
|
+
*
|
|
21
|
+
* - Members allowed to configure the bot (the `ManageGuild` permission, until
|
|
22
|
+
* the permissions feature exists) also read which settings are missing, by
|
|
23
|
+
* their translated labels, in the reply language (the pipeline's
|
|
24
|
+
* `LocaleResolver`). Other members never see them.
|
|
25
|
+
* - The status is read through the service's cache, which every write
|
|
26
|
+
* invalidates: the guard passes as soon as the value is set, no restart.
|
|
27
|
+
* - Outside a guild there is nothing to configure for: denied like
|
|
28
|
+
* `guildOnlyGuard`, without reading any setting.
|
|
29
|
+
*
|
|
30
|
+
* Typed over `BaseInteraction`, so the same guard is a command's `guard` and a
|
|
31
|
+
* component handler's `guard`.
|
|
32
|
+
*
|
|
33
|
+
* @param declaration - the module's settings declaration.
|
|
34
|
+
* @param service - reads the guild's configuration status.
|
|
35
|
+
*/
|
|
36
|
+
export function requireConfigured(
|
|
37
|
+
declaration: SettingsDeclaration,
|
|
38
|
+
service: SettingsService,
|
|
39
|
+
): Guard<BaseInteraction> {
|
|
40
|
+
return {
|
|
41
|
+
async check(interaction, runtime) {
|
|
42
|
+
if (!interaction.guildId) {
|
|
43
|
+
return { ok: false, message: { key: CORE_MESSAGES.guardGuildOnly } };
|
|
44
|
+
}
|
|
45
|
+
const { missing } = await service.status(declaration, interaction.guildId);
|
|
46
|
+
if (missing.length === 0) {
|
|
47
|
+
return ALLOW;
|
|
48
|
+
}
|
|
49
|
+
const canConfigure =
|
|
50
|
+
interaction.memberPermissions?.has(PermissionFlagsBits.ManageGuild) ?? false;
|
|
51
|
+
if (!canConfigure || runtime === undefined) {
|
|
52
|
+
return NOT_CONFIGURED;
|
|
53
|
+
}
|
|
54
|
+
const { translator } = runtime;
|
|
55
|
+
const locale = await replyLocale(interaction, runtime);
|
|
56
|
+
const fields = missing
|
|
57
|
+
.map((key) => translator.translate(locale, declaration.fields[key]?.label ?? key))
|
|
58
|
+
.join(", ");
|
|
59
|
+
return {
|
|
60
|
+
ok: false,
|
|
61
|
+
message: [
|
|
62
|
+
translator.translate(locale, SETTINGS_MESSAGES.moduleNotConfigured),
|
|
63
|
+
translator.translate(locale, SETTINGS_MESSAGES.moduleNotConfiguredMissing, { fields }),
|
|
64
|
+
].join(" "),
|
|
65
|
+
};
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { BASE_COLOR_NAMES, indexColorNames, normalizeColorName } from "@/color";
|
|
2
|
+
import { ColorAliasTooLongError } from "@/discord/ui/color-input-errors";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The names the kernel ships: the vendor-neutral base colours, `blurple` included.
|
|
6
|
+
*
|
|
7
|
+
* A bot adds its own — the colour it calls by its brand's name — through
|
|
8
|
+
* {@link registerColorAliases}, which is also why this table is not the one
|
|
9
|
+
* lookups read: {@link COLOR_ALIASES} is.
|
|
10
|
+
*/
|
|
11
|
+
const BUILT_IN_COLOR_ALIASES: Readonly<Record<string, string>> = {
|
|
12
|
+
...BASE_COLOR_NAMES,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Every name a lookup answers to: the kernel's own, plus whatever the bot
|
|
17
|
+
* registered. Exposed for inspection — a screen listing the colours it accepts
|
|
18
|
+
* reads this — never for mutation, which goes through
|
|
19
|
+
* {@link registerColorAliases} so the length check cannot be skipped.
|
|
20
|
+
*/
|
|
21
|
+
export const COLOR_ALIASES: Readonly<Record<string, string>> = {
|
|
22
|
+
...BUILT_IN_COLOR_ALIASES,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const COLORS_BY_NORMALIZED_NAME = indexColorNames(COLOR_ALIASES);
|
|
26
|
+
|
|
27
|
+
/** The `#rrggbb` a recognised colour name stands for, `null` for anything else. */
|
|
28
|
+
export function resolveNamedColor(raw: string): string | null {
|
|
29
|
+
return COLORS_BY_NORMALIZED_NAME.get(normalizeColorName(raw)) ?? null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** `#RRGGBB` — the longest hexadecimal form the parser accepts. */
|
|
33
|
+
const HEX_INPUT_LENGTH = 7;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* What a colour input field may hold.
|
|
37
|
+
*
|
|
38
|
+
* Derived from the built-in table rather than written down, so adding a longer
|
|
39
|
+
* name here cannot leave the field silently truncating it at the client — and
|
|
40
|
+
* fixed rather than recomputed on registration, because field definitions read
|
|
41
|
+
* it once at module load, long before a bot registers anything.
|
|
42
|
+
*/
|
|
43
|
+
export const MAX_COLOR_INPUT_LENGTH = Math.max(
|
|
44
|
+
HEX_INPUT_LENGTH,
|
|
45
|
+
...Object.keys(BUILT_IN_COLOR_ALIASES).map((name) => name.length),
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Teach the parser the colours this bot calls by name — its brand gold, its
|
|
50
|
+
* team colours — from the composition root, before any module is constructed.
|
|
51
|
+
*
|
|
52
|
+
* A name over {@link MAX_COLOR_INPUT_LENGTH} is refused rather than accepted:
|
|
53
|
+
* the input field is already sized, so registering a longer name would let an
|
|
54
|
+
* administrator type a colour the client cuts short, which then reads as the
|
|
55
|
+
* parser rejecting a name it actually knows.
|
|
56
|
+
*
|
|
57
|
+
* @throws ColorAliasTooLongError when a name cannot fit the input field.
|
|
58
|
+
*/
|
|
59
|
+
export function registerColorAliases(aliases: Readonly<Record<string, string>>): void {
|
|
60
|
+
for (const [name, hex] of Object.entries(aliases)) {
|
|
61
|
+
if (name.length > MAX_COLOR_INPUT_LENGTH) {
|
|
62
|
+
throw new ColorAliasTooLongError(name, MAX_COLOR_INPUT_LENGTH);
|
|
63
|
+
}
|
|
64
|
+
// Both views are written: the table is what a screen lists, the map is what
|
|
65
|
+
// a lookup reads, and a name present in one but not the other would be a
|
|
66
|
+
// colour the bot advertises and refuses.
|
|
67
|
+
(COLOR_ALIASES as Record<string, string>)[name] = hex;
|
|
68
|
+
COLORS_BY_NORMALIZED_NAME.set(normalizeColorName(name), hex);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -1,179 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* The generic web colours, at the value the name carries everywhere else — an
|
|
6
|
-
* administrator typing `orange` gets the orange they would get in CSS, not a
|
|
7
|
-
* shade this module invented.
|
|
8
|
-
*/
|
|
9
|
-
const RED = "#ff0000";
|
|
10
|
-
const BLUE = "#0000ff";
|
|
11
|
-
const GREEN = "#008000";
|
|
12
|
-
const YELLOW = "#ffff00";
|
|
13
|
-
const ORANGE = "#ffa500";
|
|
14
|
-
const PURPLE = "#800080";
|
|
15
|
-
const PINK = "#ffc0cb";
|
|
16
|
-
const WHITE = "#ffffff";
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Not `#000000`: Discord reads a zero colour as "no colour set" and renders the
|
|
20
|
-
* default grey bar instead, so pure black is the one value an administrator
|
|
21
|
-
* could ask for and never see. One step off zero reads as black.
|
|
22
|
-
*/
|
|
23
|
-
const BLACK = "#010101";
|
|
24
|
-
|
|
25
|
-
const GREY = "#808080";
|
|
26
|
-
const CYAN = "#00ffff";
|
|
27
|
-
const BROWN = "#a52a2a";
|
|
28
|
-
const SILVER = "#c0c0c0";
|
|
29
|
-
const TURQUOISE = "#40e0d0";
|
|
30
|
-
const MAGENTA = "#ff00ff";
|
|
31
|
-
const NAVY = "#000080";
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Colour names an administrator may type instead of a hex code, French and
|
|
35
|
-
* English side by side.
|
|
36
|
-
*
|
|
37
|
-
* This table is **input vocabulary, not display text**. Nothing in it is ever
|
|
38
|
-
* shown to anyone, and its keys must never be translated at runtime: a catalog
|
|
39
|
-
* lookup resolves against the reply locale, which would make `rouge` stop
|
|
40
|
-
* working the moment an admin's Discord is set to English — the opposite of the
|
|
41
|
-
* point. Both spellings are accepted from everyone, always, which only a flat
|
|
42
|
-
* table can promise.
|
|
43
|
-
*
|
|
44
|
-
* These are the names the kernel ships. A bot adds its own — the colour it
|
|
45
|
-
* calls by its brand's name — through {@link registerColorAliases}, which is
|
|
46
|
-
* also why this table is not the one lookups read: {@link COLOR_ALIASES} is.
|
|
47
|
-
*/
|
|
48
|
-
const BUILT_IN_COLOR_ALIASES: Readonly<Record<string, string>> = {
|
|
49
|
-
red: RED,
|
|
50
|
-
rouge: RED,
|
|
51
|
-
blue: BLUE,
|
|
52
|
-
bleu: BLUE,
|
|
53
|
-
green: GREEN,
|
|
54
|
-
vert: GREEN,
|
|
55
|
-
yellow: YELLOW,
|
|
56
|
-
jaune: YELLOW,
|
|
57
|
-
orange: ORANGE,
|
|
58
|
-
purple: PURPLE,
|
|
59
|
-
// French for purple, not the CSS `violet` (a pale pink): an admin typing it
|
|
60
|
-
// here is asking for the colour the word means in their own language.
|
|
61
|
-
violet: PURPLE,
|
|
62
|
-
pink: PINK,
|
|
63
|
-
rose: PINK,
|
|
64
|
-
white: WHITE,
|
|
65
|
-
blanc: WHITE,
|
|
66
|
-
black: BLACK,
|
|
67
|
-
noir: BLACK,
|
|
68
|
-
grey: GREY,
|
|
69
|
-
gray: GREY,
|
|
70
|
-
gris: GREY,
|
|
71
|
-
cyan: CYAN,
|
|
72
|
-
brown: BROWN,
|
|
73
|
-
marron: BROWN,
|
|
74
|
-
silver: SILVER,
|
|
75
|
-
argent: SILVER,
|
|
76
|
-
argenté: SILVER,
|
|
77
|
-
turquoise: TURQUOISE,
|
|
78
|
-
magenta: MAGENTA,
|
|
79
|
-
navy: NAVY,
|
|
80
|
-
marine: NAVY,
|
|
81
|
-
blurple: DISCORD_BLURPLE,
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Accents must not be a trap: `dore` and `doré` are the same request, and an
|
|
86
|
-
* administrator has no way of knowing which one this file happened to spell.
|
|
87
|
-
* Case and surrounding whitespace are folded for the same reason.
|
|
88
|
-
*/
|
|
89
|
-
function normalizeColorName(raw: string): string {
|
|
90
|
-
return raw
|
|
91
|
-
.trim()
|
|
92
|
-
.toLowerCase()
|
|
93
|
-
.normalize("NFD")
|
|
94
|
-
.replace(/\p{Diacritic}/gu, "");
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Every name a lookup answers to: the kernel's own, plus whatever the bot
|
|
99
|
-
* registered. Exposed for inspection — a screen listing the colours it accepts
|
|
100
|
-
* reads this — never for mutation, which goes through
|
|
101
|
-
* {@link registerColorAliases} so the length check cannot be skipped.
|
|
102
|
-
*/
|
|
103
|
-
export const COLOR_ALIASES: Readonly<Record<string, string>> = {
|
|
104
|
-
...BUILT_IN_COLOR_ALIASES,
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
const COLORS_BY_NORMALIZED_NAME = new Map(
|
|
108
|
-
Object.entries(COLOR_ALIASES).map(([name, hex]) => [normalizeColorName(name), hex]),
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
/** The `#rrggbb` a recognised colour name stands for, `null` for anything else. */
|
|
112
|
-
function resolveNamedColor(raw: string): string | null {
|
|
113
|
-
return COLORS_BY_NORMALIZED_NAME.get(normalizeColorName(raw)) ?? null;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/** `#RRGGBB` — the longest hexadecimal form the parser accepts. */
|
|
117
|
-
const HEX_INPUT_LENGTH = 7;
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* What a colour input field may hold.
|
|
121
|
-
*
|
|
122
|
-
* Derived from the built-in table rather than written down, so adding a longer
|
|
123
|
-
* name here cannot leave the field silently truncating it at the client — and
|
|
124
|
-
* fixed rather than recomputed on registration, because field definitions read
|
|
125
|
-
* it once at module load, long before a bot registers anything.
|
|
126
|
-
*/
|
|
127
|
-
export const MAX_COLOR_INPUT_LENGTH = Math.max(
|
|
128
|
-
HEX_INPUT_LENGTH,
|
|
129
|
-
...Object.keys(BUILT_IN_COLOR_ALIASES).map((name) => name.length),
|
|
130
|
-
);
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Teach the parser the colours this bot calls by name — its brand gold, its
|
|
134
|
-
* team colours — from the composition root, before any module is constructed.
|
|
135
|
-
*
|
|
136
|
-
* A name over {@link MAX_COLOR_INPUT_LENGTH} is refused rather than accepted:
|
|
137
|
-
* the input field is already sized, so registering a longer name would let an
|
|
138
|
-
* administrator type a colour the client cuts short, which then reads as the
|
|
139
|
-
* parser rejecting a name it actually knows.
|
|
140
|
-
*
|
|
141
|
-
* @throws ColorAliasTooLongError when a name cannot fit the input field.
|
|
142
|
-
*/
|
|
143
|
-
export function registerColorAliases(aliases: Readonly<Record<string, string>>): void {
|
|
144
|
-
for (const [name, hex] of Object.entries(aliases)) {
|
|
145
|
-
if (name.length > MAX_COLOR_INPUT_LENGTH) {
|
|
146
|
-
throw new ColorAliasTooLongError(name, MAX_COLOR_INPUT_LENGTH);
|
|
147
|
-
}
|
|
148
|
-
// Both views are written: the table is what a screen lists, the map is what
|
|
149
|
-
// a lookup reads, and a name present in one but not the other would be a
|
|
150
|
-
// colour the bot advertises and refuses.
|
|
151
|
-
(COLOR_ALIASES as Record<string, string>)[name] = hex;
|
|
152
|
-
COLORS_BY_NORMALIZED_NAME.set(normalizeColorName(name), hex);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/** Discord accepts `#RGB` and `#RRGGBB`, with or without the leading hash. */
|
|
157
|
-
const HEX_COLOR_PATTERN = /^#?(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;
|
|
1
|
+
import { parseHexColor } from "@/color";
|
|
2
|
+
import { resolveNamedColor } from "@/discord/ui/color-aliases";
|
|
158
3
|
|
|
159
4
|
/**
|
|
160
5
|
* Normalize a colour input to `#RRGGBB`, from a hexadecimal code or from one of
|
|
161
|
-
* the names in {@link COLOR_ALIASES} — an
|
|
162
|
-
* not have to look a hex code up. `null`
|
|
163
|
-
* refusal an administrator reads belongs to
|
|
164
|
-
* reports that it could not read the value and
|
|
6
|
+
* the names in {@link import("@/discord/ui/color-aliases").COLOR_ALIASES} — an
|
|
7
|
+
* administrator picking a colour should not have to look a hex code up. `null`
|
|
8
|
+
* for anything that is neither: the refusal an administrator reads belongs to
|
|
9
|
+
* whichever screen asked, so core reports that it could not read the value and
|
|
10
|
+
* leaves the wording to the caller.
|
|
165
11
|
*/
|
|
166
12
|
export function parseColorInput(raw: string): string | null {
|
|
167
|
-
|
|
168
|
-
const named = resolveNamedColor(value);
|
|
169
|
-
if (named !== null) {
|
|
170
|
-
return named;
|
|
171
|
-
}
|
|
172
|
-
if (!HEX_COLOR_PATTERN.test(value)) {
|
|
173
|
-
return null;
|
|
174
|
-
}
|
|
175
|
-
const digits = value.replace("#", "").toLowerCase();
|
|
176
|
-
const expanded =
|
|
177
|
-
digits.length === 3 ? [...digits].map((digit) => `${digit}${digit}`).join("") : digits;
|
|
178
|
-
return `#${expanded}`;
|
|
13
|
+
return resolveNamedColor(raw) ?? parseHexColor(raw);
|
|
179
14
|
}
|
package/src/discord/ui/colors.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { DISCORD_BLURPLE } from "@/color";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* The five roles an embed colour can play. A bot restyles by supplying its own
|
|
3
5
|
* values for these, never by adding a sixth: a role the kernel does not know is
|
|
@@ -18,9 +20,6 @@ export function toDiscordColor(hex: string): number {
|
|
|
18
20
|
return Number.parseInt(hex.slice(1), 16);
|
|
19
21
|
}
|
|
20
22
|
|
|
21
|
-
/** Discord's own brand colour, the fallback for a bot that declares no palette. */
|
|
22
|
-
export const DISCORD_BLURPLE = "#5865f2";
|
|
23
|
-
|
|
24
23
|
/**
|
|
25
24
|
* What every embed renders as until the composition root says otherwise. Kept
|
|
26
25
|
* deliberately unbranded: a kernel that shipped one bot's colours would make
|
package/src/discord/ui/embed.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { EmbedBuilder } from "discord.js";
|
|
2
|
+
import { truncateText } from "@/discord/ui/truncate-text";
|
|
2
3
|
|
|
3
4
|
/** Discord hard limit on the number of fields per embed. */
|
|
4
5
|
const MAX_EMBED_FIELDS = 25;
|
|
@@ -35,8 +36,8 @@ export function appendBoundedFields(
|
|
|
35
36
|
if (shown >= MAX_EMBED_FIELDS) {
|
|
36
37
|
break;
|
|
37
38
|
}
|
|
38
|
-
const name =
|
|
39
|
-
const value =
|
|
39
|
+
const name = truncateText(field.name, MAX_FIELD_NAME);
|
|
40
|
+
const value = truncateText(field.value, MAX_FIELD_VALUE);
|
|
40
41
|
if (usedChars + name.length + value.length > MAX_EMBED_TOTAL - EMBED_TOTAL_SAFETY_MARGIN) {
|
|
41
42
|
break;
|
|
42
43
|
}
|
|
@@ -51,13 +52,6 @@ export function appendBoundedFields(
|
|
|
51
52
|
}
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
function truncate(text: string, max: number): string {
|
|
55
|
-
if (text.length <= max) {
|
|
56
|
-
return text;
|
|
57
|
-
}
|
|
58
|
-
return `${text.slice(0, max - 1)}…`;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
55
|
/** Build a plain titled embed. */
|
|
62
56
|
export function buildEmbed(title: string, description: string): EmbedBuilder {
|
|
63
57
|
return new EmbedBuilder().setTitle(title).setDescription(description);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `text` cut to at most `max` characters, its last one an ellipsis when it
|
|
3
|
+
* had to be cut: Discord rejects a whole message over one text that is too
|
|
4
|
+
* long, so a value of unknown length is shortened rather than sent as is.
|
|
5
|
+
*
|
|
6
|
+
* @param text - the text to fit.
|
|
7
|
+
* @param max - the most characters the result may hold.
|
|
8
|
+
* @returns `text` itself when it fits, its shortened form otherwise.
|
|
9
|
+
*/
|
|
10
|
+
export function truncateText(text: string, max: number): string {
|
|
11
|
+
if (text.length <= max) {
|
|
12
|
+
return text;
|
|
13
|
+
}
|
|
14
|
+
return `${text.slice(0, max - 1)}…`;
|
|
15
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The message of an unknown thrown value, for a log field that only needs the
|
|
3
|
+
* one line: an `Error`'s own message, anything else as its string form.
|
|
4
|
+
*
|
|
5
|
+
* {@link import("@/errors/describe-error").describeError} is the fuller record
|
|
6
|
+
* — name, stack and nested causes — for failures worth investigating.
|
|
7
|
+
*/
|
|
8
|
+
export function errorMessage(error: unknown): string {
|
|
9
|
+
return error instanceof Error ? error.message : String(error);
|
|
10
|
+
}
|
package/src/i18n/catalog.ts
CHANGED
|
@@ -18,6 +18,15 @@ export interface CatalogEntry {
|
|
|
18
18
|
*/
|
|
19
19
|
export type Catalog = Readonly<Record<string, CatalogEntry>>;
|
|
20
20
|
|
|
21
|
+
/** The catalog keys a `*_MESSAGES` constant holds. */
|
|
22
|
+
export type MessageKey<M extends Readonly<Record<string, string>>> = M[keyof M];
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* A catalog with exactly one entry per key: a key without its entry, or an
|
|
26
|
+
* entry under a key no `*_MESSAGES` constant declares, fails to compile.
|
|
27
|
+
*/
|
|
28
|
+
export type KeyedCatalog<K extends string> = Readonly<Record<K, CatalogEntry>>;
|
|
29
|
+
|
|
21
30
|
/**
|
|
22
31
|
* Accumulates the core catalog and every module's catalog at boot. Registration
|
|
23
32
|
* is fail-fast: a duplicate key throws {@link DuplicateTranslationKeyError}
|
|
@@ -35,6 +44,11 @@ export class TranslationRegistry {
|
|
|
35
44
|
}
|
|
36
45
|
}
|
|
37
46
|
|
|
47
|
+
/** Whether `key` was registered by some catalog (and so has an English source). */
|
|
48
|
+
has(key: string): boolean {
|
|
49
|
+
return this.entries.has(key);
|
|
50
|
+
}
|
|
51
|
+
|
|
38
52
|
get(key: string): CatalogEntry | undefined {
|
|
39
53
|
return this.entries.get(key);
|
|
40
54
|
}
|
package/src/i18n/locale.ts
CHANGED
|
@@ -7,6 +7,9 @@ export const SUPPORTED_LOCALES = ["en", "fr"] as const;
|
|
|
7
7
|
|
|
8
8
|
export type Locale = (typeof SUPPORTED_LOCALES)[number];
|
|
9
9
|
|
|
10
|
+
/** The language every catalog entry provides (`CatalogEntry.en`): the last fallback of any lookup. */
|
|
11
|
+
export const SOURCE_LOCALE: Locale = "en";
|
|
12
|
+
|
|
10
13
|
/**
|
|
11
14
|
* Map a raw Discord locale string (`en-US`, `en-GB`, `fr`, `de`…) to a
|
|
12
15
|
* supported base language. Regional variants collapse to their base tag, so
|
package/src/i18n/translator.ts
CHANGED
|
@@ -22,6 +22,9 @@ export interface Translator {
|
|
|
22
22
|
resolve(locale: Locale, text: LocalizedText): string;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
/** Translates one catalog key into a locale already chosen by the caller. */
|
|
26
|
+
export type TranslateKey = (key: string) => string;
|
|
27
|
+
|
|
25
28
|
export interface TranslatorOptions {
|
|
26
29
|
readonly defaultLocale: Locale;
|
|
27
30
|
readonly logger: Logger;
|
package/src/module/module.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { ContextMenuCommand, SlashCommand } from "@/discord/command/types";
|
|
2
2
|
import type { ComponentHandler } from "@/discord/components/component-router";
|
|
3
3
|
import type { DiscordEvent } from "@/discord/events/types";
|
|
4
|
-
import type { Catalog } from "@/i18n";
|
|
4
|
+
import type { Catalog } from "@/i18n/catalog";
|
|
5
5
|
import type { ScheduledJob } from "@/scheduler/scheduler";
|
|
6
|
+
import type { SettingsDeclaration } from "@/settings/define-settings";
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* A self-contained vertical feature slice. A module bundles everything it
|
|
@@ -17,6 +18,12 @@ import type { ScheduledJob } from "@/scheduler/scheduler";
|
|
|
17
18
|
export interface BotModule {
|
|
18
19
|
/** Stable identifier, used in logs. */
|
|
19
20
|
readonly name: string;
|
|
21
|
+
/**
|
|
22
|
+
* Catalog key of the module's name as administrators read it (for example on
|
|
23
|
+
* its enable/disable toggle). Without one, screens show {@link name} as is and
|
|
24
|
+
* the translator logs it as an unknown key.
|
|
25
|
+
*/
|
|
26
|
+
readonly label?: string;
|
|
20
27
|
readonly commands?: readonly SlashCommand[];
|
|
21
28
|
/** Right-click entries on a member or a message (the Apps submenu). */
|
|
22
29
|
readonly contextMenuCommands?: readonly ContextMenuCommand[];
|
|
@@ -30,6 +37,13 @@ export interface BotModule {
|
|
|
30
37
|
* duplicate key across modules fails the boot, never a runtime lookup.
|
|
31
38
|
*/
|
|
32
39
|
readonly translations?: Catalog;
|
|
40
|
+
/**
|
|
41
|
+
* The module's settings declarations. The composition root collects them into
|
|
42
|
+
* the settings registry at boot, the same way it collects `translations`.
|
|
43
|
+
*/
|
|
44
|
+
readonly settings?: readonly SettingsDeclaration[];
|
|
45
|
+
/** Whether the module starts enabled in a guild that never toggled it. Defaults to `true`. */
|
|
46
|
+
readonly defaultEnabled?: boolean;
|
|
33
47
|
/** One-off initialisation run once at boot, after the client is ready. */
|
|
34
48
|
setup?(): Promise<void> | void;
|
|
35
49
|
/**
|
package/src/resilience/retry.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import cron from "node-cron";
|
|
2
|
+
import { errorMessage } from "@/errors/error-message";
|
|
2
3
|
import type { Logger } from "@/logger";
|
|
3
4
|
import {
|
|
4
5
|
AmbiguousJobScheduleError,
|
|
@@ -64,10 +65,6 @@ export interface Scheduler {
|
|
|
64
65
|
stop(): void;
|
|
65
66
|
}
|
|
66
67
|
|
|
67
|
-
function describeError(error: unknown): string {
|
|
68
|
-
return error instanceof Error ? error.message : String(error);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
68
|
/**
|
|
72
69
|
* A job as it actually arrives at runtime. The exported union already forbids
|
|
73
70
|
* the invalid combinations, which is exactly why the check below cannot be
|
|
@@ -233,7 +230,7 @@ export class CronScheduler implements Scheduler {
|
|
|
233
230
|
try {
|
|
234
231
|
await job.run();
|
|
235
232
|
} catch (error) {
|
|
236
|
-
this.logger.error({ job: job.name, err:
|
|
233
|
+
this.logger.error({ job: job.name, err: errorMessage(error) }, "Scheduled job failed");
|
|
237
234
|
}
|
|
238
235
|
}
|
|
239
236
|
|