@azurioh/discord-kernel 0.1.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/LICENSE +21 -0
- package/README.md +77 -0
- package/dist/authz/authorizer.d.ts +45 -0
- package/dist/authz/authorizer.d.ts.map +1 -0
- package/dist/authz/authorizer.js +31 -0
- package/dist/authz/authorizer.js.map +1 -0
- package/dist/authz/require-level-guard.d.ts +23 -0
- package/dist/authz/require-level-guard.d.ts.map +1 -0
- package/dist/authz/require-level-guard.js +37 -0
- package/dist/authz/require-level-guard.js.map +1 -0
- package/dist/clock.d.ts +13 -0
- package/dist/clock.d.ts.map +1 -0
- package/dist/clock.js +13 -0
- package/dist/clock.js.map +1 -0
- package/dist/concurrency/keyed-queue.d.ts +18 -0
- package/dist/concurrency/keyed-queue.d.ts.map +1 -0
- package/dist/concurrency/keyed-queue.js +37 -0
- package/dist/concurrency/keyed-queue.js.map +1 -0
- package/dist/concurrency/run-with-concurrency.d.ts +15 -0
- package/dist/concurrency/run-with-concurrency.d.ts.map +1 -0
- package/dist/concurrency/run-with-concurrency.js +40 -0
- package/dist/concurrency/run-with-concurrency.js.map +1 -0
- package/dist/config/env.d.ts +17 -0
- package/dist/config/env.d.ts.map +1 -0
- package/dist/config/env.js +49 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/errors.d.ts +14 -0
- package/dist/config/errors.d.ts.map +1 -0
- package/dist/config/errors.js +25 -0
- package/dist/config/errors.js.map +1 -0
- package/dist/datetime.d.ts +34 -0
- package/dist/datetime.d.ts.map +1 -0
- package/dist/datetime.js +188 -0
- package/dist/datetime.js.map +1 -0
- package/dist/discord/api-errors.d.ts +20 -0
- package/dist/discord/api-errors.d.ts.map +1 -0
- package/dist/discord/api-errors.js +90 -0
- package/dist/discord/api-errors.js.map +1 -0
- package/dist/discord/channel-exporter.d.ts +42 -0
- package/dist/discord/channel-exporter.d.ts.map +1 -0
- package/dist/discord/channel-exporter.js +3 -0
- package/dist/discord/channel-exporter.js.map +1 -0
- package/dist/discord/command/autocomplete-dispatcher.d.ts +26 -0
- package/dist/discord/command/autocomplete-dispatcher.d.ts.map +1 -0
- package/dist/discord/command/autocomplete-dispatcher.js +41 -0
- package/dist/discord/command/autocomplete-dispatcher.js.map +1 -0
- package/dist/discord/command/autocomplete.d.ts +18 -0
- package/dist/discord/command/autocomplete.d.ts.map +1 -0
- package/dist/discord/command/autocomplete.js +65 -0
- package/dist/discord/command/autocomplete.js.map +1 -0
- package/dist/discord/command/context.d.ts +33 -0
- package/dist/discord/command/context.d.ts.map +1 -0
- package/dist/discord/command/context.js +48 -0
- package/dist/discord/command/context.js.map +1 -0
- package/dist/discord/command/cooldown-guard.d.ts +25 -0
- package/dist/discord/command/cooldown-guard.d.ts.map +1 -0
- package/dist/discord/command/cooldown-guard.js +60 -0
- package/dist/discord/command/cooldown-guard.js.map +1 -0
- package/dist/discord/command/create-command.d.ts +100 -0
- package/dist/discord/command/create-command.d.ts.map +1 -0
- package/dist/discord/command/create-command.js +245 -0
- package/dist/discord/command/create-command.js.map +1 -0
- package/dist/discord/command/create-context-menu-command.d.ts +51 -0
- package/dist/discord/command/create-context-menu-command.d.ts.map +1 -0
- package/dist/discord/command/create-context-menu-command.js +128 -0
- package/dist/discord/command/create-context-menu-command.js.map +1 -0
- package/dist/discord/command/errors.d.ts +19 -0
- package/dist/discord/command/errors.d.ts.map +1 -0
- package/dist/discord/command/errors.js +27 -0
- package/dist/discord/command/errors.js.map +1 -0
- package/dist/discord/command/guard.d.ts +30 -0
- package/dist/discord/command/guard.d.ts.map +1 -0
- package/dist/discord/command/guard.js +34 -0
- package/dist/discord/command/guard.js.map +1 -0
- package/dist/discord/command/localization.d.ts +9 -0
- package/dist/discord/command/localization.d.ts.map +1 -0
- package/dist/discord/command/localization.js +13 -0
- package/dist/discord/command/localization.js.map +1 -0
- package/dist/discord/command/options.d.ts +113 -0
- package/dist/discord/command/options.d.ts.map +1 -0
- package/dist/discord/command/options.js +509 -0
- package/dist/discord/command/options.js.map +1 -0
- package/dist/discord/command/permission-guard.d.ts +21 -0
- package/dist/discord/command/permission-guard.d.ts.map +1 -0
- package/dist/discord/command/permission-guard.js +69 -0
- package/dist/discord/command/permission-guard.js.map +1 -0
- package/dist/discord/command/router.d.ts +48 -0
- package/dist/discord/command/router.d.ts.map +1 -0
- package/dist/discord/command/router.js +118 -0
- package/dist/discord/command/router.js.map +1 -0
- package/dist/discord/command/types.d.ts +51 -0
- package/dist/discord/command/types.d.ts.map +1 -0
- package/dist/discord/command/types.js +3 -0
- package/dist/discord/command/types.js.map +1 -0
- package/dist/discord/components/component-router.d.ts +116 -0
- package/dist/discord/components/component-router.d.ts.map +1 -0
- package/dist/discord/components/component-router.js +123 -0
- package/dist/discord/components/component-router.js.map +1 -0
- package/dist/discord/components/interactive-message/index.d.ts +3 -0
- package/dist/discord/components/interactive-message/index.d.ts.map +1 -0
- package/dist/discord/components/interactive-message/index.js +11 -0
- package/dist/discord/components/interactive-message/index.js.map +1 -0
- package/dist/discord/components/interactive-message/interactive-message-collector.d.ts +195 -0
- package/dist/discord/components/interactive-message/interactive-message-collector.d.ts.map +1 -0
- package/dist/discord/components/interactive-message/interactive-message-collector.js +151 -0
- package/dist/discord/components/interactive-message/interactive-message-collector.js.map +1 -0
- package/dist/discord/components/interactive-message/mount-interactive-message.d.ts +30 -0
- package/dist/discord/components/interactive-message/mount-interactive-message.d.ts.map +1 -0
- package/dist/discord/components/interactive-message/mount-interactive-message.js +44 -0
- package/dist/discord/components/interactive-message/mount-interactive-message.js.map +1 -0
- package/dist/discord/components/paginator/index.d.ts +5 -0
- package/dist/discord/components/paginator/index.d.ts.map +1 -0
- package/dist/discord/components/paginator/index.js +15 -0
- package/dist/discord/components/paginator/index.js.map +1 -0
- package/dist/discord/components/paginator/mount-paginator.d.ts +25 -0
- package/dist/discord/components/paginator/mount-paginator.d.ts.map +1 -0
- package/dist/discord/components/paginator/mount-paginator.js +41 -0
- package/dist/discord/components/paginator/mount-paginator.js.map +1 -0
- package/dist/discord/components/paginator/page-state.d.ts +21 -0
- package/dist/discord/components/paginator/page-state.d.ts.map +1 -0
- package/dist/discord/components/paginator/page-state.js +44 -0
- package/dist/discord/components/paginator/page-state.js.map +1 -0
- package/dist/discord/components/paginator/paginator-buttons.d.ts +17 -0
- package/dist/discord/components/paginator/paginator-buttons.d.ts.map +1 -0
- package/dist/discord/components/paginator/paginator-buttons.js +45 -0
- package/dist/discord/components/paginator/paginator-buttons.js.map +1 -0
- package/dist/discord/components/paginator/paginator-view.d.ts +26 -0
- package/dist/discord/components/paginator/paginator-view.d.ts.map +1 -0
- package/dist/discord/components/paginator/paginator-view.js +28 -0
- package/dist/discord/components/paginator/paginator-view.js.map +1 -0
- package/dist/discord/components/persistent-paginator.d.ts +47 -0
- package/dist/discord/components/persistent-paginator.d.ts.map +1 -0
- package/dist/discord/components/persistent-paginator.js +137 -0
- package/dist/discord/components/persistent-paginator.js.map +1 -0
- package/dist/discord/components/settings-editor/index.d.ts +7 -0
- package/dist/discord/components/settings-editor/index.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/index.js +22 -0
- package/dist/discord/components/settings-editor/index.js.map +1 -0
- package/dist/discord/components/settings-editor/mount-settings-editor.d.ts +140 -0
- package/dist/discord/components/settings-editor/mount-settings-editor.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/mount-settings-editor.js +344 -0
- package/dist/discord/components/settings-editor/mount-settings-editor.js.map +1 -0
- package/dist/discord/components/settings-editor/navigation.d.ts +49 -0
- package/dist/discord/components/settings-editor/navigation.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/navigation.js +39 -0
- package/dist/discord/components/settings-editor/navigation.js.map +1 -0
- package/dist/discord/components/settings-editor/settings-editor-card.view.d.ts +56 -0
- package/dist/discord/components/settings-editor/settings-editor-card.view.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/settings-editor-card.view.js +284 -0
- package/dist/discord/components/settings-editor/settings-editor-card.view.js.map +1 -0
- package/dist/discord/components/settings-editor/settings-editor-field-modal.d.ts +69 -0
- package/dist/discord/components/settings-editor/settings-editor-field-modal.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/settings-editor-field-modal.js +353 -0
- package/dist/discord/components/settings-editor/settings-editor-field-modal.js.map +1 -0
- package/dist/discord/components/settings-editor/settings-editor-fields.d.ts +216 -0
- package/dist/discord/components/settings-editor/settings-editor-fields.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/settings-editor-fields.js +66 -0
- package/dist/discord/components/settings-editor/settings-editor-fields.js.map +1 -0
- package/dist/discord/components/settings-editor/settings-editor.view.d.ts +303 -0
- package/dist/discord/components/settings-editor/settings-editor.view.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/settings-editor.view.js +316 -0
- package/dist/discord/components/settings-editor/settings-editor.view.js.map +1 -0
- package/dist/discord/events/create-event.d.ts +5 -0
- package/dist/discord/events/create-event.d.ts.map +1 -0
- package/dist/discord/events/create-event.js +8 -0
- package/dist/discord/events/create-event.js.map +1 -0
- package/dist/discord/events/event-router.d.ts +16 -0
- package/dist/discord/events/event-router.d.ts.map +1 -0
- package/dist/discord/events/event-router.js +51 -0
- package/dist/discord/events/event-router.js.map +1 -0
- package/dist/discord/events/types.d.ts +16 -0
- package/dist/discord/events/types.d.ts.map +1 -0
- package/dist/discord/events/types.js +3 -0
- package/dist/discord/events/types.js.map +1 -0
- package/dist/discord/i18n.d.ts +58 -0
- package/dist/discord/i18n.d.ts.map +1 -0
- package/dist/discord/i18n.js +173 -0
- package/dist/discord/i18n.js.map +1 -0
- package/dist/discord/interaction/button.d.ts +39 -0
- package/dist/discord/interaction/button.d.ts.map +1 -0
- package/dist/discord/interaction/button.js +36 -0
- package/dist/discord/interaction/button.js.map +1 -0
- package/dist/discord/interaction/interaction-router.d.ts +21 -0
- package/dist/discord/interaction/interaction-router.d.ts.map +1 -0
- package/dist/discord/interaction/interaction-router.js +24 -0
- package/dist/discord/interaction/interaction-router.js.map +1 -0
- package/dist/discord/interaction/modal.d.ts +171 -0
- package/dist/discord/interaction/modal.d.ts.map +1 -0
- package/dist/discord/interaction/modal.js +264 -0
- package/dist/discord/interaction/modal.js.map +1 -0
- package/dist/discord/interaction/select-menu.d.ts +104 -0
- package/dist/discord/interaction/select-menu.d.ts.map +1 -0
- package/dist/discord/interaction/select-menu.js +126 -0
- package/dist/discord/interaction/select-menu.js.map +1 -0
- package/dist/discord/permissions.d.ts +32 -0
- package/dist/discord/permissions.d.ts.map +1 -0
- package/dist/discord/permissions.js +40 -0
- package/dist/discord/permissions.js.map +1 -0
- package/dist/discord/placeholder-registry-errors.d.ts +10 -0
- package/dist/discord/placeholder-registry-errors.d.ts.map +1 -0
- package/dist/discord/placeholder-registry-errors.js +18 -0
- package/dist/discord/placeholder-registry-errors.js.map +1 -0
- package/dist/discord/placeholder-registry.d.ts +53 -0
- package/dist/discord/placeholder-registry.d.ts.map +1 -0
- package/dist/discord/placeholder-registry.js +52 -0
- package/dist/discord/placeholder-registry.js.map +1 -0
- package/dist/discord/presenter.d.ts +26 -0
- package/dist/discord/presenter.d.ts.map +1 -0
- package/dist/discord/presenter.js +3 -0
- package/dist/discord/presenter.js.map +1 -0
- package/dist/discord/ui/card.d.ts +114 -0
- package/dist/discord/ui/card.d.ts.map +1 -0
- package/dist/discord/ui/card.js +162 -0
- package/dist/discord/ui/card.js.map +1 -0
- package/dist/discord/ui/color-input-errors.d.ts +12 -0
- package/dist/discord/ui/color-input-errors.d.ts.map +1 -0
- package/dist/discord/ui/color-input-errors.js +21 -0
- package/dist/discord/ui/color-input-errors.js.map +1 -0
- package/dist/discord/ui/color-input.d.ts +37 -0
- package/dist/discord/ui/color-input.d.ts.map +1 -0
- package/dist/discord/ui/color-input.js +166 -0
- package/dist/discord/ui/color-input.js.map +1 -0
- package/dist/discord/ui/colors.d.ts +44 -0
- package/dist/discord/ui/colors.d.ts.map +1 -0
- package/dist/discord/ui/colors.js +45 -0
- package/dist/discord/ui/colors.js.map +1 -0
- package/dist/discord/ui/embed.d.ts +16 -0
- package/dist/discord/ui/embed.d.ts.map +1 -0
- package/dist/discord/ui/embed.js +53 -0
- package/dist/discord/ui/embed.js.map +1 -0
- package/dist/discord/ui/message-attachments.d.ts +40 -0
- package/dist/discord/ui/message-attachments.d.ts.map +1 -0
- package/dist/discord/ui/message-attachments.js +27 -0
- package/dist/discord/ui/message-attachments.js.map +1 -0
- package/dist/errors/business-error.d.ts +41 -0
- package/dist/errors/business-error.d.ts.map +1 -0
- package/dist/errors/business-error.js +39 -0
- package/dist/errors/business-error.js.map +1 -0
- package/dist/errors/describe-error.d.ts +29 -0
- package/dist/errors/describe-error.d.ts.map +1 -0
- package/dist/errors/describe-error.js +49 -0
- package/dist/errors/describe-error.js.map +1 -0
- package/dist/errors/incident-ref.d.ts +7 -0
- package/dist/errors/incident-ref.d.ts.map +1 -0
- package/dist/errors/incident-ref.js +13 -0
- package/dist/errors/incident-ref.js.map +1 -0
- package/dist/errors/index.d.ts +4 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +15 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/i18n/business-message.d.ts +14 -0
- package/dist/i18n/business-message.d.ts.map +1 -0
- package/dist/i18n/business-message.js +19 -0
- package/dist/i18n/business-message.js.map +1 -0
- package/dist/i18n/catalog.d.ts +27 -0
- package/dist/i18n/catalog.d.ts.map +1 -0
- package/dist/i18n/catalog.js +25 -0
- package/dist/i18n/catalog.js.map +1 -0
- package/dist/i18n/errors.d.ts +10 -0
- package/dist/i18n/errors.d.ts.map +1 -0
- package/dist/i18n/errors.js +18 -0
- package/dist/i18n/errors.js.map +1 -0
- package/dist/i18n/index.d.ts +6 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +16 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/i18n/locale.d.ts +22 -0
- package/dist/i18n/locale.d.ts.map +1 -0
- package/dist/i18n/locale.js +40 -0
- package/dist/i18n/locale.js.map +1 -0
- package/dist/i18n/translator.d.ts +35 -0
- package/dist/i18n/translator.d.ts.map +1 -0
- package/dist/i18n/translator.js +40 -0
- package/dist/i18n/translator.js.map +1 -0
- package/dist/logger.d.ts +28 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +3 -0
- package/dist/logger.js.map +1 -0
- package/dist/module/module.d.ts +42 -0
- package/dist/module/module.d.ts.map +1 -0
- package/dist/module/module.js +3 -0
- package/dist/module/module.js.map +1 -0
- package/dist/persistence/database.d.ts +24 -0
- package/dist/persistence/database.d.ts.map +1 -0
- package/dist/persistence/database.js +3 -0
- package/dist/persistence/database.js.map +1 -0
- package/dist/resilience/retry.d.ts +36 -0
- package/dist/resilience/retry.d.ts.map +1 -0
- package/dist/resilience/retry.js +78 -0
- package/dist/resilience/retry.js.map +1 -0
- package/dist/scheduler/errors.d.ts +25 -0
- package/dist/scheduler/errors.d.ts.map +1 -0
- package/dist/scheduler/errors.js +45 -0
- package/dist/scheduler/errors.js.map +1 -0
- package/dist/scheduler/scheduler.d.ts +68 -0
- package/dist/scheduler/scheduler.d.ts.map +1 -0
- package/dist/scheduler/scheduler.js +162 -0
- package/dist/scheduler/scheduler.js.map +1 -0
- package/package.json +89 -0
- package/src/authz/authorizer.ts +76 -0
- package/src/authz/require-level-guard.ts +42 -0
- package/src/clock.ts +18 -0
- package/src/concurrency/keyed-queue.ts +50 -0
- package/src/concurrency/run-with-concurrency.ts +44 -0
- package/src/config/env.ts +58 -0
- package/src/config/errors.ts +21 -0
- package/src/datetime.ts +221 -0
- package/src/discord/api-errors.ts +91 -0
- package/src/discord/channel-exporter.ts +48 -0
- package/src/discord/command/autocomplete-dispatcher.ts +54 -0
- package/src/discord/command/autocomplete.ts +76 -0
- package/src/discord/command/context.ts +85 -0
- package/src/discord/command/cooldown-guard.ts +81 -0
- package/src/discord/command/create-command.ts +431 -0
- package/src/discord/command/create-context-menu-command.ts +227 -0
- package/src/discord/command/errors.ts +22 -0
- package/src/discord/command/guard.ts +54 -0
- package/src/discord/command/localization.ts +11 -0
- package/src/discord/command/options.ts +847 -0
- package/src/discord/command/permission-guard.ts +74 -0
- package/src/discord/command/router.ts +142 -0
- package/src/discord/command/types.ts +52 -0
- package/src/discord/components/component-router.ts +213 -0
- package/src/discord/components/interactive-message/index.ts +18 -0
- package/src/discord/components/interactive-message/interactive-message-collector.ts +374 -0
- package/src/discord/components/interactive-message/mount-interactive-message.ts +71 -0
- package/src/discord/components/paginator/index.ts +17 -0
- package/src/discord/components/paginator/mount-paginator.ts +69 -0
- package/src/discord/components/paginator/page-state.ts +45 -0
- package/src/discord/components/paginator/paginator-buttons.ts +60 -0
- package/src/discord/components/paginator/paginator-view.ts +61 -0
- package/src/discord/components/persistent-paginator.ts +197 -0
- package/src/discord/components/settings-editor/index.ts +41 -0
- package/src/discord/components/settings-editor/mount-settings-editor.ts +646 -0
- package/src/discord/components/settings-editor/navigation.ts +67 -0
- package/src/discord/components/settings-editor/settings-editor-card.view.ts +344 -0
- package/src/discord/components/settings-editor/settings-editor-field-modal.ts +538 -0
- package/src/discord/components/settings-editor/settings-editor-fields.ts +290 -0
- package/src/discord/components/settings-editor/settings-editor.view.ts +654 -0
- package/src/discord/events/create-event.ts +7 -0
- package/src/discord/events/event-router.ts +53 -0
- package/src/discord/events/types.ts +17 -0
- package/src/discord/i18n.ts +173 -0
- package/src/discord/interaction/button.ts +75 -0
- package/src/discord/interaction/interaction-router.ts +28 -0
- package/src/discord/interaction/modal.ts +505 -0
- package/src/discord/interaction/select-menu.ts +246 -0
- package/src/discord/permissions.ts +57 -0
- package/src/discord/placeholder-registry-errors.ts +11 -0
- package/src/discord/placeholder-registry.ts +77 -0
- package/src/discord/presenter.ts +26 -0
- package/src/discord/ui/card.ts +303 -0
- package/src/discord/ui/color-input-errors.ts +17 -0
- package/src/discord/ui/color-input.ts +179 -0
- package/src/discord/ui/colors.ts +58 -0
- package/src/discord/ui/embed.ts +64 -0
- package/src/discord/ui/message-attachments.ts +57 -0
- package/src/errors/business-error.ts +49 -0
- package/src/errors/describe-error.ts +73 -0
- package/src/errors/incident-ref.ts +10 -0
- package/src/errors/index.ts +12 -0
- package/src/i18n/business-message.ts +23 -0
- package/src/i18n/catalog.ts +41 -0
- package/src/i18n/errors.ts +11 -0
- package/src/i18n/index.ts +11 -0
- package/src/i18n/locale.ts +41 -0
- package/src/i18n/translator.ts +78 -0
- package/src/logger.ts +28 -0
- package/src/module/module.ts +42 -0
- package/src/persistence/database.ts +23 -0
- package/src/resilience/retry.ts +102 -0
- package/src/scheduler/errors.ts +40 -0
- package/src/scheduler/scheduler.ts +248 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ActionRowBuilder,
|
|
3
|
+
type AnySelectMenuInteraction,
|
|
4
|
+
ChannelSelectMenuBuilder,
|
|
5
|
+
type ChannelSelectMenuInteraction,
|
|
6
|
+
type ChannelType,
|
|
7
|
+
RoleSelectMenuBuilder,
|
|
8
|
+
type RoleSelectMenuInteraction,
|
|
9
|
+
StringSelectMenuBuilder,
|
|
10
|
+
type StringSelectMenuInteraction,
|
|
11
|
+
UserSelectMenuBuilder,
|
|
12
|
+
type UserSelectMenuInteraction,
|
|
13
|
+
} from "discord.js";
|
|
14
|
+
import type {
|
|
15
|
+
ComponentAuthorization,
|
|
16
|
+
ComponentHandler,
|
|
17
|
+
} from "@/discord/components/component-router";
|
|
18
|
+
|
|
19
|
+
/** The builders this DSL can produce; the union an action row accepts. */
|
|
20
|
+
export type AnySelectMenuBuilder =
|
|
21
|
+
| StringSelectMenuBuilder
|
|
22
|
+
| ChannelSelectMenuBuilder
|
|
23
|
+
| RoleSelectMenuBuilder
|
|
24
|
+
| UserSelectMenuBuilder;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Context handed to `onSelect`, mirroring
|
|
28
|
+
* {@link import("@/discord/interaction/button").ComponentContext}: the
|
|
29
|
+
* triggering interaction plus the picked values, already unwrapped so a handler
|
|
30
|
+
* does not have to know where discord.js keeps them.
|
|
31
|
+
*/
|
|
32
|
+
export interface SelectContext<I extends AnySelectMenuInteraction> {
|
|
33
|
+
readonly interaction: I;
|
|
34
|
+
readonly values: readonly string[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Presentation options shared by every select flavour. Split from the handler so
|
|
39
|
+
* the shared `applyBaseOptions` never sees `onSelect`: a callback is
|
|
40
|
+
* contravariant in its interaction type, so a common supertype for the
|
|
41
|
+
* *definitions* cannot exist.
|
|
42
|
+
*/
|
|
43
|
+
export interface SelectLayout {
|
|
44
|
+
id: string;
|
|
45
|
+
placeholder?: string;
|
|
46
|
+
minValues?: number;
|
|
47
|
+
maxValues?: number;
|
|
48
|
+
disabled?: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** A select declaration: its layout plus the behaviour it carries. */
|
|
52
|
+
export interface BaseSelectDef<I extends AnySelectMenuInteraction> extends SelectLayout {
|
|
53
|
+
onSelect?: (ctx: SelectContext<I>) => Promise<void> | void;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Compiled select menu: renders itself (`apply`) and owns its behaviour
|
|
58
|
+
* (`onSelect`), exactly like a compiled
|
|
59
|
+
* {@link import("@/discord/interaction/button").Button}. It lives in the
|
|
60
|
+
* interaction layer for the same reason — it carries behaviour, it is not a
|
|
61
|
+
* passive widget.
|
|
62
|
+
*/
|
|
63
|
+
export interface SelectMenu<B, I extends AnySelectMenuInteraction> {
|
|
64
|
+
readonly id: string;
|
|
65
|
+
readonly onSelect?: (ctx: SelectContext<I>) => Promise<void> | void;
|
|
66
|
+
apply(forceDisabled?: boolean): B;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface StringSelectOption {
|
|
70
|
+
label: string;
|
|
71
|
+
value: string;
|
|
72
|
+
description?: string;
|
|
73
|
+
emoji?: string;
|
|
74
|
+
default?: boolean;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface StringSelectDef extends BaseSelectDef<StringSelectMenuInteraction> {
|
|
78
|
+
options: readonly StringSelectOption[];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface ChannelSelectDef extends BaseSelectDef<ChannelSelectMenuInteraction> {
|
|
82
|
+
/** Restricts what the picker offers, e.g. text channels only. */
|
|
83
|
+
channelTypes?: readonly ChannelType[];
|
|
84
|
+
/**
|
|
85
|
+
* What the picker opens already holding.
|
|
86
|
+
*
|
|
87
|
+
* A picker returns its whole selection, never a delta, so one that opens
|
|
88
|
+
* empty over a setting that already has a value turns "change this" into
|
|
89
|
+
* "type the list again" — and anything the administrator forgets to re-pick
|
|
90
|
+
* is silently dropped.
|
|
91
|
+
*/
|
|
92
|
+
defaultChannelIds?: readonly string[];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface RoleSelectDef extends BaseSelectDef<RoleSelectMenuInteraction> {
|
|
96
|
+
/** What the picker opens already holding. See {@link ChannelSelectDef.defaultChannelIds}. */
|
|
97
|
+
defaultRoleIds?: readonly string[];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface UserSelectDef extends BaseSelectDef<UserSelectMenuInteraction> {
|
|
101
|
+
/** What the picker opens already holding. See {@link ChannelSelectDef.defaultChannelIds}. */
|
|
102
|
+
defaultUserIds?: readonly string[];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Apply the options every select flavour shares onto its builder. */
|
|
106
|
+
function applyBaseOptions<B extends AnySelectMenuBuilder>(
|
|
107
|
+
builder: B,
|
|
108
|
+
def: SelectLayout,
|
|
109
|
+
forceDisabled: boolean,
|
|
110
|
+
): B {
|
|
111
|
+
builder.setCustomId(def.id);
|
|
112
|
+
if (def.placeholder !== undefined) {
|
|
113
|
+
builder.setPlaceholder(def.placeholder);
|
|
114
|
+
}
|
|
115
|
+
if (def.minValues !== undefined) {
|
|
116
|
+
builder.setMinValues(def.minValues);
|
|
117
|
+
}
|
|
118
|
+
if (def.maxValues !== undefined) {
|
|
119
|
+
builder.setMaxValues(def.maxValues);
|
|
120
|
+
}
|
|
121
|
+
builder.setDisabled(forceDisabled || (def.disabled ?? false));
|
|
122
|
+
return builder;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** A menu of caller-defined string values. */
|
|
126
|
+
export function createStringSelect(
|
|
127
|
+
def: StringSelectDef,
|
|
128
|
+
): SelectMenu<StringSelectMenuBuilder, StringSelectMenuInteraction> {
|
|
129
|
+
return {
|
|
130
|
+
id: def.id,
|
|
131
|
+
onSelect: def.onSelect,
|
|
132
|
+
apply(forceDisabled = false) {
|
|
133
|
+
const builder = applyBaseOptions(new StringSelectMenuBuilder(), def, forceDisabled);
|
|
134
|
+
return builder.addOptions(
|
|
135
|
+
...def.options.map((option) => ({
|
|
136
|
+
label: option.label,
|
|
137
|
+
value: option.value,
|
|
138
|
+
description: option.description,
|
|
139
|
+
emoji: option.emoji,
|
|
140
|
+
default: option.default,
|
|
141
|
+
})),
|
|
142
|
+
);
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** A menu populated by Discord with the guild's channels. */
|
|
148
|
+
export function createChannelSelect(
|
|
149
|
+
def: ChannelSelectDef,
|
|
150
|
+
): SelectMenu<ChannelSelectMenuBuilder, ChannelSelectMenuInteraction> {
|
|
151
|
+
return {
|
|
152
|
+
id: def.id,
|
|
153
|
+
onSelect: def.onSelect,
|
|
154
|
+
apply(forceDisabled = false) {
|
|
155
|
+
const builder = applyBaseOptions(new ChannelSelectMenuBuilder(), def, forceDisabled);
|
|
156
|
+
if (def.defaultChannelIds !== undefined && def.defaultChannelIds.length > 0) {
|
|
157
|
+
builder.setDefaultChannels(...def.defaultChannelIds);
|
|
158
|
+
}
|
|
159
|
+
if (def.channelTypes !== undefined) {
|
|
160
|
+
builder.setChannelTypes(...def.channelTypes);
|
|
161
|
+
}
|
|
162
|
+
return builder;
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** A menu populated by Discord with the guild's roles. */
|
|
168
|
+
export function createRoleSelect(
|
|
169
|
+
def: RoleSelectDef,
|
|
170
|
+
): SelectMenu<RoleSelectMenuBuilder, RoleSelectMenuInteraction> {
|
|
171
|
+
return {
|
|
172
|
+
id: def.id,
|
|
173
|
+
onSelect: def.onSelect,
|
|
174
|
+
apply(forceDisabled = false) {
|
|
175
|
+
const builder = applyBaseOptions(new RoleSelectMenuBuilder(), def, forceDisabled);
|
|
176
|
+
if (def.defaultRoleIds !== undefined && def.defaultRoleIds.length > 0) {
|
|
177
|
+
builder.setDefaultRoles(...def.defaultRoleIds);
|
|
178
|
+
}
|
|
179
|
+
return builder;
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* A menu populated by Discord with the guild's members.
|
|
186
|
+
*
|
|
187
|
+
* A distinct flavour from the mentionable one on purpose: naming a person is not
|
|
188
|
+
* the same act as naming a role, and the calls behind it — adding someone to the
|
|
189
|
+
* recap's ping list, granting a user a level — take a user id, not either.
|
|
190
|
+
*/
|
|
191
|
+
export function createUserSelect(
|
|
192
|
+
def: UserSelectDef,
|
|
193
|
+
): SelectMenu<UserSelectMenuBuilder, UserSelectMenuInteraction> {
|
|
194
|
+
return {
|
|
195
|
+
id: def.id,
|
|
196
|
+
onSelect: def.onSelect,
|
|
197
|
+
apply(forceDisabled = false) {
|
|
198
|
+
const builder = applyBaseOptions(new UserSelectMenuBuilder(), def, forceDisabled);
|
|
199
|
+
if (def.defaultUserIds !== undefined && def.defaultUserIds.length > 0) {
|
|
200
|
+
builder.setDefaultUsers(...def.defaultUserIds);
|
|
201
|
+
}
|
|
202
|
+
return builder;
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Wrap a select menu in its own action row. Unlike buttons, Discord gives a
|
|
209
|
+
* select menu a whole row, so there is nothing to group — hence a distinct
|
|
210
|
+
* helper from `createActionRow`.
|
|
211
|
+
*/
|
|
212
|
+
export function createSelectRow<B extends AnySelectMenuBuilder, I extends AnySelectMenuInteraction>(
|
|
213
|
+
menu: SelectMenu<B, I>,
|
|
214
|
+
forceDisabled = false,
|
|
215
|
+
): ActionRowBuilder<B> {
|
|
216
|
+
return new ActionRowBuilder<B>().addComponents(menu.apply(forceDisabled));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Bridge a select menu's `onSelect` to the persistent
|
|
221
|
+
* {@link import("@/discord/components/component-router").ComponentRouter}.
|
|
222
|
+
*
|
|
223
|
+
* Without this a declared `onSelect` would never run: the router dispatches to a
|
|
224
|
+
* `ComponentHandler`, and nothing else turns an interaction back into the menu
|
|
225
|
+
* that produced it. Register the result through a module's `components`.
|
|
226
|
+
*/
|
|
227
|
+
export function createSelectHandler<
|
|
228
|
+
B extends AnySelectMenuBuilder,
|
|
229
|
+
I extends AnySelectMenuInteraction,
|
|
230
|
+
>(menu: SelectMenu<B, I>, authorize: ComponentAuthorization): ComponentHandler {
|
|
231
|
+
return {
|
|
232
|
+
customId: menu.id,
|
|
233
|
+
// Passed in rather than defaulted: this bridge knows nothing about what
|
|
234
|
+
// the menu does, and a default here would be exactly the silent "anyone
|
|
235
|
+
// may click" the required field exists to prevent.
|
|
236
|
+
authorize,
|
|
237
|
+
async handle(interaction) {
|
|
238
|
+
// The router also routes buttons and modals to a matching customId; a
|
|
239
|
+
// menu only speaks for select interactions.
|
|
240
|
+
if (!interaction.isAnySelectMenu()) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
await menu.onSelect?.({ interaction: interaction as I, values: interaction.values });
|
|
244
|
+
},
|
|
245
|
+
};
|
|
246
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { PermissionFlagsBits, PermissionsBitField } from "discord.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Permission helpers shared by the command DSL, the guards and any adapter that
|
|
5
|
+
* needs to reason about what the bot (or a member) is allowed to do.
|
|
6
|
+
*
|
|
7
|
+
* Everything is expressed in terms of {@link PermissionBit} rather than raw
|
|
8
|
+
* `bigint`s: the values come from discord.js' `PermissionFlagsBits`, so a typo
|
|
9
|
+
* is a type error instead of a silently wrong bitfield.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A single Discord permission flag. Deriving the type from the enum-like object
|
|
14
|
+
* means the DSL accepts `PermissionFlagsBits.ManageChannels` and rejects an
|
|
15
|
+
* arbitrary `bigint`.
|
|
16
|
+
*/
|
|
17
|
+
export type PermissionBit = (typeof PermissionFlagsBits)[keyof typeof PermissionFlagsBits];
|
|
18
|
+
|
|
19
|
+
/** Combine permission flags into the single bitfield Discord's API expects. */
|
|
20
|
+
export function resolvePermissions(permissions: readonly PermissionBit[]): bigint {
|
|
21
|
+
return PermissionsBitField.resolve([...permissions]);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Bits in `required` that `held` lacks (empty when everything is granted).
|
|
26
|
+
*
|
|
27
|
+
* `held === null` means the permissions could not be resolved (outside a guild,
|
|
28
|
+
* or an uncached member). It returns *all* of `required`: an absent answer must
|
|
29
|
+
* never be mistaken for a granted one.
|
|
30
|
+
*/
|
|
31
|
+
export function missingPermissions(
|
|
32
|
+
held: Readonly<PermissionsBitField> | null,
|
|
33
|
+
required: readonly PermissionBit[],
|
|
34
|
+
): PermissionBit[] {
|
|
35
|
+
if (held === null) {
|
|
36
|
+
return [...required];
|
|
37
|
+
}
|
|
38
|
+
return required.filter((permission) => !held.has(permission));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Human-readable names for permission bits, for user-facing messages. Resolved
|
|
43
|
+
* by reverse lookup in `PermissionFlagsBits` so the names track discord.js
|
|
44
|
+
* rather than a hand-maintained table that silently rots.
|
|
45
|
+
*/
|
|
46
|
+
export function formatPermissions(permissions: readonly PermissionBit[]): string {
|
|
47
|
+
return permissions.map(permissionName).join(", ");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function permissionName(permission: PermissionBit): string {
|
|
51
|
+
for (const [name, bit] of Object.entries(PermissionFlagsBits)) {
|
|
52
|
+
if (bit === permission) {
|
|
53
|
+
return name;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return String(permission);
|
|
57
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Two providers registered the same token. Raised while modules register their
|
|
3
|
+
* placeholders at boot — a wiring mistake that must stop the boot, never a
|
|
4
|
+
* runtime condition a guild's rendered text could trigger.
|
|
5
|
+
*/
|
|
6
|
+
export class DuplicatePlaceholderTokenError extends Error {
|
|
7
|
+
constructor(readonly token: string) {
|
|
8
|
+
super(`Placeholder token "${token}" is registered twice`);
|
|
9
|
+
this.name = "DuplicatePlaceholderTokenError";
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { DuplicatePlaceholderTokenError } from "@/discord/placeholder-registry-errors";
|
|
2
|
+
import type { Locale } from "@/i18n";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A token a module offers to text another module's administrator writes,
|
|
6
|
+
* resolved only once that text is actually rendered.
|
|
7
|
+
*
|
|
8
|
+
* `core` sits between the two by design: a module that publishes a token
|
|
9
|
+
* (`tournament`'s `{tournament_template}`) and a module that renders text
|
|
10
|
+
* carrying it (`ticket`'s welcome message) never import each other —
|
|
11
|
+
* `AGENTS.md` forbids it. Both go through {@link PlaceholderRegistry} instead,
|
|
12
|
+
* which `bootstrap` hands to every module factory from the same container-held
|
|
13
|
+
* instance, so neither module ever names the other.
|
|
14
|
+
*/
|
|
15
|
+
export interface PlaceholderProvider {
|
|
16
|
+
/** The token, braces included — `{tournament_template}`. */
|
|
17
|
+
readonly token: string;
|
|
18
|
+
/** What it becomes, for one guild, at render time. */
|
|
19
|
+
resolve(guildId: string, locale: Locale): Promise<string>;
|
|
20
|
+
/**
|
|
21
|
+
* Catalog key describing this token in the legend an admin reads while
|
|
22
|
+
* writing the text — owned by the module that offers the token, since it is
|
|
23
|
+
* the only one that knows what the token means.
|
|
24
|
+
*/
|
|
25
|
+
readonly legendKey: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Accumulates every {@link PlaceholderProvider} registered at boot and resolves
|
|
30
|
+
* them against a piece of already-rendered text.
|
|
31
|
+
*
|
|
32
|
+
* Consulted at render, never at construction — that is what makes the order
|
|
33
|
+
* `bootstrap/modules.ts` lists modules in irrelevant here: a module registers
|
|
34
|
+
* into this from its own factory, whatever position it holds in that list, and
|
|
35
|
+
* `resolve` only ever reads the registry as it stands by the time some guild's
|
|
36
|
+
* text is actually rendered — well after every module has finished
|
|
37
|
+
* registering, unlike `ChannelExporter` (`bootstrap/channel-exporter.ts`),
|
|
38
|
+
* which has to be built with the enabled module list already known.
|
|
39
|
+
*/
|
|
40
|
+
export class PlaceholderRegistry {
|
|
41
|
+
private readonly providers: PlaceholderProvider[] = [];
|
|
42
|
+
|
|
43
|
+
/** @throws DuplicatePlaceholderTokenError — a boot-time wiring mistake. */
|
|
44
|
+
register(provider: PlaceholderProvider): void {
|
|
45
|
+
if (this.providers.some((existing) => existing.token === provider.token)) {
|
|
46
|
+
throw new DuplicatePlaceholderTokenError(provider.token);
|
|
47
|
+
}
|
|
48
|
+
this.providers.push(provider);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Every registered provider, in registration order — for building a legend. */
|
|
52
|
+
list(): readonly PlaceholderProvider[] {
|
|
53
|
+
return this.providers;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** The token every registered provider answers to. */
|
|
57
|
+
tokens(): readonly string[] {
|
|
58
|
+
return this.providers.map((provider) => provider.token);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Replace every registered token `text` carries with what it resolves to for
|
|
63
|
+
* `guildId`/`locale`. A provider whose token is absent from `text` is never
|
|
64
|
+
* asked to resolve — the common case, since one guild's text rarely uses
|
|
65
|
+
* every token every module has ever registered.
|
|
66
|
+
*/
|
|
67
|
+
async resolve(text: string, guildId: string, locale: Locale): Promise<string> {
|
|
68
|
+
let result = text;
|
|
69
|
+
for (const provider of this.providers) {
|
|
70
|
+
if (result.includes(provider.token)) {
|
|
71
|
+
const value = await provider.resolve(guildId, locale);
|
|
72
|
+
result = result.split(provider.token).join(value);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { EmbedBuilder } from "discord.js";
|
|
2
|
+
import type { Locale } from "@/i18n";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Port that renders framework-level responses (errors, denials, confirmations)
|
|
6
|
+
* as embeds. The command pipeline depends on this interface, never on a concrete
|
|
7
|
+
* embed module — so a bot can fully restyle/localise feedback by providing its
|
|
8
|
+
* own implementation, with zero changes to the core.
|
|
9
|
+
*
|
|
10
|
+
* Every method receives the locale resolved for the interaction being answered,
|
|
11
|
+
* so titles and fixed copy follow the user's language, not the process's.
|
|
12
|
+
*
|
|
13
|
+
* The default implementation lives in `infrastructure/discord/default-presenter.ts`.
|
|
14
|
+
*/
|
|
15
|
+
export interface Presenter {
|
|
16
|
+
/** A successful, affirmative action. */
|
|
17
|
+
confirmation(message: string, locale: Locale): EmbedBuilder;
|
|
18
|
+
/** A benign business problem (bad input, missing resource, conflict). */
|
|
19
|
+
warning(message: string, locale: Locale): EmbedBuilder;
|
|
20
|
+
/** A serious—but expected—business error. */
|
|
21
|
+
error(message: string, locale: Locale): EmbedBuilder;
|
|
22
|
+
/** An authorization denial from a guard. */
|
|
23
|
+
denial(message: string, locale: Locale): EmbedBuilder;
|
|
24
|
+
/** An unexpected failure, carrying a quotable incident reference. */
|
|
25
|
+
systemError(message: string, reference: string, locale: Locale): EmbedBuilder;
|
|
26
|
+
}
|