Skip to content

Commit

Permalink
Feature/issue 74 user retweet lookup (#76)
Browse files Browse the repository at this point in the history
* implemntation of the retweeted by API

* added the user retweet lookup example

* added the reference in the readme
  • Loading branch information
g8rswimmer authored Jan 3, 2022
1 parent a1a4f6c commit c05159f
Show file tree
Hide file tree
Showing 8 changed files with 451 additions and 16 deletions.
1 change: 1 addition & 0 deletions v2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Here are the current twitter `v2` API features supported:
* [Manage Retweet](https://developer.twitter.com/en/docs/twitter-api/tweets/retweets/introduction)
* [retweet example](./_examples/user-retweet)
* [delete retweet example](./_examples/user-delete-retweet)
* [retweet lookup example](./_examples/user-retweet-lookup)

## Examples
Much like `v1`, there is an `_example` directory to demostrate library usage. Refer to the [readme](./_examples) for more information.
Expand Down
7 changes: 7 additions & 0 deletions v2/_examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,10 @@ This [example](./user-delete-retweet) demonstrates the deleting user retweet API
```
go run *.go -token=YOUR_API_TOKEN -user_id=2244994945 -tweet_id=1228393702244134912
```

## User Retweet Lookup
This [example](./user-retweet-lookup) demonstrates looking up user that have retweeted a tweet API call.

```
go run *.go -token=YOUR_API_TOKEN -tweet_id=1228393702244134912
```
53 changes: 53 additions & 0 deletions v2/_examples/user-retweet-lookup/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package main

import (
"context"
"encoding/json"
"flag"
"fmt"
"log"
"net/http"

twitter "github.com/g8rswimmer/go-twitter/v2"
)

type authorize struct {
Token string
}

func (a authorize) Add(req *http.Request) {
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", a.Token))
}

/**
In order to run, the user will need to provide the bearer token and the list of user ids.
**/
func main() {
token := flag.String("token", "", "twitter API token")
tweetID := flag.String("tweet_id", "", "tweet id")
flag.Parse()

client := &twitter.Client{
Authorizer: authorize{
Token: *token,
},
Client: http.DefaultClient,
Host: "https://api.twitter.com",
}
opts := twitter.UserRetweetLookuoOpts{
Expansions: []twitter.Expansion{twitter.ExpansionPinnedTweetID},
}

fmt.Println("Callout to user lookup callout")

userResponse, err := client.UserRetweetLookup(context.Background(), *tweetID, opts)
if err != nil {
log.Panicf("user lookup error: %v", err)
}

enc, err := json.MarshalIndent(userResponse, "", " ")
if err != nil {
log.Panic(err)
}
fmt.Println(string(enc))
}
52 changes: 52 additions & 0 deletions v2/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,58 @@ func (c *Client) UserLookup(ctx context.Context, ids []string, opts UserLookupOp
}, nil
}

// UserRetweetLookup allows you to get information about users that have retweeted a tweet
func (c *Client) UserRetweetLookup(ctx context.Context, tweetID string, opts UserRetweetLookuoOpts) (*UserRetweetLookupResponse, error) {
switch {
case len(tweetID) == 0:
return nil, fmt.Errorf("user retweet lookup: an id is required: %w", ErrParameter)
default:
}

ep := userRetweetLookupEndpoint.urlID(c.Host, tweetID)

req, err := http.NewRequestWithContext(ctx, http.MethodGet, ep, nil)
if err != nil {
return nil, fmt.Errorf("user retweet lookup request: %w", err)
}
req.Header.Add("Accept", "application/json")
c.Authorizer.Add(req)
opts.addQuery(req)

resp, err := c.Client.Do(req)
if err != nil {
return nil, fmt.Errorf("user retweet lookup response: %w", err)
}
defer resp.Body.Close()

decoder := json.NewDecoder(resp.Body)

if resp.StatusCode != http.StatusOK {
e := &ErrorResponse{}
if err := decoder.Decode(e); err != nil {
return nil, &HTTPError{
Status: resp.Status,
StatusCode: resp.StatusCode,
URL: resp.Request.URL.String(),
}
}
e.StatusCode = resp.StatusCode
return nil, e
}

raw := struct {
*UserRetweetRaw
Meta *UserRetweetMeta `json:"meta"`
}{}
if err := decoder.Decode(&raw); err != nil {
return nil, fmt.Errorf("user retweet lookup dictionary: %w", err)
}
return &UserRetweetLookupResponse{
Raw: raw.UserRetweetRaw,
Meta: raw.Meta,
}, nil
}

// UserNameLookup returns information about an user or group of users specified by a group of usernames.
func (c *Client) UserNameLookup(ctx context.Context, usernames []string, opts UserLookupOpts) (*UserLookupResponse, error) {
ep := userNameLookupEndpoint.url(c.Host)
Expand Down
258 changes: 258 additions & 0 deletions v2/client_retweet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,3 +187,261 @@ func TestClient_DeleteUserRetweet(t *testing.T) {
})
}
}

func TestClient_UserRetweetLookup(t *testing.T) {
type fields struct {
Authorizer Authorizer
Client *http.Client
Host string
}
type args struct {
tweetID string
opts UserRetweetLookuoOpts
}
tests := []struct {
name string
fields fields
args args
want *UserRetweetLookupResponse
wantErr bool
}{
{
name: "Success - no options",
fields: fields{
Authorizer: &mockAuth{},
Host: "https://www.test.com",
Client: mockHTTPClient(func(req *http.Request) *http.Response {
if req.Method != http.MethodGet {
log.Panicf("the method is not correct %s %s", req.Method, http.MethodGet)
}
if strings.Contains(req.URL.String(), userRetweetLookupEndpoint.urlID("", "tweet-1234")) == false {
log.Panicf("the url is not correct %s %s", req.URL.String(), userRetweetLookupEndpoint)
}
body := `{
"data": [
{
"id": "1065249714214457345",
"name": "Spaces",
"username": "TwitterSpaces"
},
{
"id": "783214",
"name": "Twitter",
"username": "Twitter"
},
{
"id": "1526228120",
"name": "Twitter Data",
"username": "TwitterData"
}
],
"meta": {
"result_count": 3
}
}`
return &http.Response{
StatusCode: http.StatusOK,
Body: ioutil.NopCloser(strings.NewReader(body)),
}
}),
},
args: args{
tweetID: "tweet-1234",
},
want: &UserRetweetLookupResponse{
Raw: &UserRetweetRaw{
Users: []*UserObj{
{
ID: "1065249714214457345",
Name: "Spaces",
UserName: "TwitterSpaces",
},
{
ID: "783214",
Name: "Twitter",
UserName: "Twitter",
},
{
ID: "1526228120",
Name: "Twitter Data",
UserName: "TwitterData",
},
},
},
Meta: &UserRetweetMeta{
ResultCount: 3,
},
},
wantErr: false,
},
{
name: "Success - with options",
fields: fields{
Authorizer: &mockAuth{},
Host: "https://www.test.com",
Client: mockHTTPClient(func(req *http.Request) *http.Response {
if req.Method != http.MethodGet {
log.Panicf("the method is not correct %s %s", req.Method, http.MethodGet)
}
if strings.Contains(req.URL.String(), userRetweetLookupEndpoint.urlID("", "tweet-1234")) == false {
log.Panicf("the url is not correct %s %s", req.URL.String(), userRetweetLookupEndpoint)
}
body := `{
"data": [
{
"id": "1065249714214457345",
"created_at": "2018-11-21T14:24:58.000Z",
"name": "Spaces",
"pinned_tweet_id": "1389270063807598594",
"description": "Twitter Spaces is where live audio conversations happen.",
"username": "TwitterSpaces"
},
{
"id": "783214",
"created_at": "2007-02-20T14:35:54.000Z",
"name": "Twitter",
"description": "What's happening?!",
"username": "Twitter"
},
{
"id": "1526228120",
"created_at": "2013-06-17T23:57:45.000Z",
"name": "Twitter Data",
"description": "Data-driven insights about notable moments and conversations from Twitter, Inc., plus tips and tricks to help you get the most out of Twitter data.",
"username": "TwitterData"
},
{
"id": "2244994945",
"created_at": "2013-12-14T04:35:55.000Z",
"name": "Twitter Dev",
"pinned_tweet_id": "1354143047324299264",
"description": "The voice of the #TwitterDev team and your official source for updates, news, and events, related to the #TwitterAPI.",
"username": "TwitterDev"
},
{
"id": "6253282",
"created_at": "2007-05-23T06:01:13.000Z",
"name": "Twitter API",
"pinned_tweet_id": "1293595870563381249",
"description": "Tweets about changes and service issues. Follow @TwitterDev for more.",
"username": "TwitterAPI"
}
],
"includes": {
"tweets": [
{
"id": "1389270063807598594",
"text": "now, everyone with 600 or more followers can host a Space.nnbased on what we've learned, these accounts are likely to have a good experience hosting because of their existing audience. before bringing the ability to create a Space to everyone, we're focused on a few things. :thread:"
},
{
"id": "1354143047324299264",
"text": "Academics are one of the biggest groups using the #TwitterAPI to research what's happening. Their work helps make the world (& Twitter) a better place, and now more than ever, we must enable more of it. nIntroducing :drum_with_drumsticks: the Academic Research product track!nhttps://t.co/nOFiGewAV2"
},
{
"id": "1293595870563381249",
"text": "Twitter API v2: Early Access releasednnToday we announced Early Access to the first endpoints of the new Twitter API!nn#TwitterAPI #EarlyAccess #VersionBump https://t.co/g7v3aeIbtQ"
}
]
},
"meta": {
"result_count": 5
}
}`
return &http.Response{
StatusCode: http.StatusOK,
Body: ioutil.NopCloser(strings.NewReader(body)),
}
}),
},
args: args{
tweetID: "tweet-1234",
opts: UserRetweetLookuoOpts{
Expansions: []Expansion{ExpansionPinnedTweetID},
UserFields: []UserField{UserFieldCreatedAt, UserFieldDescription},
TweetFields: []TweetField{TweetFieldCreatedAt},
},
},
want: &UserRetweetLookupResponse{
Raw: &UserRetweetRaw{
Users: []*UserObj{
{
ID: "1065249714214457345",
Name: "Spaces",
UserName: "TwitterSpaces",
CreatedAt: "2018-11-21T14:24:58.000Z",
PinnedTweetID: "1389270063807598594",
Description: "Twitter Spaces is where live audio conversations happen.",
},
{
ID: "783214",
Name: "Twitter",
UserName: "Twitter",
CreatedAt: "2007-02-20T14:35:54.000Z",
Description: "What's happening?!",
},
{
ID: "1526228120",
Name: "Twitter Data",
UserName: "TwitterData",
CreatedAt: "2013-06-17T23:57:45.000Z",
Description: "Data-driven insights about notable moments and conversations from Twitter, Inc., plus tips and tricks to help you get the most out of Twitter data.",
},
{
ID: "2244994945",
Name: "Twitter Dev",
UserName: "TwitterDev",
CreatedAt: "2013-12-14T04:35:55.000Z",
PinnedTweetID: "1354143047324299264",
Description: "The voice of the #TwitterDev team and your official source for updates, news, and events, related to the #TwitterAPI.",
},
{
ID: "6253282",
Name: "Twitter API",
UserName: "TwitterAPI",
CreatedAt: "2007-05-23T06:01:13.000Z",
PinnedTweetID: "1293595870563381249",
Description: "Tweets about changes and service issues. Follow @TwitterDev for more.",
},
},
Includes: &UserRetweetRawIncludes{
Tweets: []*TweetObj{
{
ID: "1389270063807598594",
Text: "now, everyone with 600 or more followers can host a Space.nnbased on what we've learned, these accounts are likely to have a good experience hosting because of their existing audience. before bringing the ability to create a Space to everyone, we're focused on a few things. :thread:",
},
{
ID: "1354143047324299264",
Text: "Academics are one of the biggest groups using the #TwitterAPI to research what's happening. Their work helps make the world (& Twitter) a better place, and now more than ever, we must enable more of it. nIntroducing :drum_with_drumsticks: the Academic Research product track!nhttps://t.co/nOFiGewAV2",
},
{
ID: "1293595870563381249",
Text: "Twitter API v2: Early Access releasednnToday we announced Early Access to the first endpoints of the new Twitter API!nn#TwitterAPI #EarlyAccess #VersionBump https://t.co/g7v3aeIbtQ",
},
},
},
},
Meta: &UserRetweetMeta{
ResultCount: 5,
},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &Client{
Authorizer: tt.fields.Authorizer,
Client: tt.fields.Client,
Host: tt.fields.Host,
}
got, err := c.UserRetweetLookup(context.Background(), tt.args.tweetID, tt.args.opts)
if (err != nil) != tt.wantErr {
t.Errorf("Client.UserRetweetLookup() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Client.UserRetweetLookup() = %v, want %v", got, tt.want)
}
})
}
}
Loading

0 comments on commit c05159f

Please sign in to comment.