feat(messages): счётчики упоминаний, квота приглашений и read state автора

- упоминания в сообщении увеличивают счётчик упоминаний комнаты у упомянутых
  (store.BumpMentionCount) и отправляют им READ_STATE_UPDATE на все устройства;
- отправка сообщения отмечает его прочитанным для автора;
- подтверждение прочтения сбрасывает упоминания;
- суточная квота 10 приглашений на пользователя (AGENT.md 8.6), администратор
  инстанса её обходит; httpx.NewRateLimiterWindow для произвольных окон;
- тест на рост и сброс счётчика упоминаний.
This commit is contained in:
2026-09-19 23:37:02 +03:00
parent 0e39b8e027
commit 8619f37cd5
15 changed files with 186 additions and 6 deletions
+18
View File
@@ -274,6 +274,24 @@ func (s *Store) SetReadState(ctx context.Context, userID, channelID, lastMessage
return err
}
// BumpMentionCount увеличивает счётчик упоминаний пользователя в комнате и
// возвращает новое значение (AGENT.md 7.16).
func (s *Store) BumpMentionCount(ctx context.Context, userID, channelID uint64) (int, error) {
var count int
err := s.writer.QueryRowContext(ctx, `
INSERT INTO channel_read_states (user_id, channel_id, last_message_id, mention_count, updated_at)
VALUES (?, ?, 0, 1, ?)
ON CONFLICT (user_id, channel_id) DO UPDATE SET
mention_count = channel_read_states.mention_count + 1,
updated_at = excluded.updated_at
RETURNING mention_count`,
int64(userID), int64(channelID), s.Now()).Scan(&count)
if err != nil {
return 0, mapError(err)
}
return count, nil
}
// ListReadStates отдаёт состояния прочтения пользователя (для READY).
func (s *Store) ListReadStates(ctx context.Context, userID uint64) ([]ReadState, error) {
rows, err := s.reader.QueryContext(ctx, `