feat(emojis): кастомные эмодзи сервера и системные сообщения
AGENT.md 7.6, 7.12:
- таблица `guild_emojis` (миграция 00008), загрузка PNG/JPEG/WebP/GIF до 512 КБ
с правом MANAGE_EMOJIS, имя `[a-zA-Z0-9_]{2,32}`, лимит 100 на сервер;
- ручки: список эмодзи сервера, переименование, удаление (с удалением файла),
загрузка multipart; аудит emoji.create/update/delete и событие
GUILD_EMOJIS_UPDATE всем участникам сервера;
- кастомные эмодзи в READY (`guilds[].emojis` с токеном `<:name:id>`), реакции
кастомными эмодзи проверяются по файлу, неизвестный эмодзи — 422;
- файлы эмодзи доступны авторизованным (используются в чужих сообщениях);
- системные сообщения: создание комнаты, вход участника (по приглашению и в
публичный сервер) и исключение участника пишутся в ленту типом `system`;
- тесты: права на загрузку, список, реакция кастомным эмодзи, переименование и
удаление, фильтрация системных записей в проверках истории.
This commit is contained in:
@@ -18,6 +18,9 @@ import (
|
||||
// mentionPattern находит упоминания вида <@123> (AGENT.md 7.6).
|
||||
var mentionPattern = regexp.MustCompile(`<@([0-9]{1,20})>`)
|
||||
|
||||
// customEmojiPattern находит кастомные эмодзи <:name:id> и <a:name:id> (AGENT.md 7.12).
|
||||
var customEmojiPattern = regexp.MustCompile(`<a?:([a-zA-Z0-9_]{2,32}):([0-9]{1,20})>`)
|
||||
|
||||
// messageKey ключует лимиты по комнате и пользователю.
|
||||
func messageKey(channelID, userID uint64) string {
|
||||
return formatSnowflake(channelID) + ":" + formatSnowflake(userID)
|
||||
@@ -743,9 +746,20 @@ func (s *Server) changeReaction(ctx context.Context, rawChannelID, rawMessageID,
|
||||
return nil, err
|
||||
}
|
||||
emoji = strings.TrimSpace(emoji)
|
||||
if emoji == "" || len([]rune(emoji)) > 32 {
|
||||
if emoji == "" || len([]rune(emoji)) > 64 {
|
||||
return nil, humaErrorStatus(http.StatusUnprocessableEntity, "validation.failed", "emoji is invalid")
|
||||
}
|
||||
// Реакция кастомным эмодзи: проверяем, что файл существует и это эмодзи
|
||||
// доступного сервера (AGENT.md 7.12).
|
||||
if match := customEmojiPattern.FindStringSubmatch(emoji); match != nil {
|
||||
fileID, err := parseID("emoji", match[2])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := s.store.EmojiByFileID(ctx, fileID); err != nil {
|
||||
return nil, humaErrorStatus(http.StatusUnprocessableEntity, "validation.failed", "unknown custom emoji")
|
||||
}
|
||||
}
|
||||
channelID, _, _, err := s.requireChannelPermission(ctx, rawChannelID, user, permissions.AddReactions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
Reference in New Issue
Block a user