package twitch import ( "bytes" "context" "encoding/json" "fmt" "net/http" "time" "velox-bot/internal/db/repos/twitchrepo" ) type Service struct { repo *twitchrepo.Repo client *http.Client twitchClient string } func New(repo *twitchrepo.Repo, twitchClientID string) *Service { return &Service{ repo: repo, client: &http.Client{ Timeout: 5 * time.Second, }, twitchClient: twitchClientID, } } func (s *Service) ListGuilds(ctx context.Context) ([]int64, error) { return s.repo.ListGuilds(ctx) } func (s *Service) ListUsersForGuild(ctx context.Context, guildID int64) ([]string, error) { return s.repo.ListUsersForGuild(ctx, guildID) } func (s *Service) GetStatus(ctx context.Context, guildID int64, username string) (string, bool, error) { return s.repo.GetStatus(ctx, guildID, username) } func (s *Service) UpdateStatus(ctx context.Context, guildID int64, username, status string) error { return s.repo.UpdateStatus(ctx, guildID, username, status) } func (s *Service) UpsertStreamer(ctx context.Context, guildID int64, username string) error { return s.repo.UpsertStreamer(ctx, guildID, username) } func (s *Service) RemoveStreamer(ctx context.Context, guildID int64, username string) error { return s.repo.RemoveStreamer(ctx, guildID, username) } func (s *Service) GetNotificationChannel(ctx context.Context, guildID int64) (int64, bool, error) { return s.repo.GetNotificationChannel(ctx, guildID) } func (s *Service) SetNotificationChannel(ctx context.Context, guildID, channelID int64) error { return s.repo.SetNotificationChannel(ctx, guildID, channelID) } func (s *Service) IsUserStreaming(ctx context.Context, username string) (bool, error) { type gqlRequest struct { Query string `json:"query"` Variables map[string]any `json:"variables"` } type gqlResponse struct { Data struct { User *struct { Stream *struct { ID string `json:"id"` } `json:"stream"` } `json:"user"` } `json:"data"` } q := fmt.Sprintf(`query { user(login: "%s") { stream { id } } }`, username) body, err := json.Marshal(gqlRequest{ Query: q, Variables: map[string]any{}, }) if err != nil { return false, err } req, err := http.NewRequestWithContext(ctx, http.MethodPost, "https://gql.twitch.tv/gql", bytes.NewReader(body)) if err != nil { return false, err } req.Header.Set("Content-Type", "application/json") req.Header.Set("Client-Id", s.twitchClient) resp, err := s.client.Do(req) if err != nil { return false, err } defer resp.Body.Close() var respBody gqlResponse if err := json.NewDecoder(resp.Body).Decode(&respBody); err != nil { return false, err } if respBody.Data.User == nil { return false, nil } return respBody.Data.User.Stream != nil, nil }