2026-09-20 18:13:04 +03:00
|
|
|
|
/**
|
|
|
|
|
|
* Системные сообщения (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 }
|
2026-09-20 19:30:43 +03:00
|
|
|
|
| { kind: 'member_leave'; userId: string }
|
2026-09-20 18:13:04 +03:00
|
|
|
|
| { kind: 'member_kick'; userId: string }
|
|
|
|
|
|
| { kind: 'channel_created'; name: string; channelType: string };
|
|
|
|
|
|
|
|
|
|
|
|
function asRecord(value: unknown): Record<string, unknown> | null {
|
|
|
|
|
|
return typeof value === 'object' && value !== null ? (value as Record<string, unknown>) : 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'];
|
2026-09-20 19:30:43 +03:00
|
|
|
|
if (event === 'member_join' || event === 'member_leave' || event === 'member_kick') {
|
2026-09-20 18:13:04 +03:00
|
|
|
|
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;
|
|
|
|
|
|
}
|