package twitch import ( "fmt" "time" "github.com/nicklaw5/helix/v2" "git.sr.ht/~ashkeel/strimertul/database" ) func GetConfig(db database.Database) (Config, error) { var config Config if err := db.GetJSON(ConfigKey, &config); err != nil { return Config{}, fmt.Errorf("failed to get twitch config: %w", err) } return config, nil } type AuthResponse struct { AccessToken string `json:"access_token"` RefreshToken string `json:"refresh_token"` ExpiresIn int `json:"expires_in"` Scope []string `json:"scope"` Time time.Time } func GetUserClient(db database.Database, forceRefresh bool) (*helix.Client, error) { var authResp AuthResponse if err := db.GetJSON(AuthKey, &authResp); err != nil { return nil, err } // Handle token expiration if forceRefresh || time.Now().After(authResp.Time.Add(time.Duration(authResp.ExpiresIn)*time.Second)) { // Refresh tokens api, err := GetHelixAPI(db) if err != nil { return nil, err } refreshed, err := api.RefreshUserAccessToken(authResp.RefreshToken) if err != nil { return nil, err } authResp.AccessToken = refreshed.Data.AccessToken authResp.RefreshToken = refreshed.Data.RefreshToken authResp.Time = time.Now().Add(time.Duration(refreshed.Data.ExpiresIn) * time.Second) // Save new token pair err = db.PutJSON(AuthKey, authResp) if err != nil { return nil, err } } config, err := GetConfig(db) if err != nil { return nil, err } return helix.NewClient(&helix.Options{ ClientID: config.APIClientID, ClientSecret: config.APIClientSecret, UserAccessToken: authResp.AccessToken, }) } func GetHelixAPI(db database.Database) (*helix.Client, error) { config, err := GetConfig(db) if err != nil { return nil, err } baseurl, err := baseURL(db) if err != nil { return nil, err } redirectURI := getRedirectURI(baseurl) // Create Twitch client api, err := helix.NewClient(&helix.Options{ ClientID: config.APIClientID, ClientSecret: config.APIClientSecret, RedirectURI: redirectURI, }) if err != nil { return nil, err } // Get access token resp, err := api.RequestAppAccessToken([]string{"user:read:email"}) if err != nil { return nil, err } // Set the access token on the client api.SetAppAccessToken(resp.Data.AccessToken) return api, nil } func baseURL(db database.Database) (string, error) { var severConfig struct { Bind string `json:"bind"` } err := db.GetJSON("http/config", &severConfig) return severConfig.Bind, err } func getRedirectURI(baseurl string) string { return fmt.Sprintf("http://%s/twitch/callback", baseurl) }