package tiktok import ( "encoding/json" "fmt" "net/http" "time" "git.linuxforward.com/byom/byom-trends/config" "git.linuxforward.com/byom/byom-trends/logger" "github.com/sirupsen/logrus" ) type Client struct { httpClient *http.Client apiKey string baseURL string logger *logrus.Entry } type ProfileStats struct { Username string `json:"username"` FollowerCount int64 `json:"follower_count"` FollowingCount int64 `json:"following_count"` VideoCount int64 `json:"video_count"` LikeCount int64 `json:"like_count"` Engagement float64 `json:"engagement"` VerifiedStatus bool `json:"verified_status"` } type Video struct { ID string `json:"id"` Description string `json:"description"` CreateTime time.Time `json:"create_time"` Duration int `json:"duration"` LikeCount int64 `json:"like_count"` CommentCount int64 `json:"comment_count"` ShareCount int64 `json:"share_count"` ViewCount int64 `json:"view_count"` ThumbnailURL string `json:"thumbnail_url"` VideoURL string `json:"video_url"` MusicInfo string `json:"music_info"` HashTags []string `json:"hashtags"` } func NewClient(cfg *config.TikTokConfig) *Client { return &Client{ httpClient: &http.Client{ Timeout: time.Second * 10, }, apiKey: cfg.APIKey, baseURL: cfg.BaseURL, logger: logger.NewLogger("tiktok-client"), } } func (c *Client) GetProfileStats(username string) (*ProfileStats, error) { url := fmt.Sprintf("%s/users/%s", c.baseURL, username) req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, fmt.Errorf("create request: %w", err) } req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.apiKey)) resp, err := c.httpClient.Do(req) if err != nil { return nil, fmt.Errorf("do request: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode) } var stats ProfileStats if err := json.NewDecoder(resp.Body).Decode(&stats); err != nil { return nil, fmt.Errorf("decode response: %w", err) } return &stats, nil } func (c *Client) GetRecentVideos(username string, limit int) ([]Video, error) { url := fmt.Sprintf("%s/users/%s/videos?limit=%d", c.baseURL, username, limit) req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, fmt.Errorf("create request: %w", err) } req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.apiKey)) resp, err := c.httpClient.Do(req) if err != nil { return nil, fmt.Errorf("do request: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode) } var videos []Video if err := json.NewDecoder(resp.Body).Decode(&videos); err != nil { return nil, fmt.Errorf("decode response: %w", err) } return videos, nil } func (c *Client) CalculateEngagement(videos []Video) float64 { if len(videos) == 0 { return 0 } var totalEngagement int64 for _, video := range videos { // TikTok engagement includes likes, comments, shares, and views totalEngagement += video.LikeCount + video.CommentCount + video.ShareCount + video.ViewCount } return float64(totalEngagement) / float64(len(videos)) } func (c *Client) GetTrendingHashtags(videos []Video) map[string]int { hashtags := make(map[string]int) for _, video := range videos { for _, tag := range video.HashTags { hashtags[tag]++ } } return hashtags }