admin: add account spam restriction (join/message gate)

Adds a narrower spam sanction alongside the existing account freeze: a
restricted account keeps every existing membership and conversation, but
cannot join new channels/groups (public join or invite link) and cannot
start a new conversation with a non-contact. Reachable both as a standalone
admin action and as a decision on a reported user's moderation case, with
the same idempotent-supersession and appeal wiring freeze already has.
This commit is contained in:
Astra 2026-09-16 14:03:15 +01:00
parent 3ca8ef1a16
commit f33e25af8d
32 changed files with 750 additions and 44 deletions

View file

@ -550,6 +550,12 @@ type AccountFreezeService interface {
AccountFreeze(ctx context.Context, userID int64) (domain.AccountFreeze, bool, error)
}
// AccountRestrictionService exposes the narrower spam-restriction fact used
// by the central RPC join gate and the private-message send path.
type AccountRestrictionService interface {
AccountRestriction(ctx context.Context, userID int64) (domain.AccountRestriction, bool, error)
}
// AccountFreezeNotificationService owns the durable non-PTS notification
// queue. It is intentionally separate from AccountFreezeService so hot
// read-only gates can use a versioned fact cache without disabling queue
@ -607,6 +613,10 @@ type UserEmojiStatusUpdatesService interface {
type ContactsService interface {
GetContacts(ctx context.Context, userID int64, hash int64) (domain.ContactList, bool, error)
ContactIDs(ctx context.Context, userID int64, hash int64) ([]int, bool, error)
// IsContact reports whether peerUserID is in userID's own contact list.
// Used by the account-restriction send gate, which is a one-directional
// check unrelated to the recipient's privacy settings.
IsContact(ctx context.Context, userID, peerUserID int64) (bool, error)
AddContact(ctx context.Context, userID int64, input domain.ContactInput) (domain.Contact, error)
AcceptContact(ctx context.Context, userID, contactUserID int64) (domain.Contact, error)
ImportContacts(ctx context.Context, userID int64, inputs []domain.ContactInput) (domain.ImportContactsResult, error)
@ -1149,6 +1159,7 @@ type Deps struct {
AppUpdates updatecdn.Resolver
AccountFreeze AccountFreezeService
AccountFreezeNotifications AccountFreezeNotificationService
AccountRestriction AccountRestrictionService
AICompose AIComposeService
Ephemeral EphemeralService
EphemeralPush store.EphemeralPushBroker