feat(social): друзья, личные беседы, присутствие, аватары и часовой пояс
По запросу пользователя (вне очереди AGENT.md §13): - главного сервера как точки входа больше нет: новичок начинает с пустым списком серверов, вход — только по приглашению или созданием своего; - друзья: поиск по логину с экранированием LIKE, заявки (POST /users/@me/relationships), принятие, удаление/отклонение, списки friends/ incoming/outgoing/blocked; - личные беседы: POST /users/@me/channels (идемпотентно), GET /users/@me/channels со собеседником, статусом и последним сообщением; сообщения в DM работают через общие ручки комнат, доступ — только участникам (посторонний получает 404, события в Gateway тоже фильтруются); - присутствие: last_seen_at обновляется при активности, «невидимка» и простой дольше двух минут выглядят как офлайн, смена статуса рассылает PRESENCE_UPDATE друзьям; - READY отдаёт dm_channels (собеседник, аватар, статус, последнее сообщение); - профиль: timezone (по умолчанию Europe/Moscow) в PATCH /users/@me, загрузка аватара POST /users/@me/avatar (проверка, что это изображение, в том числе по содержимому) и удаление DELETE /users/@me/avatar; старый файл удаляется с диска; - тесты: заявки в друзья, личные беседы и их изоляция, «невидимка», часовой пояс и аватар, PRESENCE_UPDATE другу, отсутствие событий DM у постороннего.
This commit is contained in:
@@ -539,8 +539,29 @@ func (s *Server) requireChannelPermission(ctx context.Context, rawChannelID stri
|
||||
return 0, permissions.Resolved{}, nil, humaError(err)
|
||||
}
|
||||
if channel.GuildID == nil {
|
||||
// Личные комнаты появятся в Фазе 4: сейчас их нет.
|
||||
return 0, permissions.Resolved{}, nil, humaErrorStatus(http.StatusNotFound, "not_found", "channel not found")
|
||||
// Личная беседа: участник получает права на переписку, посторонний
|
||||
// не видит канал вовсе (AGENT.md 7.8).
|
||||
if channel.Type != store.ChannelDM {
|
||||
return 0, permissions.Resolved{}, nil, humaErrorStatus(http.StatusNotFound, "not_found", "channel not found")
|
||||
}
|
||||
participant, err := s.store.IsDMParticipant(ctx, channelID, user.ID)
|
||||
if err != nil {
|
||||
return 0, permissions.Resolved{}, nil, humaError(err)
|
||||
}
|
||||
if !participant {
|
||||
return 0, permissions.Resolved{}, nil, humaErrorStatus(http.StatusNotFound, "not_found", "channel not found")
|
||||
}
|
||||
dmPermissions := permissions.ViewChannel | permissions.SendMessages |
|
||||
permissions.ReadMessageHistory | permissions.AttachFiles | permissions.AddReactions
|
||||
resolved := permissions.Resolved{
|
||||
Guild: dmPermissions,
|
||||
Channel: dmPermissions,
|
||||
IsMember: true,
|
||||
}
|
||||
if !resolved.Can(permission) {
|
||||
return 0, permissions.Resolved{}, nil, humaErrorStatus(http.StatusForbidden, "perm.denied", "permission denied")
|
||||
}
|
||||
return channelID, resolved, channel, nil
|
||||
}
|
||||
resolved, err := s.perms.Channel(ctx, *channel.GuildID, channelID, user.ID, user.IsInstanceAdmin)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user