package games import ( "bytes" "encoding/json" "fmt" "hugo-medialog/utils" "io/ioutil" "net/http" "net/url" "os" "path/filepath" "strings" "time" ) type TokenResponse struct { AccessToken string `json:"access_token"` ExpiresIn int `json:"expires_in"` TokenType string `json:"token_type"` } func getAccessToken() (string, error) { clientID := os.Getenv("TWITCH_CLIENT_ID") clientSecret := os.Getenv("TWITCH_SECRET_ID") authURL := "https://id.twitch.tv/oauth2/token" data := url.Values{} data.Set("client_id", clientID) data.Set("client_secret", clientSecret) data.Set("grant_type", "client_credentials") req, err := http.NewRequest("POST", authURL, strings.NewReader(data.Encode())) if err != nil { return "", err } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") client := &http.Client{Timeout: 10 * time.Second} resp, err := client.Do(req) if err != nil { return "", err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return "", fmt.Errorf("error: non-200 response code: %d", resp.StatusCode) } var tokenResp TokenResponse if err := json.NewDecoder(resp.Body).Decode(&tokenResp); err != nil { return "", err } return tokenResp.AccessToken, nil } func SearchGameByTitle(title string, game *Game) error { clientID := os.Getenv("TWITCH_CLIENT_ID") accessToken, err := getAccessToken() if err != nil { fmt.Printf("Error getting access token: %v\n", err) return err } url := "https://api.igdb.com/v4/games" query := fmt.Sprintf("search \"%s\";\nfields id,name,slug,cover,artworks,first_release_date,genres,platforms,screenshots,slug,url;\n", title) req, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte(query))) if err != nil { return err } req.Header.Set("Client-ID", clientID) req.Header.Set("Authorization", "Bearer "+accessToken) req.Header.Set("Content-Type", "text/plain") client := &http.Client{Timeout: 10 * time.Second} resp, err := client.Do(req) if err != nil { return err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return err } var games []map[string]interface{} if err := json.Unmarshal(body, &games); err != nil { return err } if len(games) == 0 { return fmt.Errorf("No games found") } first_game := games[0] // Timestamp if timestamp, ok := first_game["first_release_date"].(float64); ok { game.Year = time.Unix(int64(timestamp), 0).Format("2006") game.Subtitle = game.Year } // Cover coverID := first_game["cover"].(float64) coverURL, err := makeRequest("covers", coverID) if err != nil { fmt.Printf("Err: %s", err) } game.Image = coverURL // Artwork if artworks, ok := first_game["artworks"].([]interface{}); ok && len(artworks) > 0 { artworkURL, err := makeRequest("artworks", artworks[0].(float64)) if err != nil { fmt.Printf("Err: %s", err) } game.Poster = artworkURL } // Screenshots if screenshots, ok := first_game["screenshots"].([]interface{}); ok && len(screenshots) > 0 { artworkURL, err := makeRequest("screenshots", screenshots[0].(float64)) if err != nil { fmt.Printf("Err: %s", err) } game.Background = artworkURL } game.ID = int(first_game["id"].(float64)) game.Link = first_game["url"].(string) return nil } func getImageURL(imageID int, size string) string { return fmt.Sprintf("https://images.igdb.com/igdb/image/upload/t_%s/%d.jpg", size, imageID) } // Function to make requests to IGDB func makeRequest(endpoint string, id float64) (string, error) { clientID := os.Getenv("TWITCH_CLIENT_ID") accessToken, err := getAccessToken() if err != nil { fmt.Printf("Error getting access token: %v\n", err) return "", err } // Create the request body idStr := fmt.Sprint(id) body := fmt.Sprintf("fields id, image_id; where id = (%s);", idStr) req, err := http.NewRequest("POST", "https://api.igdb.com/v4/"+endpoint, strings.NewReader(body)) if err != nil { return "", err } // Add headers req.Header.Add("Client-ID", clientID) req.Header.Add("Authorization", "Bearer "+accessToken) req.Header.Add("Content-Type", "application/x-www-form-urlencoded") // Send the request client := &http.Client{} resp, err := client.Do(req) if err != nil { return "", err } defer resp.Body.Close() // Read the response responseData, err := ioutil.ReadAll(resp.Body) if err != nil { return "", err } // Parse the response var coverResponse []map[string]interface{} err = json.Unmarshal(responseData, &coverResponse) if err != nil { return "", err } // Get the image_id and construct the URL if len(coverResponse) > 0 { imageID := coverResponse[0]["image_id"].(string) coverURL := fmt.Sprintf("https://images.igdb.com/igdb/image/upload/t_original/%s.jpg", imageID) return coverURL, nil } return "", fmt.Errorf("no image found for ID %d", id) } func DownloadImage(url, slug, suffix string) error { imageDir := filepath.Join(os.Getenv("MARKDOWN_OUTPUT_GAMES_DIR"), os.Getenv("IMAGES_OUTPUT_DIR")) if err := utils.CreateDirIfNotExists(imageDir); err != nil { return err } filename := fmt.Sprintf("%s%s.jpg", slug, suffix) filePath := filepath.Join(imageDir, filename) resp, err := http.Get(url) if err != nil { return fmt.Errorf("error downloading image: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return fmt.Errorf("non-200 response while downloading image: %d", resp.StatusCode) } file, err := os.Create(filePath) if err != nil { return fmt.Errorf("error creating image file: %w", err) } defer file.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return fmt.Errorf("error reading image data: %w", err) } _, err = file.Write(body) if err != nil { return fmt.Errorf("error writing image data: %w", err) } fmt.Printf(" - Image saved successfully at: %s\n", filePath) return nil }