🤖 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`.
3.0 KiB
3.0 KiB
title, weight
| title | weight |
|---|---|
| Голосовые функции | 3 |
Цель: реализовать голосовое взаимодействие с изоляцией ресурсов по серверам (гильдиям) и корректным управлением аудиосессиями.
1. Изоляция ресурсов
Голосовые функции бота обязательно изолируются на уровне отдельного сервера (гильдии):
- Архитектурный шаблон: контроллер хранит соответствие
Map<GuildID, Player>. - Поведение:
- Подключение (Join): создать
Playerдля сервера A, если он ещё не существует. - Отключение (Leave): удалить
Playerи освободить соединение. - Изоляция: команды на сервере B никак не влияют на сервер A.
- Подключение (Join): создать
2. Маршрутизация команд
Голосовые функции опираются на набор команд, работу которых координирует контроллер:
| Команда | Действие | Логика контроллера |
|---|---|---|
/join |
Подключиться к голосовому каналу | createPlayer(guildId, channelId) |
/play |
Добавить трек в очередь | getPlayer(guildId).enqueue(track) |
/stop |
Отключиться от канала | destroyPlayer(guildId) |
3. Управление состоянием
Аудиосессии по своей природе эфемерны:
- Временное состояние: активный аудиопоток (сбрасывается при отключении).
- Долговременное состояние: очередь воспроизведения или плейлист (хранится в контроллере до перезапуска бота).
Tip
Надёжность: разделяйте логику очереди воспроизведения и логику голосового соединения. Тогда бот сможет переподключиться без потери пользовательского плейлиста.
4. Пример реализации
// lib/MusicController.ts
class MusicController {
private players = new Map<string, AudioPlayer>();
getPlayer(guildId: string): AudioPlayer {
if (!this.players.has(guildId)) {
this.players.set(guildId, new AudioPlayer());
}
return this.players.get(guildId);
}
destroyPlayer(guildId: string) {
this.players.get(guildId)?.disconnect();
this.players.delete(guildId);
}
}
См. также: