Ctrl K
ComunidadGuías…

internal/app/welcomemessages/service.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package welcomemessages

import (
	"context"
	"time"

	"telesrv/internal/domain"
	"telesrv/internal/store"
)

type ChannelAccess interface {
	ResolveChannel(ctx context.Context, userID, channelID int64) (domain.ChannelView, error)
}

type Option func(*Service)

func WithClock(now func() time.Time) Option {
	return func(s *Service) {
		if now != nil {
			s.now = now
		}
	}
}

type Service struct {
	messages store.WelcomeMessageStore
	channels ChannelAccess
	now      func() time.Time
}

func NewService(messages store.WelcomeMessageStore, channels ChannelAccess, options ...Option) *Service {
	s := &Service{messages: messages, channels: channels, now: time.Now}
	for _, option := range options {
		if option != nil {
			option(s)
		}
	}
	return s
}

// Authorize is the cheap gate RPC uses before resolving upload/media/rich
// content. Mutations call authorize again immediately before the store write so
// a concurrent demotion cannot turn this preflight into a stale capability.
func (s *Service) Authorize(ctx context.Context, userID int64, peer domain.Peer) error {
	return s.authorize(ctx, userID, peer)
}

func (s *Service) Create(ctx context.Context, userID int64, peer domain.Peer, randomID int64, content domain.WelcomeMessageContent) (domain.WelcomeMessage, bool, error) {
	if err := s.authorize(ctx, userID, peer); err != nil {
		return domain.WelcomeMessage{}, false, err
	}
	if err := content.Validate(); err != nil {
		return domain.WelcomeMessage{}, false, err
	}
	fingerprint, err := domain.WelcomeCreateFingerprint(peer, userID, randomID, content)
	if err != nil {
		return domain.WelcomeMessage{}, false, domain.ErrWelcomeMessageInvalid
	}
	return s.messages.CreateWelcomeMessage(ctx, domain.CreateWelcomeMessageRequest{
		Peer: peer, CreatorUserID: userID, Date: int(s.now().Unix()), RandomID: randomID,
		Content: content, CreateFingerprint: fingerprint,
	})
}

func (s *Service) Edit(ctx context.Context, userID int64, peer domain.Peer, id int, fields domain.WelcomeMessageEditFields) (domain.WelcomeMessage, error) {
	if err := s.authorize(ctx, userID, peer); err != nil {
		return domain.WelcomeMessage{}, err
	}
	return s.messages.EditWelcomeMessage(ctx, domain.EditWelcomeMessageRequest{
		Peer: peer, ID: id, EditDate: int(s.now().Unix()), Fields: fields,
	})
}

func (s *Service) List(ctx context.Context, userID int64, peer domain.Peer, hash int64) (domain.WelcomeMessageList, error) {
	if err := s.authorize(ctx, userID, peer); err != nil {
		return domain.WelcomeMessageList{}, err
	}
	return s.messages.ListWelcomeMessages(ctx, peer, hash)
}

func (s *Service) Delete(ctx context.Context, userID int64, peer domain.Peer, id int) (bool, error) {
	if err := s.authorize(ctx, userID, peer); err != nil {
		return false, err
	}
	return s.messages.DeleteWelcomeMessage(ctx, peer, id)
}

func (s *Service) DeleteAll(ctx context.Context, userID int64, peer domain.Peer) (bool, error) {
	if err := s.authorize(ctx, userID, peer); err != nil {
		return false, err
	}
	return s.messages.DeleteAllWelcomeMessages(ctx, peer)
}

// HasAny is used only after the ordinary full-chat access check has succeeded.
// It deliberately does not require manage_welcome_messages so non-admin members
// receive the same has_welcome_messages projection as official clients.
func (s *Service) HasAny(ctx context.Context, peer domain.Peer) (bool, error) {
	if s == nil || s.messages == nil || peer.Type != domain.PeerTypeChannel || peer.ID <= 0 {
		return false, domain.ErrWelcomeMessageInvalid
	}
	return s.messages.HasWelcomeMessages(ctx, peer)
}

func (s *Service) authorize(ctx context.Context, userID int64, peer domain.Peer) error {
	if s == nil || s.messages == nil || s.channels == nil || userID <= 0 ||
		peer.Type != domain.PeerTypeChannel || peer.ID <= 0 {
		return domain.ErrWelcomeMessageInvalid
	}
	view, err := s.channels.ResolveChannel(ctx, userID, peer.ID)
	if err != nil {
		return err
	}
	if view.Channel.Monoforum {
		return domain.ErrWelcomeMessagePeerInvalid
	}
	if !view.Self.CanManageWelcomeMessages() {
		return domain.ErrWelcomeMessageForbidden
	}
	return nil
}