/** * Системные сообщения (AGENT.md §7.6). * * Сервер пишет их с `type: "system"`, а `content` — JSON-строка о событии: * `{"event":"member_join","user_id":"123"}`, `{"event":"member_kick",…}` или * `{"event":"channel_created","name":"общий","type":"text"}`. Разбор живёт * отдельно от компонентов: лента рисует человекочитаемую строку, а * справочник авторов по нему же добирает идентификаторы участников. * * Неизвестное событие и невалидный JSON возвращают `null` — вызывающий код * показывает исходный текст сообщения как есть, ничего не теряя. */ /** Событие системного сообщения. */ export type SystemMessageEvent = | { kind: 'member_join'; userId: string } | { kind: 'member_leave'; userId: string } | { kind: 'member_kick'; userId: string } | { kind: 'channel_created'; name: string; channelType: string }; function asRecord(value: unknown): Record | null { return typeof value === 'object' && value !== null ? (value as Record) : null; } /** Разбор `content` системного сообщения. */ export function parseSystemMessage(content: string): SystemMessageEvent | null { let payload: unknown; try { payload = JSON.parse(content) as unknown; } catch { // Не JSON — показываем текст как есть. return null; } const record = asRecord(payload); const event = record?.['event']; const userId = record?.['user_id']; if (event === 'member_join' || event === 'member_leave' || event === 'member_kick') { return typeof userId === 'string' && userId !== '' ? { kind: event, userId } : null; } if (event === 'channel_created') { const name = record?.['name']; if (typeof name !== 'string' || name === '') { return null; } const type = record?.['type']; return { kind: 'channel_created', name, channelType: typeof type === 'string' ? type : 'text' }; } return null; } /** * Идентификатор пользователя из системного сообщения. Нужен ленте: имя * автора события резолвится тем же справочником, что и авторы сообщений. */ export function systemMessageUserId(content: string): string | null { const event = parseSystemMessage(content); return event !== null && event.kind !== 'channel_created' ? event.userId : null; }