feat: twitch notifications

This commit is contained in:
2026-03-18 04:42:02 +00:00
parent 076e616b9c
commit df656ca12d
10 changed files with 607 additions and 12 deletions
+6 -3
View File
@@ -4,10 +4,11 @@ import (
"velox-bot/internal/db/services/level"
"velox-bot/internal/db/services/levelsettings"
"velox-bot/internal/db/services/meeting"
"velox-bot/internal/db/services/schedule"
"velox-bot/internal/db/services/usersettings"
"velox-bot/internal/db/services/projects"
"velox-bot/internal/db/services/rps"
"velox-bot/internal/db/services/schedule"
"velox-bot/internal/db/services/twitch"
"velox-bot/internal/db/services/usersettings"
)
type Services struct {
@@ -18,11 +19,12 @@ type Services struct {
UserSettings *usersettings.Service
Projects *projects.Service
RPS *rps.Service
Twitch *twitch.Service
}
var Global *Services
func NewServices(level *level.Service, levelSettings *levelsettings.Service, meeting *meeting.Service, schedule *schedule.Service, userSettings *usersettings.Service, projects *projects.Service, rps *rps.Service) *Services {
func NewServices(level *level.Service, levelSettings *levelsettings.Service, meeting *meeting.Service, schedule *schedule.Service, userSettings *usersettings.Service, projects *projects.Service, rps *rps.Service, twitchSvc *twitch.Service) *Services {
s := &Services{
Level: level,
LevelSettings: levelSettings,
@@ -31,6 +33,7 @@ func NewServices(level *level.Service, levelSettings *levelsettings.Service, mee
UserSettings: userSettings,
Projects: projects,
RPS: rps,
Twitch: twitchSvc,
}
Global = s
return s
+109
View File
@@ -0,0 +1,109 @@
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
}