feat(moderation): баны сервера, тайм-ауты и фильтры журнала аудита
Фаза 4 (AGENT.md 7.17, 7.18): модерация участников.
- миграция 00011: таблица `guild_bans` (бан переживает исключение участника);
- `PUT /guilds/{id}/bans/{user_id}` — бан с причиной и права `BAN_MEMBERS`:
исключает участника, отключает его от голосовой комнаты, пишет системное
сообщение, аудит и события `MEMBER_REMOVE`/`GUILD_DELETE`;
- `DELETE /guilds/{id}/bans/{user_id}` — снятие бана, `GET .../bans` — список
с автором и причиной (только модераторам);
- вход по приглашению для забаненного отклоняется с `guild.banned`;
- тайм-аут уже был в схеме, но не работал: кэш прав комнат не сбрасывался при
изменениях сервера, поэтому участник с тайм-аутом продолжал писать. Ключ
кэша комнат теперь включает сервер, `InvalidateGuild` чистит и его;
- тайм-аут уходит в `MEMBER_UPDATE` (и снимается событием), истёкшие
тайм-ауты не показываются в профиле участника;
- журнал аудита получил фильтры `action`, `actor_id`, `target_id`, `before_id`;
- тесты: полный цикл бана (включая запрет входа и разбан), права и иерархия,
блокировка сообщений тайм-аутом, запрет тайм-аута на себя, фильтры аудита.
This commit is contained in:
@@ -181,6 +181,12 @@ func (s *Server) registerInviteRoutes(api huma.API) {
|
||||
if err != nil {
|
||||
return nil, humaError(err)
|
||||
}
|
||||
// Бан сервера сильнее приглашения: снять его может только модератор.
|
||||
if banned, err := s.store.IsGuildBanned(ctx, invite.GuildID, user.ID); err != nil {
|
||||
return nil, humaError(err)
|
||||
} else if banned {
|
||||
return nil, humaErrorStatus(http.StatusForbidden, "guild.banned", "you are banned from this guild")
|
||||
}
|
||||
isCreator := invite.CreatorID != nil && *invite.CreatorID == user.ID
|
||||
if !isCreator {
|
||||
if _, _, err := s.requireGuildPermission(ctx, formatSnowflake(invite.GuildID), user, permissions.ManageGuild); err != nil {
|
||||
@@ -216,6 +222,12 @@ func (s *Server) registerInviteRoutes(api huma.API) {
|
||||
if err != nil {
|
||||
return nil, humaError(err)
|
||||
}
|
||||
// Бан сервера сильнее приглашения: снять его может только модератор.
|
||||
if banned, err := s.store.IsGuildBanned(ctx, invite.GuildID, user.ID); err != nil {
|
||||
return nil, humaError(err)
|
||||
} else if banned {
|
||||
return nil, humaErrorStatus(http.StatusForbidden, "guild.banned", "you are banned from this guild")
|
||||
}
|
||||
if invite.ExpiresAt != nil && invite.ExpiresAt.Before(time.Now().UTC()) {
|
||||
return nil, humaErrorStatus(http.StatusGone, "invite.expired", "invite has expired")
|
||||
}
|
||||
@@ -244,6 +256,12 @@ func (s *Server) registerInviteRoutes(api huma.API) {
|
||||
if err != nil {
|
||||
return nil, humaError(err)
|
||||
}
|
||||
// Бан сервера сильнее приглашения: снять его может только модератор.
|
||||
if banned, err := s.store.IsGuildBanned(ctx, invite.GuildID, user.ID); err != nil {
|
||||
return nil, humaError(err)
|
||||
} else if banned {
|
||||
return nil, humaErrorStatus(http.StatusForbidden, "guild.banned", "you are banned from this guild")
|
||||
}
|
||||
// Уже участник: повторное принятие не расходует использование.
|
||||
if _, err := s.store.GetGuildMember(ctx, invite.GuildID, user.ID); err == nil {
|
||||
output := &guildIDOutput{}
|
||||
|
||||
Reference in New Issue
Block a user