Skip to content

Commit 7ddacc3

Browse files
committed
feat: add v2 user endpoints and types (#16)
Squashed commit of the following: commit 727d7cf Author: Paul-Louis Hery <[email protected]> Date: Sun Dec 13 01:06:17 2020 +0100 Add some examples to the readme commit c5ea1bf Author: Paul-Louis Hery <[email protected]> Date: Sun Dec 13 00:52:19 2020 +0100 feat: add v2 user endpoints and types
1 parent 6f63978 commit 7ddacc3

File tree

4 files changed

+122
-15
lines changed

4 files changed

+122
-15
lines changed

README.md

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,22 @@ const { usertoken, userSecret } = twitterClient.login('<THE_OAUTH_VERIFIER>');
3131
// Tell typescript it's a readonly app
3232
const twitterClient = new TwitterApi(xxx).readOnly;
3333

34-
// Manually call the API
35-
const tweets = await twitterClient.v2.get('tweets/search/recent', {query: 'nodeJS', max_results: '100'});
36-
const tweets = await twitterClient.v1.tweet('Hello, this is a test.'),
37-
const tweets = await twitterClient.v1.uploadMedia(await fs.promises.readFile(path), { type: 'jpg' })
34+
// Play with the built in methods
35+
36+
await twitterClient.v2.userByUsername('plhery').data.id;
37+
await twitterClient.v1.tweet('Hello, this is a test.');
38+
await twitterClient.v1.uploadMedia(await fs.promises.readFile(path), { type: 'jpg' })
39+
40+
// the search utils
41+
42+
const results = await twitterClient.v2.search('hello');
43+
console.log(results.tweets); // 10 tweets
44+
await results.fetchNext(100);
45+
await results.fetchNext(100);
46+
console.log(results.tweets, results.rateLimit); // 210 tweets
47+
48+
// Or manually call the API
49+
await twitterClient.v2.get('tweets/search/recent', {query: 'nodeJS', max_results: '100'});
3850
const tweets = await twitterClient.get('https://api.twitter.com/2/tweets/search/recent?query=nodeJS&max_results=100');
3951
```
4052

@@ -59,7 +71,7 @@ They caused me some frustration:
5971
- [x] read/write/DM aware typing
6072
- [x] get/post methods
6173
- [ ] Twitter API V2 tweets methods
62-
- [ ] Twitter API V2 users methods
74+
- [x] Twitter API V2 users methods
6375
- [ ] Auto pagination
6476
- [ ] Error code enums
6577

src/client.subclient.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@ export default abstract class TwitterApiSubClient extends TwitterApiBase {
2323
this._oauth = inst._oauth;
2424
}
2525

26-
public async get<T = any>(url: string, full_response?: boolean) : Promise<T | TwitterResponse<T>>;
27-
public async get<T = any>(url: string, parameters?: Record<string, string | number | undefined>, full_response?: false, prefix?: string) : Promise<T>;
28-
public async get<T = any>(url: string, parameters?: Record<string, string | number | undefined>, full_response?: true, prefix?: string) : Promise<TwitterResponse<T>>;
26+
public async get<T = any>(url: string, full_response?: false) : Promise<T>;
27+
public async get<T = any>(url: string, full_response: true) : Promise<TwitterResponse<T>>;
28+
public async get<T = any>(url: string, parameters: Record<string, string | number | undefined>, full_response?: false, prefix?: string) : Promise<T>;
29+
public async get<T = any>(url: string, parameters: Record<string, string | number | undefined>, full_response: true, prefix?: string) : Promise<TwitterResponse<T>>;
2930

30-
public async get<T = any>(url: string, parameters?: Record<string, string | number | undefined> | boolean, full_response = false, prefix = this._prefix) : Promise<T | TwitterResponse<T>> {
31+
public async get<T = any>(url: string, parameters?: Record<string, string | number | undefined> | boolean, full_response = false, prefix = this._prefix) : Promise<T | TwitterResponse<T>> {
3132
if (typeof parameters === 'boolean') {
3233
full_response = parameters;
3334
parameters = undefined;
@@ -38,9 +39,10 @@ export default abstract class TwitterApiSubClient extends TwitterApiBase {
3839
}
3940

4041

41-
public async delete<T = any>(url: string, full_response?: boolean) : Promise<T | TwitterResponse<T>>;
42-
public async delete<T = any>(url: string, parameters?: Record<string, string | number | undefined>, full_response?: false, prefix?: string) : Promise<T>;
43-
public async delete<T = any>(url: string, parameters?: Record<string, string | number | undefined>, full_response?: true, prefix?: string) : Promise<TwitterResponse<T>>;
42+
public async delete<T = any>(url: string, full_response?: false) : Promise<T>;
43+
public async delete<T = any>(url: string, full_response: true) : Promise<TwitterResponse<T>>;
44+
public async delete<T = any>(url: string, parameters: Record<string, string | number | undefined>, full_response?: false, prefix?: string) : Promise<T>;
45+
public async delete<T = any>(url: string, parameters: Record<string, string | number | undefined>, full_response: true, prefix?: string) : Promise<TwitterResponse<T>>;
4446

4547
public async delete<T = any>(url: string, parameters?: Record<string, string | number | undefined> | boolean, full_response = false, prefix = this._prefix) : Promise<T | TwitterResponse<T>> {
4648
if (typeof parameters === 'boolean') {

src/v2/client.v2.read.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import TwitterApiSubClient from '../client.subclient';
22
import { API_V2_PREFIX } from '../globals';
33
import TweetPaginator from './TweetPaginator';
4-
import { Tweetv2SearchParams, Tweetv2SearchResult } from './types.v2';
4+
import {Tweetv2SearchParams, Tweetv2SearchResult, User, UserResult, UsersResult, UsersV2Params} from './types.v2';
55

66
/**
77
* Base Twitter v2 client with only read right.
@@ -15,4 +15,22 @@ export default class TwitterApiv2ReadOnly extends TwitterApiSubClient {
1515

1616
return new TweetPaginator(initialRq.data, initialRq.rateLimit!, this, queryParams);
1717
}
18+
19+
public user(userId: string, options: Partial<UsersV2Params> = {}) {
20+
return this.get<UserResult>(`users/${userId}`, options);
21+
}
22+
23+
public users(userIds: string | string[], options: Partial<UsersV2Params> = {}) {
24+
const ids = Array.isArray(userIds) ? userIds.join(',') : userIds;
25+
return this.get<UsersResult>(`users`, {...options, ids});
26+
}
27+
28+
public userByUsername(username: string, options: Partial<UsersV2Params> = {}) {
29+
return this.get<UserResult>(`users/by/username/${username}`, options);
30+
}
31+
32+
public usersByUsernames(usernames: string | string[], options: Partial<UsersV2Params> = {}) {
33+
usernames = Array.isArray(usernames) ? usernames.join(',') : usernames;
34+
return this.get<UsersResult>(`users/by`, {...options, usernames});
35+
}
1836
}

src/v2/types.v2.ts

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
1+
// Tweets
22
export interface Tweetv2SearchParams extends Partial<Tweetv2FieldsParams> {
33
/** ISO date string */
44
end_time?: string;
@@ -22,7 +22,7 @@ export interface Tweetv2FieldsParams {
2222

2323
// TODO type
2424
export interface Tweet {
25-
[query: string]: any;
25+
[field: string]: any;
2626
}
2727

2828
export interface Tweetv2SearchResult {
@@ -34,3 +34,78 @@ export interface Tweetv2SearchResult {
3434
next_token: string;
3535
};
3636
}
37+
38+
// Entities
39+
interface Entity {
40+
start: number;
41+
end: number;
42+
}
43+
interface UrlEntity extends Entity {
44+
url: string; // https;//t.co/...
45+
expanded_url: string; // https://unfollow.ninja/
46+
display_url: string; // unfollow.ninja
47+
}
48+
interface HashtagEntity extends Entity {
49+
hashtag: string;
50+
}
51+
interface CashtagEntity extends Entity {
52+
cashtag: string;
53+
}
54+
interface MentionEntity extends Entity {
55+
username: string;
56+
}
57+
58+
// Users
59+
export interface UsersV2Params {
60+
expansions: 'pinned_tweet_id';
61+
'tweet.fields': string;
62+
'user.fields': string;
63+
}
64+
65+
66+
export interface User {
67+
id: string;
68+
name: string;
69+
username: string;
70+
created_at?: string; // ISO 8601 date
71+
protected?: boolean;
72+
withheld?: {
73+
country_codes?: string[];
74+
scope?: 'user';
75+
}
76+
location?: string;
77+
url?: string;
78+
description?: string;
79+
verified?: boolean;
80+
entities?: {
81+
url?: { urls: UrlEntity[] };
82+
description: {
83+
urls?: UrlEntity[];
84+
hashtags?: HashtagEntity[];
85+
cashtags?: CashtagEntity[];
86+
mentions?: MentionEntity[];
87+
}
88+
}
89+
profile_image_url?: string;
90+
public_metrics?: {
91+
followers_count?: number;
92+
following_count?: number;
93+
tweet_count?: number;
94+
listed_count?: number;
95+
},
96+
pinned_tweet_id?: string;
97+
}
98+
99+
export interface UserResult {
100+
data: User
101+
includes?: {
102+
tweets: [Tweet]; // pinned tweet
103+
}
104+
}
105+
106+
export interface UsersResult {
107+
data: User[]
108+
includes?: {
109+
tweets: Tweet[]; // pinned tweets
110+
}
111+
}

0 commit comments

Comments
 (0)