feat(guilds): приватные комнаты — права доступа поверх серверных
Сервер:
- store: оверрайды всех комнат сервера одним запросом (без N+1) и снятие
оверрайда с признаком «был ли он»;
- API: PUT/DELETE /guilds/{id}/channels/{cid}/overwrites/{role|user}/{tid}
(права именами через `|`, как у ролей), step-up на изменение, проверка что
роль принадлежит серверу, а участник состоит в нём;
- список комнат отдаёт permission_overwrites; после правки сбрасывается кэш
прав комнаты, пишется аудит (channel.overwrite_set/delete) и уходит событие
GUILD_CHANNELS_SYNC — видимость комнаты меняется у всех участников.
Клиент:
- редактор «Доступ к комнате»: приватность одним переключателем (запрет
VIEW_CHANNEL для @everyone), права просмотра/переписки/входа для ролей и
участников, подтверждение личности по требованию сервера;
- в настройках комнаты теперь и голосовые комнаты (фон и права), вебхуки —
только у текстовых; событие GUILD_CHANNELS_SYNC перечитывает список комнат.
Тесты: 2 Go-теста (скрытие и открытие комнаты ролями, проверка цели и прав) и
3 web-теста редактора (маски, step-up, скрытие без MANAGE_ROLES).
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { parseGuildEmojiList } from './emojis';
|
||||
import { parseGuildSoundList } from './sounds';
|
||||
import { userStatuses, type Channel, type UserStatus } from './types';
|
||||
import { userStatuses, type Channel, type ChannelOverwrite, type UserStatus } from './types';
|
||||
import type { GatewayUser } from './gateway';
|
||||
import type { GuildEmoji } from './emojis';
|
||||
import type { GuildSound } from './sounds';
|
||||
@@ -284,6 +284,42 @@ export function parseSoundboardPlay(value: unknown): SoundboardPlayEvent | null
|
||||
}
|
||||
|
||||
/** Канал из `CHANNEL_CREATE`/`CHANNEL_UPDATE` — та же форма, что в REST. */
|
||||
/** `GUILD_CHANNELS_SYNC`: права комнат сервера изменились — перечитать список. */
|
||||
export function parseGuildChannelsSyncEvent(value: unknown): { guild_id: string } | null {
|
||||
const record = asRecord(value);
|
||||
if (record === null) {
|
||||
return null;
|
||||
}
|
||||
const guildId = asString(record['guild_id']);
|
||||
return guildId === undefined ? null : { guild_id: guildId };
|
||||
}
|
||||
|
||||
/** parseChannelOverwrites читает список оверрайдов прав комнаты. */
|
||||
export function parseChannelOverwrites(value: unknown): ChannelOverwrite[] {
|
||||
if (!Array.isArray(value)) {
|
||||
return [];
|
||||
}
|
||||
const result: ChannelOverwrite[] = [];
|
||||
for (const item of value) {
|
||||
const record = asRecord(item);
|
||||
if (record === null) {
|
||||
continue;
|
||||
}
|
||||
const targetType = asString(record['target_type']);
|
||||
const targetId = asString(record['target_id']);
|
||||
if ((targetType !== 'role' && targetType !== 'user') || targetId === undefined) {
|
||||
continue;
|
||||
}
|
||||
result.push({
|
||||
target_type: targetType,
|
||||
target_id: targetId,
|
||||
allow: asString(record['allow']) ?? '0',
|
||||
deny: asString(record['deny']) ?? '0',
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function parseChannelPayload(value: unknown): Channel | null {
|
||||
const record = asRecord(value);
|
||||
if (record === null) {
|
||||
@@ -327,6 +363,11 @@ export function parseChannelPayload(value: unknown): Channel | null {
|
||||
if (typeof record['can_view'] === 'boolean') {
|
||||
channel.can_view = record['can_view'];
|
||||
}
|
||||
// Права комнаты: нужны редактору доступа (AGENT.md 6.2).
|
||||
const overwrites = parseChannelOverwrites(record['permission_overwrites']);
|
||||
if (overwrites.length > 0) {
|
||||
channel.permission_overwrites = overwrites;
|
||||
}
|
||||
// Фон комнаты: приходит в READY и CHANNEL_UPDATE (AGENT.md 7.5).
|
||||
const backgroundFileId = asString(record['background_file_id']);
|
||||
if (backgroundFileId !== undefined) {
|
||||
|
||||
Reference in New Issue
Block a user