github.com/wtfutil/wtf@v0.43.0/modules/twitterstats/client.go (about)

     1  package twitterstats
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"log"
     9  	"net/http"
    10  
    11  	"golang.org/x/oauth2"
    12  	"golang.org/x/oauth2/clientcredentials"
    13  )
    14  
    15  // Client contains state that allows stats to be fetched about a list of Twitter users
    16  type Client struct {
    17  	httpClient  *http.Client
    18  	screenNames []string
    19  }
    20  
    21  // TwitterStats Represents a stats snapshot for a single Twitter user at a point in time
    22  type TwitterStats struct {
    23  	FollowerCount int64 `json:"followers_count"`
    24  	TweetCount    int64 `json:"statuses_count"`
    25  }
    26  
    27  const (
    28  	userTimelineURL = "https://api.twitter.com/1.1/users/show.json"
    29  )
    30  
    31  // NewClient creates a new twitterstats client that contains an OAuth2 HTTP client which can be used
    32  func NewClient(settings *Settings) *Client {
    33  	usernames := make([]string, len(settings.screenNames))
    34  	for i, username := range settings.screenNames {
    35  		var ok bool
    36  		if usernames[i], ok = username.(string); !ok {
    37  			log.Fatalf("All `screenName`s in twitterstats config must be of type string")
    38  		}
    39  	}
    40  
    41  	var httpClient *http.Client
    42  	// If a bearer token is supplied, use that directly.  Otherwise, let the Oauth client fetch a token
    43  	// using the consumer key and secret.
    44  	if settings.bearerToken == "" {
    45  		conf := &clientcredentials.Config{
    46  			ClientID:     settings.consumerKey,
    47  			ClientSecret: settings.consumerSecret,
    48  			TokenURL:     "https://api.twitter.com/oauth2/token",
    49  		}
    50  		httpClient = conf.Client(context.Background())
    51  	} else {
    52  		ctx := context.Background()
    53  		httpClient = oauth2.NewClient(ctx, oauth2.StaticTokenSource(&oauth2.Token{
    54  			AccessToken: settings.bearerToken,
    55  			TokenType:   "Bearer",
    56  		}))
    57  	}
    58  
    59  	client := Client{
    60  		httpClient:  httpClient,
    61  		screenNames: usernames,
    62  	}
    63  
    64  	return &client
    65  }
    66  
    67  // GetStatsForUser Fetches stats for a single user.  If there is an error fetching or parsing the response
    68  // from the Twitter API, an empty stats struct will be returned.
    69  func (client *Client) GetStatsForUser(username string) TwitterStats {
    70  	stats := TwitterStats{
    71  		FollowerCount: 0,
    72  		TweetCount:    0,
    73  	}
    74  
    75  	url := fmt.Sprintf("%s?screen_name=%s", userTimelineURL, username)
    76  	resp, err := client.httpClient.Get(url)
    77  	if err != nil {
    78  		return stats
    79  	}
    80  	defer func() { _ = resp.Body.Close() }()
    81  
    82  	body, err := io.ReadAll(resp.Body)
    83  	if err != nil {
    84  		return stats
    85  	}
    86  
    87  	// If there is an error while parsing, just discard the error and return the empty stats
    88  	err = json.Unmarshal(body, &stats)
    89  	if err != nil {
    90  		return stats
    91  	}
    92  
    93  	return stats
    94  }
    95  
    96  // GetStats Returns a slice of `TwitterStats` structs for each username in `client.screenNames` in the same
    97  // order of `client.screenNames`
    98  func (client *Client) GetStats() []TwitterStats {
    99  	stats := make([]TwitterStats, len(client.screenNames))
   100  
   101  	for i, username := range client.screenNames {
   102  		stats[i] = client.GetStatsForUser(username)
   103  	}
   104  
   105  	return stats
   106  }