Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/issue 74 user retweet lookup #76

Merged
merged 3 commits into from
Jan 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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