Add global command channel rules and enhance command handling

- Introduced global command channel whitelist and blacklist in the GuildSettings model for improved command access control.
- Implemented command gate logic in the command routing to handle channel and role restrictions, providing user feedback for denied commands.
- Enhanced the CommandsManager component to support global rules and channel/role selection, improving the user interface for managing command permissions.
- Updated localization files to include new keys for global command rules and related messages, ensuring clarity in user interactions.
This commit is contained in:
TheOnlyMace
2026-07-22 20:26:40 +02:00
parent 0fb8195236
commit d31660f813
24 changed files with 963 additions and 185 deletions

View File

@@ -88,6 +88,22 @@
- Musik- und KI-Modul (nur auf Zuruf)
- Premium-Zahlungsanbindung
## Post-Phase Command-Global-Rules + Channel-Picker (Status: implementiert)
### Abgeschlossen (Code)
- Globale Command Channel-Whitelist/-Blacklist in `GuildSettings` + Commands-UI
- Searchable Multi-Select für Kanäle/Rollen (statt Raw-IDs) auf der Commands-Seite
- APIs: `/commands/globals`, `/channels`, `/roles`
- Bot erzwingt globale + per-Command Channel/Role/Cooldown-Regeln in `routeCommand`
- Migration `20260722210000_command_global_channels`
### Manuell testen
- [ ] Commands: globale Whitelist/Blacklist speichern, in Discord prüfen
- [ ] Per-Command Channel/Rollen per Dropdown setzen
- [ ] Deaktivierter Command / Cooldown antwortet ephemeral
## Nächster geplanter Schritt
- Deploy auf Traefik-Server manuell verifizieren; danach ggf. `deploy``main` mergen (nur auf Freigabe).

Binary file not shown.

Before

Width:  |  Height:  |  Size: 448 KiB

After

Width:  |  Height:  |  Size: 431 KiB

View File

@@ -5,9 +5,9 @@
<stop offset="0.55" stop-color="#12141F"/>
<stop offset="1" stop-color="#0A0A0D"/>
</linearGradient>
<radialGradient id="glow" cx="28%" cy="48%" r="45%">
<stop offset="0" stop-color="#6366F1" stop-opacity="0.35"/>
<stop offset="0.55" stop-color="#6366F1" stop-opacity="0.12"/>
<radialGradient id="glow" cx="48%" cy="42%" r="42%">
<stop offset="0" stop-color="#6366F1" stop-opacity="0.32"/>
<stop offset="0.55" stop-color="#6366F1" stop-opacity="0.10"/>
<stop offset="1" stop-color="#6366F1" stop-opacity="0"/>
</radialGradient>
<linearGradient id="mark" x1="0" y1="1" x2="1" y2="0">
@@ -19,40 +19,42 @@
<rect width="1200" height="480" fill="url(#bg)"/>
<rect width="1200" height="480" fill="url(#glow)"/>
<!-- faint network dots -->
<!-- faint network dots (keep away from avatar corner) -->
<g fill="#6366F1" fill-opacity="0.18">
<circle cx="620" cy="90" r="3"/>
<circle cx="760" cy="140" r="2.5"/>
<circle cx="900" cy="100" r="3"/>
<circle cx="980" cy="200" r="2"/>
<circle cx="840" cy="280" r="2.5"/>
<circle cx="1040" cy="320" r="3"/>
<circle cx="700" cy="360" r="2"/>
<circle cx="1120" cy="160" r="2.5"/>
<circle cx="180" cy="70" r="2.5"/>
<circle cx="980" cy="80" r="3"/>
<circle cx="1080" cy="160" r="2.5"/>
<circle cx="1020" cy="280" r="2"/>
<circle cx="160" cy="160" r="2"/>
</g>
<g stroke="#6366F1" stroke-opacity="0.12" stroke-width="1.5" fill="none">
<path d="M620 90 L760 140 L900 100 L980 200"/>
<path d="M760 140 L840 280 L700 360"/>
<path d="M900 100 L1120 160 L1040 320"/>
<g stroke="#6366F1" stroke-opacity="0.10" stroke-width="1.5" fill="none">
<path d="M180 70 L160 160"/>
<path d="M980 80 L1080 160 L1020 280"/>
</g>
<!-- N mark — shifted up so Discord avatar does not cover it -->
<g transform="translate(90,-70) scale(0.92)">
<g fill="none" stroke="url(#mark)" stroke-linecap="round" stroke-linejoin="round">
<path d="M170 362 V150 L342 362 V150" stroke-width="78" stroke-opacity="0.10"/>
<path d="M170 362 V150 L342 362 V150" stroke-width="52" stroke-opacity="0.16"/>
<path d="M170 362 V150 L342 362 V150" stroke-width="30"/>
<!--
Centered lockup: N + wordmark.
Shifted right so Discord's bottom-left avatar does not cover the N.
Local N bounds ~170..342 x 150..362; scale 0.78 → ~134×165.
-->
<g transform="translate(310,-20)">
<g transform="scale(0.78)">
<g fill="none" stroke="url(#mark)" stroke-linecap="round" stroke-linejoin="round">
<path d="M170 362 V150 L342 362 V150" stroke-width="78" stroke-opacity="0.10"/>
<path d="M170 362 V150 L342 362 V150" stroke-width="52" stroke-opacity="0.16"/>
<path d="M170 362 V150 L342 362 V150" stroke-width="30"/>
</g>
<g fill="url(#mark)">
<circle cx="170" cy="150" r="34"/>
<circle cx="170" cy="362" r="34"/>
<circle cx="342" cy="362" r="34"/>
<circle cx="342" cy="150" r="34"/>
</g>
<circle cx="342" cy="150" r="14" fill="#C7D2FE"/>
</g>
<g fill="url(#mark)">
<circle cx="170" cy="150" r="34"/>
<circle cx="170" cy="362" r="34"/>
<circle cx="342" cy="362" r="34"/>
<circle cx="342" cy="150" r="34"/>
</g>
<circle cx="342" cy="150" r="14" fill="#C7D2FE"/>
</g>
<!-- wordmark -->
<text x="560" y="200" fill="#E6E6EA" font-family="Inter, ui-sans-serif, system-ui, sans-serif" font-size="92" font-weight="700" letter-spacing="-2">Nexumi</text>
<text x="564" y="250" fill="#9A9AA5" font-family="Inter, ui-sans-serif, system-ui, sans-serif" font-size="28" font-weight="500">nexumi.de</text>
<!-- wordmark to the right of N, vertically centered to mark -->
<text x="320" y="195" fill="#E6E6EA" font-family="Inter, ui-sans-serif, system-ui, sans-serif" font-size="88" font-weight="700" letter-spacing="-2">Nexumi</text>
<text x="324" y="245" fill="#9A9AA5" font-family="Inter, ui-sans-serif, system-ui, sans-serif" font-size="26" font-weight="500">nexumi.de</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB