discord-bot-kernel/ROADMAP.md

105 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ROADMAP - Loki Blog Bot (Rust kernel)
> Цель: не клон `discord-bot` (экономика/музыка/левел), а бот для **блога/портфолио** - чистый, KISS, ≤200 строк/файл, ≤4 файла/папку.
---
## Сервер блога - желаемая структура Discord
```
📁 INFO #rules, #welcome, #announcements, #blog-feed (RSS → webhook), #changelog
📁 COMMUNITY #general, #ideas, #showcase (юзеры постят проекты), #feedback
📁 SUPPORT #open-ticket, #faq
📁 VOICE create-voice (temp), afk
📁 STAFF (🔒) #mod-logs, #bot-logs
↑ counter 👥 Members: N голосовой канал ВНЕ категории в самом верху (pos 0, bots excluded)
```
Роли: `@Verified` (кнопка в #welcome), `@Blogger`, `@Support`, `@Staff`, `@Muted`.
---
## Phase 1 - Ядро ✅ (скелет готов)
- [x] Rust 1.82, tokio, serenity 0.12 + poise 0.6 (slash + prefix `!`)
- [x] `config/loader` dotenv → BotConfig, `db/pool` WAL + FK=ON (фикс C6 старого бота)
- [x] `db/migration` idempotent транзакции (фикс C5)
- [x] `core/lifecycle` trait, `core/bot` ReadyEvent + graceful shutdown
- [x] `feature/help|info|admin` - `!ping`, `!serverinfo`, `!shutdown` (owner) → теперь help интерактивный (селект категорий)
- [x] `ds-setup` CLI: `inspect` / `bootstrap --dry-run/--apply` (BOT token, user-token с ворнингом)
**Запуск:**
```bash
cp .env.example .env # вставь DISCORD_BOT_TOKEN, GUILD_ID
cargo run --bin bot
cargo run --bin ds-setup -- inspect --guild-id 123
cargo run --bin ds-setup -- bootstrap --guild-id 123 --dry-run
```
## Phase 2 - Верификация и доступ (guild 1509503154708811837)
- [x] **Verify (embed+✅)** - `#✅|verify` read-only, auto-embed на старте, `✅` → `✅ Verified`
- [x] Help - переписан как в Java: `help/categories.rs` + `help/handler.rs` + селект `help:category` → `UpdateMessage`, цвета/эмодзи по категориям, фильтр мод-прав (Info/Mod/Tickets/Utility), как `HelpMenuBuilder.java`/`HelpMenuHandler.java`
- [x] Counter fix - `feature/counter/task.rs`: вне категории `position(0)`, каждый старт проверяет `parent_id` и `position`, поднимает вверх; считает только живых (пагинация `members(1000, after)`, `!user.bot`), fallback на `approximate_member_count`
- [ ] `!mute/!unmute/!warn/!purge/!slowmode` - атомарные транзакции
- [ ] Logging в #mod-logs (join/leave, edit/delete, mod actions)
- [ ] RateLimiter + `core/queue` Semaphore 100 (уже заготовка)
## Phase 3 - Блог-фичи (главное отличие от старого бота)
- [ ] RSS/Telegram → #blog-feed (webhook, `reqwest` + `feed-rs`)
- [ ] `!post` - анонс поста (embed с cover, линк)
- [x] GitHub webhook → #changelog (нативная интеграция Discord, без кода: GitHub webhook URL = `<discord-webhook-url>/github`)
- [x] Ticket v2 - SelectMenu `ticket_topic` + Modal `ticket_modal:<topic>` (subject/description) → приватный канал в SUPPORT, overwrites за 1 операцию, sanitize имени, claim, blacklist, transcript → hastebin - `feature/ticket/handler.rs` + auto-panel `ensure_ticket_panel()` на Ready (как verify)
### Phase 3.1 - Ticket hardening (TODO, приоритезировано 22.09.2026)
- [ ] **Add/Remove user** - `/ticket_add @user` `/ticket_remove @user` (staff): `MANAGE_CHANNELS` check, `channel.permission_overwrites` add/remove Member overwrite `VIEW+SEND`, лог в ticket-logs, DM добавленному.
- [ ] **Close reason modal + Reopen/Delete** - `Close` → modal `ticket_close_reason` (reason 10-500), затем `ControlPanel`: `Reopen` (снимает `archived`, восстанавливает overwrites), `Transcript` (haste + file), `Delete` (транскрипт → `ChannelId::delete`). Как `ticketPresentation.js:166` `buildClosedControlPanelRow()` (Reopen secondary, Save secondary, Delete danger).
- [ ] **Cooldown 5 мин + RateLimiter per user** - после `cleanup_ticket` писать в `ticket_cooldowns(user_id, guild_id, expires_at)`; при `on_select/on_modal` проверять `now < expires_at` → ephemeral `Wait 3m 12s`. Plus глобальный `RateLimiter` per-user 1 ticket/5m + burst check.
- [ ] **Orphan reconciliation on boot** - `ticketReconcile()` в `ready`: `SELECT tickets` → для каждого `guild.channels().fetch(ticket.channel_id)` если `None` → `DELETE FROM tickets/claims`, лог embed `Ticket Manually Deleted` (без транскрипта, как `threadDelete.js`), dm owner. Итого `ticketRepository.listOpenTickets()` аналог.
- [ ] **Presence `watching N tickets` + counter** - `Context::set_presence(ActivityData::watching(format!("{} tickets", count)))`, обновлять на `create/close/reconcile`; `ticket_count` из `SELECT COUNT(*) FROM tickets WHERE guild_id=?`.
- [ ] **Threads vs Channels** - сейчас `ChannelType::Text` в SUPPORT (лимит 500 каналов, видно в списке). При >50 тикетов/день → мигрировать на `ChannelType::PublicThread`/`PrivateThread` внутри `#open-ticket` (лимит 1000 активных тредов, авто-архив 24ч/7д). Флаг `TICKET_USE_THREADS=false` в env, абстрагировать `create_ticket_channel` vs `create_thread`.
- [ ] **Anti-spam** - в тикет-каналах: `slowmode 5s` по умолчанию, `mention` лимит (1 @everyone → warn), бот роль выше `Verified`/`Muted` (иначе `403 add_roles`), `MANAGE_ROLES`/`MANAGE_CHANNELS` проверке на старте.
- [ ] **Forum/channel tags vs одна категория** - см. § Forum Tags ниже.
## Phase 4 - Комьюнити
- [x] Welcome embed (`feature/welcome`)
- [x] Counters (member count в voice-канале, `feature/counter`, refresh 10 мин, теперь вне категории наверху)
- [x] Polls (`/poll вопрос; вариант1; вариант2`, embed + реакции цифрами, `feature/poll`)
- [x] Scheduler / напоминания (`/remind 10m текст`, личные, SQLite + фоновый воркер 30с, `feature/remind`)
- [x] Temp voice (`#create-voice` → временный канал, `feature/voice`)
## Phase 5 - Опционально (AI)
- [ ] AI FAQ (DeepSeek/GPT) в #faq - только по базе знаний блога
- [ ] Auto-mod (спам/ссылки)
---
## Forum / Channel Tags — вместо одной категории SUPPORT
**Что это:** Discord с 2022г поддерживает **Forum-каналы** (`ChannelType::Forum`). Внутри один канал-форум, а каждый тикет — пост-тред с тегами. Теги — это встроенные лейблы Discord (до 20 штук, цвет+эмодзи), например: `❓ General`, `🐛 Bug`, `🔇 Appeal`, `😡 Report`. Юзер при создании поста выбирает тег(и), фильтрация по тегам нативная.
**Как это соотносится с твоей текущей схемой:**
- Сейчас: `SUPPORT` — категория, внутри создаются `ticket-bug-username` каналы (`ChannelType::Text`, `parent_id=SUPPORT_CATEGORY`, `permissions` на каждого юзера). Минус: жрёт лимит 500 каналов, каждый тикет виден в сайдбаре всем стаффам сразу, надо руками чистить.
- Форум-альтернатива: создаёшь **один** канал `Forum` (`#support-tickets`, `ChannelType::Forum`, `available_tags=[ General, Bug, Appeal, Report, Owners ]` + `default_reaction_emoji`). Панель остаётся (`StringSelect` → modal), но `create_ticket` делает `forum.create_forum_post(title=subject, content=description, tags=[topicTag])` → пост-тред. Права наследуются от форума (private threads видны только `@everyone` deny + thread member). Преимущества и лимиты:
| Критерий | Каналы в категории | Forum tags |
|---|---|---|
| Лимит | 500 каналов/гильдия | 1000 активных тредов + неограниченно архивированных |
| Сайдбар | каждый тикет — отдельный канал (шум) | один форум-канал, внутри список постов (чисто) |
| Теги/фильтр | категория одна, топик в имени канала `ticket-bug-` | нативные теги (фильтр, поиск, цвет, required/optional) |
| Права | `PermissionOverwrite` на канал (кастом) | `thread_add_member` + `VIEW_CHANNEL` deny на форум |
| Транскрипт | `channel.messages().fetch()` | `thread.messages().fetch()` аналогично |
| Авто-архив | ручной `delete` | нативный `default_auto_archive_duration` 1ч/24ч/7д |
| Сортировка | вручную | по активности, по тегам |
**Рекомендация для Loki Dev:** пока <50 тикетов/день — оставляй **каналы** (проще дебажить, транскрипт/claim привычнее). При росте — мигрировать на **форум с тегами** без смены UX: селект/ модалка остаются, меняется только `service::create_ticket` (флаг `TICKET_USE_FORUM`). В форум-режиме `SUPPORT_CATEGORY` не нужен, теги создаются один раз на старте (`GET/PATCH /channels/{forum.id}` с `available_tags`). Это «канал-теги вместо категории» — один канал с 5 тегами заменяет 5 подканалов/префиксов.
---
## Уроки старого бота - что НЕ повторяем
F1,F2,F4 - экономика: атомарные `UPDATE ... WHERE cash>=?`, daily `WHERE last_daily != today`.
F8/C1 - не блокировать gateway: все DB/HTTP вне EventHandler, `tokio::spawn`, `sqlx` async.
C6 - `foreign_keys=ON` через `SqliteConnectOptions::foreign_keys(true)`, не одним `PRAGMA`.
M1 - `destroy()` на `ScheduledExecutorService` → в Rust `JoinHandle::abort` / `pool.close().await`.
## Качество
`cargo fmt --check`, `cargo clippy -- -D warnings`, `cargo test`, `cargo audit`. Лимит 200 строк - `rustfmt max_width=100`.
Каждый `feature/*` ≤4 файла, >150 строк → дробить (`service/`, `repository/`).