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 }