fix: cyrillic fonts, dev certs, page head, and ru-text typography (#67)

🤖 PR generated for branch `fix/cyrillic-fonts-dev-certs-and-page-head`.

### Changes Summary
- **Fonts**: Add Cyrillic glyph subsets for Lato, EB Garamond, and Fira
Code.
- **Dev**: Support remote Root CA and encrypted dev CA key in
`.env.dev`.
- **UI & Kits**: Add size variant to `Breadcrumbs` component and enlarge
`PageHead` breadcrumbs with tuned collapse scroll threshold.
- **ESLint**: Add `no-hanging-particles` i18n & markdown typography rule
with auto-fixer.
- **Content & i18n**: Apply `ru-text` editorial and typography polish
(em dashes, number ranges, «ё» restoration, non-breaking spaces) across
`services/www` and `services/docs`.
This commit is contained in:
deytenit authored and GitHub committed 2026-10-25 15:21:09 +03:00
1 parent c6487dc258
commit b3638f4d63
111 files changed
+2350 -1512

No files matched your search

+4 -4
View File
@@ -1,6 +1,6 @@
---
title: Quetza Bot
description: Модульный фреймворк для Discord-ботов.
description: Модульный фреймворк для Discord-ботов.
weight: 1
type: docs
cascade:
@@ -13,9 +13,9 @@ cascade:
## Основные возможности
- **Обнаружение модулей по соглашениям:** создайте папку, и Quetza автоматически загрузит её как модуль.
- **Изоляция контроллеров:** управление состоянием происходит на уровне сервера (guild), что исключает утечки данных между серверами.
- **Декларативные команды:** определение слэш-команд Discord через типизированный интерфейс.
- **Обнаружение модулей по соглашениям:** создайте папку, и Quetza автоматически загрузит её как модуль.
- **Изоляция контроллеров:** управление состоянием происходит на уровне сервера (guild), что исключает утечки данных между серверами.
- **Декларативные команды:** определение слэш-команд Discord через типизированный интерфейс.
- **Реактивные события:** подписка на любые события Discord.js внутри модулей.
## Разделы
@@ -1,5 +1,5 @@
---
title: Архитектура и философия
title: Архитектура и философия
weight: 1
---
@@ -16,9 +16,9 @@ Quetza сокращает шаблонный код за счёт строго
### Композиция важнее наследования (Composition over Inheritance)
Модули собираются из набора возможностей (capabilities), а не наследуются от базового класса.
Модули собираются из набора возможностей (capabilities), а не наследуются от базового класса.
- **Гибкость:** внутри модуля можно использовать любую архитектуру (функциональную или ООП), если модуль экспортирует обязательные интерфейсы.
- **Гибкость:** внутри модуля можно использовать любую архитектуру (функциональную или ООП), если модуль экспортирует обязательные интерфейсы.
### Изоляция важнее интеграции (Isolation over Integration)
@@ -9,7 +9,7 @@ weight: 2
### Объявление модуля (Module Declaration)
Файл `module.ts` служит манифестом и точкой входа при поиске модулей.
Файл `module.ts` служит манифестом и точкой входа при поиске модулей.
```typescript
export const name = 'my-module'; // ID
@@ -18,7 +18,7 @@ export const description = '...'; // Документация
### Регистрация команд (Command Registration)
Выполняется при наступлении события `ready`:
Выполняется при наступлении события `ready`:
1. Quetza сканирует каталоги `commands/`.
2. Формирует структуру `ApplicationCommandData`.
@@ -26,7 +26,7 @@ export const description = '...'; // Документация
### Внедрение контроллера (Controller Injection)
Экземпляр контроллера передаётся аргументом при вызове (injected), а не импортируется модулем напрямую:
Экземпляр контроллера передаётся аргументом при вызове (injected), а не импортируется модулем напрямую:
```typescript
// Фреймворк вызывает это:
@@ -37,15 +37,15 @@ execute(client, interaction, controller);
## Управление состоянием
### Эфемерность по умолчанию
### Эфемерность по умолчанию
В Quetza **нет встроенной базы данных**.
- **Память:** контроллеры сохраняют данные в стандартных JavaScript-коллекциях `Map` или `Set`.
- **Перезапуск:** при остановке процесса всё состояние в памяти сбрасывается.
- **Постоянное хранение:** если данные нужно сохранять между запусками, подключите внешнюю СУБД (например, SQLite или Redis) внутри контроллера.
- **Память:** контроллеры сохраняют данные в стандартных JavaScript-коллекциях `Map` или `Set`.
- **Перезапуск:** при остановке процесса всё состояние в памяти сбрасывается.
- **Постоянное хранение:** если данные нужно сохранять между запусками, подключите внешнюю СУБД (например, SQLite или Redis) внутри контроллера.
### Область видимости как ключ
### Область видимости как ключ
Ключи состояния должны соответствовать области его видимости:
@@ -58,7 +58,7 @@ execute(client, interaction, controller);
- **Валидация:** проверяйте права и входные параметры сразу.
- **Сообщения для пользователя:** «У вас нет прав» вместо сухого кода вроде «Ошибка: 403».
- **Изоляция:** сбой внутри `/ping` перехватывается локально и не роняет процесс бота.
- **Изоляция:** сбой внутри `/ping` перехватывается локально и не роняет процесс бота.
---
@@ -5,9 +5,9 @@ weight: 1
**Quetza** — модульный фреймворк на TypeScript для Discord-ботов. Он позволяет собирать ботов из самодостаточных независимых модулей.
## Что такое Quetza?
## Что такое Quetza?
Quetza даёт архитектурный каркас, где разработка новой функциональности сводится к добавлению модулей, а не правке ядра. Это решает проблему жёсткой архитектуры за счёт инверсии зависимостей: ядро ничего не знает о модулях, а модули взаимодействуют с системой через типизированные контракты.
Quetza даёт архитектурный каркас, где разработка новой функциональности сводится к добавлению модулей, а не правке ядра. Это решает проблему жёсткой архитектуры за счёт инверсии зависимостей: ядро ничего не знает о модулях, а модули взаимодействуют с системой через типизированные контракты.
## Основная философия
@@ -17,25 +17,25 @@ Quetza даёт архитектурный каркас, где разработ
### Решение
Quetza работает с модулями как с плагинами:
Quetza работает с модулями как с плагинами:
- **Расширение без правки ядра:** добавьте каталог с модулем, и функция подключена. Менять исходный код ядра не нужно.
- **Расширение без правки ядра:** добавьте каталог с модулем, и функция подключена. Менять исходный код ядра не нужно.
- **Изоляция:** модули выполняются независимо друг от друга.
- **Композиция:** подключайте и отключайте возможности, добавляя или удаляя каталоги модулей.
- **Композиция:** подключайте и отключайте возможности, добавляя или удаляя каталоги модулей.
## Высокоуровневая архитектура
Quetza состоит из трёх уровней:
1. **Базовый уровень (Foundation Layer):** клиент `Discord.js`, расширенный реестрами команд, событий и модулей. Выступает в роли сервис-локатора.
2. **Модульный уровень (Module Layer):** изолированные наборы функций. Модуль объявляет собственные команды и события через стандартизированную структуру файлов.
3. **Сервисный уровень (Service Layer):** контроллеры для управления состоянием бизнес-логики (например, аудиоплеером или контекстом диалога) на разных серверах (guilds).
2. **Модульный уровень (Module Layer):** изолированные наборы функций. Модуль объявляет собственные команды и события через стандартизированную структуру файлов.
3. **Сервисный уровень (Service Layer):** контроллеры для управления состоянием бизнес-логики (например, аудиоплеером или контекстом диалога) на разных серверах (guilds).
## Ключевые концепции
### Ментальная модель
При работе с Quetza полезна следующая аналогия:
При работе с Quetza полезна следующая аналогия:
- **Модули — плагины:** независимые каталоги, обнаруживаемые во время запуска.
- **Команды — эндпоинты:** обработчики без состояния, работающие аналогично конечным точкам REST API.
@@ -44,11 +44,11 @@ Quetza состоит из трёх уровней:
### Границы системы
Ограничения, которые важно учитывать при проектировании:
Ограничения, которые важно учитывать при проектировании:
- **Нет встроенного постоянного хранилища (persistence):** состояние в памяти сбрасывается при перезапуске. Для долговременного хранения нужна внешняя база данных.
- **Нет межмодульного взаимодействия:** модули не вызывают друг друга напрямую, что гарантирует слабую связанность.
- **Нет динамической перезагрузки:** модули загружаются один раз при старте процесса.
- **Нет встроенного постоянного хранилища (persistence):** состояние в памяти сбрасывается при перезапуске. Для долговременного хранения нужна внешняя база данных.
- **Нет межмодульного взаимодействия:** модули не вызывают друг друга напрямую, что гарантирует слабую связанность.
- **Нет динамической перезагрузки:** модули загружаются один раз при старте процесса.
## Архитектурные инварианты
@@ -53,4 +53,4 @@ export async function execute(interaction, controller) {
> [!WARNING]
> **Расходы на API:**
> Провайдеры ИИ обычно тарифицируют запросы по токенам. Настройте в контроллере строгие лимиты запросов (rate limits) для пользователей, чтобы избежать лишних расходов.
> Провайдеры ИИ обычно тарифицируют запросы по токенам. Настройте в контроллере строгие лимиты запросов (rate limits) для пользователей, чтобы избежать лишних расходов.
@@ -7,13 +7,13 @@ weight: 2
## 1. Подготовка окружения
Quetza настраивается через переменные окружения. **Никогда** не сохраняйте их в систему контроля версий.
Quetza настраивается через переменные окружения. **Никогда** не сохраняйте их в систему контроля версий.
| Переменная | Описание | Обязательно |
| :--- | :--- | :--- |
| `DISCORD_TOKEN` | Токен бота из Discord Developer Portal | **Да** |
| `CLIENT_ID` | Идентификатор приложения для регистрации слэш-команд | **Да** |
| `LLAMA_API_KEY` | Требуется для модуля ИИ | Нет |
| `CLIENT_ID` | Идентификатор приложения для регистрации слэш-команд | **Да** |
| `LLAMA_API_KEY` | Требуется для модуля ИИ | Нет |
## 2. Способы развёртывания
@@ -36,7 +36,7 @@ Quetza настраивается через переменные окружен
```
{{< /tab >}}
{{< tab name="Из исходного кода" >}}
Развёртывание **из исходного кода** подходит для разработки и отладки.
Развёртывание **из исходного кода** подходит для разработки и отладки.
1. **Установка** зависимостей:
@@ -7,11 +7,11 @@ weight: 1
## 1. Проектирование модуля
Перед написанием кода выберите подходящую структуру модуля:
Перед написанием кода выберите подходящую структуру модуля:
- **Простая команда:** достаточно каталога `commands/` (например, для служебных команд).
- **Реактивная логика:** нужен каталог `events/` для обработки событий Discord (например, приветственных сообщений).
- **Логика с состоянием:** понадобится контроллер (`Controller`) для управления состоянием между серверами (например, для музыкального плеера).
- **Простая команда:** достаточно каталога `commands/` (например, для служебных команд).
- **Реактивная логика:** нужен каталог `events/` для обработки событий Discord (например, приветственных сообщений).
- **Логика с состоянием:** понадобится контроллер (`Controller`) для управления состоянием между серверами (например, для музыкального плеера).
## 2. Структура каталогов
@@ -85,7 +85,7 @@ export function execute(client) {
Перезапустите бота:
1. Найдите в логах строку «Loaded module: my-feature».
2. Убедитесь, что команда `/ping` появилась и работает в Discord.
2. Убедитесь, что команда `/ping` появилась и работает в Discord.
{{% /steps %}}
@@ -93,7 +93,7 @@ export function execute(client) {
| Шаблон | Назначение | Состав |
| :--- | :--- | :--- |
| **Простая команда** | Утилиты без сохранения состояния (`/ping`, бросок кубика) | Только `commands/` |
| **Простая команда** | Утилиты без сохранения состояния (`/ping`, бросок кубика) | Только `commands/` |
| **Сервис с состоянием** | Музыка, ролевые игры, экономика | `commands/` + `lib/Controller` |
| **Событийная логика** | Автомодерация, логирование | Только `events/` |
| **Гибридный модуль** | Сложные сценарии (например, ИИ-чат) | Все компоненты |
@@ -11,7 +11,7 @@ weight: 3
- **Архитектурный шаблон:** контроллер хранит соответствие `Map<GuildID, Player>`.
- **Поведение:**
- **Подключение (Join):** создать `Player` для сервера A, если он ещё не существует.
- **Подключение (Join):** создать `Player` для сервера A, если он ещё не существует.
- **Отключение (Leave):** удалить `Player` и освободить соединение.
- **Изоляция:** команды на сервере B никак не влияют на сервер A.
@@ -29,11 +29,11 @@ weight: 3
Аудиосессии по своей природе эфемерны:
- **Временное состояние:** активный аудиопоток (сбрасывается при отключении).
- **Долговременное состояние:** очередь воспроизведения или плейлист (хранится в контроллере до перезапуска бота).
- **Временное состояние:** активный аудиопоток (сбрасывается при отключении).
- **Долговременное состояние:** очередь воспроизведения или плейлист (хранится в контроллере до перезапуска бота).
> [!TIP]
> **Надёжность:** разделяйте логику очереди воспроизведения и логику голосового соединения. Тогда бот сможет переподключиться без потери пользовательского плейлиста.
> **Надёжность:** разделяйте логику очереди воспроизведения и логику голосового соединения. Тогда бот сможет переподключиться без потери пользовательского плейлиста.
## 4. Пример реализации
@@ -3,7 +3,7 @@ title: Устранение неполадок
weight: 5
---
Диагностика неполадок в **Quetza** в основном касается структуры файлов и концептуальных ошибок. Поскольку фреймворк опирается на соглашения (convention-based), многие сбои происходят «молча» — например, модуль не загружается из-за неверного пути к файлу, не вызывая явных ошибок и трассировок стека.
Диагностика неполадок в **Quetza** в основном касается структуры файлов и концептуальных ошибок. Поскольку фреймворк опирается на соглашения (convention-based), многие сбои происходят «молча» — например, модуль не загружается из-за неверного пути к файлу, не вызывая явных ошибок и трассировок стека.
## Основные категории проблем
@@ -3,34 +3,34 @@ title: Диагностика
weight: 1
---
**Цель:** выявить и устранить частые ошибки при разработке под Quetza.
**Цель:** выявить и устранить частые ошибки при разработке под Quetza.
## 1. Сбои при обнаружении модулей
## 1. Сбои при обнаружении модулей
**Симптом:** бот запускается, но модуль не подхватывается.
**Причина:** нарушена структура файлов или соглашения об именах.
**Симптом:** бот запускается, но модуль не подхватывается.
**Причина:** нарушена структура файлов или соглашения об именах.
{{% details title="Чек-лист: Структура директорий" closed="false" %}}
- [ ] Каталог модуля существует: `src/modules/<name>/`.
- [ ] Файл `module.ts` экспортирует переменную `name`.
- [ ] Каталог команд называется именно `commands/` (а не `cmds/`).
- [ ] Каталог команд называется именно `commands/` (а не `cmds/`).
- [ ] Выполнена сборка `pnpm build` (Quetza работает из `dist/`).
{{% /details %}}
## 2. Проблемы с регистрацией команд
## 2. Проблемы с регистрацией команд
**Симптом:** команда `/command` не появляется в Discord.
### Глобальная регистрация и регистрация на тестовом сервере
### Глобальная регистрация и регистрация на тестовом сервере
- **Глобальная регистрация:** команды, зарегистрированные глобально, могут обновляться до 1 часа и подпадают под жёсткие лимиты Discord по частоте запросов (rate limits).
- **Тестовый сервер (guild):** в режиме разработки Quetza публикует команды на указанном сервере мгновенно.
- **Настройка:** проверьте, что в `.env` установлены переменные `NODE_ENV=development` и `DEV_GUILD_ID`.
- **Настройка:** проверьте, что в `.env` установлены переменные `NODE_ENV=development` и `DEV_GUILD_ID`.
### Лимиты частоты запросов (Rate Limits)
Частая повторная публикация глобальных команд приводит к превышению лимитов Discord. Для итеративной разработки всегда используйте тестовый сервер.
Частая повторная публикация глобальных команд приводит к превышению лимитов Discord. Для итеративной разработки всегда используйте тестовый сервер.
## 3. Ошибки взаимодействия (Interaction Failures)
@@ -53,7 +53,7 @@ await interaction.editReply('Готово');
## 4. Утечки состояния
**Симптом:** потребление памяти растёт, данные перемешиваются между разными серверами.
**Причина:** состояние хранится глобально, а не изолированно для каждого сервера.
**Причина:** состояние хранится глобально, а не изолированно для каждого сервера.
**Диагностика:**
Проверьте код контроллера:
@@ -63,10 +63,10 @@ await interaction.editReply('Готово');
## 5. Обработка событий
**Симптом:** обработчик срабатывает несколько раз или не вызывается вовсе.
**Симптом:** обработчик срабатывает несколько раз или не вызывается вовсе.
- **Срабатывает несколько раз:** слушатели событий суммируются. Проверьте, не зарегистрирован ли один и тот же обработчик в двух разных модулях.
- **Не срабатывает:** убедитесь, что обработчик корректно экспортирует `name` и `execute`.
- **Срабатывает несколько раз:** слушатели событий суммируются. Проверьте, не зарегистрирован ли один и тот же обработчик в двух разных модулях.
- **Не срабатывает:** убедитесь, что обработчик корректно экспортирует `name` и `execute`.
> [!TIP]
> **Режим отладки (Debug Mode):**