generated from VLADIMIR/template
159 lines
5.0 KiB
Go
159 lines
5.0 KiB
Go
package screen_service
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"evening_detective_server/internal/repos/screen_sessions_repo"
|
|
)
|
|
|
|
// ErrInvalidMode indicates an unsupported display mode.
|
|
var ErrInvalidMode = errors.New("некорректный режим экрана")
|
|
|
|
// ErrScreenNotFound indicates that a screen session was not found.
|
|
var ErrScreenNotFound = errors.New("экран не найден")
|
|
|
|
// ScreenService handles screen sessions.
|
|
type ScreenService struct {
|
|
repo *screen_sessions_repo.ScreenSessionsRepo
|
|
}
|
|
|
|
// NewScreenService creates a screen service.
|
|
func NewScreenService(repo *screen_sessions_repo.ScreenSessionsRepo) *ScreenService {
|
|
return &ScreenService{repo: repo}
|
|
}
|
|
|
|
// Create creates a screen session.
|
|
func (s *ScreenService) Create(ctx context.Context, tokenHash string) (int, error) {
|
|
return s.repo.Create(ctx, tokenHash)
|
|
}
|
|
|
|
// Get returns a screen session.
|
|
func (s *ScreenService) Get(ctx context.Context, id int, tokenHash string) (*ScreenSession, error) {
|
|
session, err := s.repo.Get(ctx, id, tokenHash)
|
|
if err != nil {
|
|
return nil, mapError(err)
|
|
}
|
|
return mapSession(session), nil
|
|
}
|
|
|
|
// ListByGame returns screen sessions for a game.
|
|
func (s *ScreenService) ListByGame(ctx context.Context, gameID int) ([]*ScreenSession, error) {
|
|
sessions, err := s.repo.ListByGame(ctx, gameID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result := make([]*ScreenSession, 0, len(sessions))
|
|
for _, session := range sessions {
|
|
result = append(result, mapSession(session))
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// ValidMode reports whether mode is a supported display mode.
|
|
func ValidMode(mode int32) bool {
|
|
switch mode {
|
|
case 1, 2, 4, 6, 7, 8, 9, 10:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// SetMode updates the display mode for a game.
|
|
func (s *ScreenService) SetMode(ctx context.Context, gameID int, mode int32) error {
|
|
if !ValidMode(mode) {
|
|
return ErrInvalidMode
|
|
}
|
|
return s.repo.SetMode(ctx, gameID, mode)
|
|
}
|
|
|
|
// SetTimer updates the display timer for a game.
|
|
func (s *ScreenService) SetTimer(ctx context.Context, gameID int, duration int64, label string, timerType int32) error {
|
|
if !ValidTimerType(timerType) {
|
|
return ErrInvalidMode
|
|
}
|
|
return s.repo.SetTimer(ctx, gameID, duration, label, timerType)
|
|
}
|
|
|
|
// SetText updates the text screen for a game.
|
|
func (s *ScreenService) SetText(ctx context.Context, gameID int, text string) error {
|
|
return s.repo.SetText(ctx, gameID, text)
|
|
}
|
|
|
|
// SetTimerCommand updates the timer command for a game.
|
|
func (s *ScreenService) SetTimerCommand(ctx context.Context, gameID int, command int32) error {
|
|
return s.repo.SetTimerCommand(ctx, gameID, command)
|
|
}
|
|
|
|
// SetCommand updates the display command for a game.
|
|
func (s *ScreenService) SetCommand(ctx context.Context, gameID int, command int32, position int64, volume int32) error {
|
|
return s.repo.SetCommand(ctx, gameID, command, position, volume)
|
|
}
|
|
|
|
// Bind attaches a screen session to a game.
|
|
func (s *ScreenService) Bind(ctx context.Context, id, gameID int, tokenHash string, initializeAudio bool) (bool, error) {
|
|
isNewBinding, err := s.repo.BindWithAudio(ctx, id, gameID, tokenHash, initializeAudio)
|
|
return isNewBinding, mapError(err)
|
|
}
|
|
|
|
// Unbind detaches a screen session from its game.
|
|
func (s *ScreenService) Unbind(ctx context.Context, id int) error {
|
|
return mapError(s.repo.Unbind(ctx, id))
|
|
}
|
|
|
|
// SetSound enables or disables sound for a screen session.
|
|
func (s *ScreenService) SetSound(ctx context.Context, id int, enabled bool) error {
|
|
return mapError(s.repo.SetSound(ctx, id, enabled))
|
|
}
|
|
|
|
func mapError(err error) error {
|
|
if errors.Is(err, screen_sessions_repo.ErrScreenNotFound) {
|
|
return ErrScreenNotFound
|
|
}
|
|
return err
|
|
}
|
|
|
|
// InitializeScreen joins the current display without resetting other screens.
|
|
func (s *ScreenService) InitializeScreen(ctx context.Context, id, gameID int, mode int32, text, label string, duration int64, qrURL string, timerType int32) error {
|
|
// Non-timer steps have no duration, but every state includes a valid timer.
|
|
if mode != 7 && duration == 0 {
|
|
duration = 600
|
|
}
|
|
if label == "" {
|
|
label = TimerLabel(timerType)
|
|
}
|
|
return s.repo.InitializeScreen(ctx, id, gameID, mode, text, label, duration, qrURL, timerType)
|
|
}
|
|
|
|
// ValidQR validates a URL and caption without fetching the URL.
|
|
func ValidQR(link, text string) bool {
|
|
u, err := url.Parse(link)
|
|
return err == nil && (u.Scheme == "https" || u.Scheme == "http") && u.Hostname() != "" && u.User == nil && len(link) <= 2048 && len(text) <= 10000 && strings.TrimSpace(link) == link
|
|
}
|
|
|
|
// ValidTimerType reports whether the timer category is supported.
|
|
func ValidTimerType(kind int32) bool { return kind >= 0 && kind <= 2 }
|
|
|
|
// TimerLabel returns the initial caption for a timer category.
|
|
func TimerLabel(kind int32) string {
|
|
switch kind {
|
|
case 1:
|
|
return "До начала игры"
|
|
case 2:
|
|
return "До конца игры"
|
|
default:
|
|
return "Перерыв"
|
|
}
|
|
}
|
|
|
|
// SetQR updates the QR screen for a game.
|
|
func (s *ScreenService) SetQR(ctx context.Context, gameID int, link, text string) error {
|
|
if !ValidQR(link, text) {
|
|
return ErrInvalidMode
|
|
}
|
|
return s.repo.SetQR(ctx, gameID, link, text)
|
|
}
|