Skip to content

Commit 4e5e43e

Browse files
authored
Merge pull request #116 from MerleLiuKun/feat-timelines
Feat timelines
2 parents 361dfc5 + 79c900d commit 4e5e43e

File tree

7 files changed

+119
-6
lines changed

7 files changed

+119
-6
lines changed

docs/docs/usage/tweets/timelines.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ api.get_timelines(user_id="2244994945")
77
# Response(data=[Tweet(id=1364275610764201984, text=If you're newly approved for the Academic...), Tweet(id=1362876655061073928, text=From our living rooms to yours 🐱‍💻🛋️Our...), Tweet(id=1362439338978467841, text=“To quote my creator Jerome Gangneux, I always...), Tweet(id=1362439338169016324, text=“In the 20th century, managers managed humans,...), Tweet(id=1362439336910675970, text=Meet one of the useful Twitter bots out there:...), Tweet(id=1359912509940011010, text=Valentine’s Day is approaching! 💙 Over the...), Tweet(id=1359554366051504129, text=Go ahead, follow another puppy account. We...), Tweet(id=1357371424487268354, text=Learn how academics can get historical Tweets...), Tweet(id=1356991771553583106, text=Who knew an API could be delicious?...), Tweet(id=1354215875998437376, text=RT @TwitterOSS: Today we’re happy to share...)])
88
```
99

10+
Get collection of the most recent Tweets and Retweets posted by you and users you follow
11+
12+
```python
13+
my_api.get_timelines_reverse_chronological(user_id="2244994945")
14+
# Response(data=[Tweet(id=1524796546306478083, text=Today marks the launch of Devs in the Details...), Tweet(id=1524468552404668416, text=📢 Join @jessicagarson @alanbenlee and @i_am_daniele tomorrow...))
15+
```
16+
1017
Get tweets which mention target user
1118

1219
```python

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Authlib = "^0.15.4"
2929
[tool.poetry.dev-dependencies]
3030
pytest = "^6.2.5"
3131
pytest-cov = "^3.0.0"
32-
responses = "^0.12.1"
32+
responses = "^0.17.0"
3333

3434
[build-system]
3535
requires = ["poetry-core>=1.0.0"]

pytwitter/api.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,76 @@ def get_timelines(
718718
return_json=return_json,
719719
)
720720

721+
def get_timelines_reverse_chronological(
722+
self,
723+
user_id: str,
724+
*,
725+
start_time: Optional[str] = None,
726+
end_time: Optional[str] = None,
727+
since_id: Optional[str] = None,
728+
until_id: Optional[str] = None,
729+
max_results: Optional[int] = None,
730+
pagination_token: Optional[str] = None,
731+
exclude: Optional[Union[str, List, Tuple]] = None,
732+
tweet_fields: Optional[Union[str, List, Tuple]] = None,
733+
expansions: Optional[Union[str, List, Tuple]] = None,
734+
user_fields: Optional[Union[str, List, Tuple]] = None,
735+
media_fields: Optional[Union[str, List, Tuple]] = None,
736+
place_fields: Optional[Union[str, List, Tuple]] = None,
737+
poll_fields: Optional[Union[str, List, Tuple]] = None,
738+
return_json: bool = False,
739+
) -> Union[dict, md.Response]:
740+
"""
741+
Allows you to retrieve a collection of the most recent Tweets and Retweets posted by you and users you follow.
742+
This endpoint returns up to the last 3200 Tweets.
743+
744+
:param user_id: Unique identifier of the user that is requesting their chronological home timeline.
745+
:param start_time: Oldest or earliest UTC timestamp for tweets, format YYYY-MM-DDTHH:mm:ssZ.
746+
:param end_time: Newest or most recent UTC timestamp for tweets, format YYYY-MM-DDTHH:mm:ssZ.
747+
:param since_id: Greater than (that is, more recent than) tweet id for response. Exclude this since_id.
748+
:param until_id: Less than (that is, older than) tweet id for response. Exclude this until_id.
749+
:param max_results: The maximum number of results to be returned per page. Number between 5 and the 100.
750+
By default, each page will return 10 results.
751+
:param pagination_token: Token for the pagination.
752+
:param exclude: Fields for types of Tweets to exclude from the response.
753+
:param tweet_fields: Fields for the tweet object.
754+
:param expansions: Fields for the expansions.
755+
:param user_fields: Fields for the user object, Expansion required.
756+
:param media_fields: Fields for the media object, Expansion required.
757+
:param place_fields: Fields for the place object, Expansion required.
758+
:param poll_fields: Fields for the poll object, Expansion required.
759+
:param return_json: Type for returned data. If you set True JSON data will be returned.
760+
:return: Response instance or json.
761+
"""
762+
args = {
763+
"start_time": start_time,
764+
"end_time": end_time,
765+
"since_id": since_id,
766+
"until_id": until_id,
767+
"exclude": enf_comma_separated(name="exclude", value=exclude),
768+
"tweet.fields": enf_comma_separated(
769+
name="tweet_fields", value=tweet_fields
770+
),
771+
"expansions": enf_comma_separated(name="expansions", value=expansions),
772+
"user.fields": enf_comma_separated(name="user_fields", value=user_fields),
773+
"media.fields": enf_comma_separated(
774+
name="media_fields", value=media_fields
775+
),
776+
"place.fields": enf_comma_separated(
777+
name="place_fields", value=place_fields
778+
),
779+
"poll.fields": enf_comma_separated(name="poll_fields", value=poll_fields),
780+
"max_results": max_results,
781+
"pagination_token": pagination_token,
782+
}
783+
return self._get(
784+
url=f"{self.BASE_URL_V2}/users/{user_id}/timelines/reverse_chronological",
785+
params=args,
786+
cls=md.Tweet,
787+
multi=True,
788+
return_json=return_json,
789+
)
790+
721791
def get_mentions(
722792
self,
723793
user_id: str,

pytwitter/rate_limit.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ def get_limit(self, auth_type, method="GET"):
6060
LIMIT_APP_GET=1500,
6161
LIMIT_USER_GET=900,
6262
)
63+
USER_TIMELINE_REVERSE = Endpoint(
64+
resource="/users/:id/timelines/reverse_chronological",
65+
regex=re.compile(r"/users/\d+/timelines/reverse_chronological"),
66+
LIMIT_USER_GET=180,
67+
)
6368
USER_MENTIONS = Endpoint(
6469
resource="/users/:id/mentions",
6570
regex=re.compile(r"/users/\d+/mentions"),
@@ -326,6 +331,7 @@ def get_limit(self, auth_type, method="GET"):
326331
TWEET_BY_ID,
327332
TWEETS_BY_ID,
328333
USER_TIMELINE,
334+
USER_TIMELINE_REVERSE,
329335
USER_MENTIONS,
330336
TWEET_SEARCH_RECENT,
331337
TWEET_SEARCH_ALL,
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"data":[{"created_at":"2022-05-12T17:00:00.000Z","text":"Today marks the launch of Devs in the Details, a technical video series made for developers by developers building with the Twitter API. 🚀nnIn this premiere episode, @jessicagarson walks us through how she built @FactualCat #WelcomeToOurTechTalkn⬇️nnhttps://t.co/nGa8JTQVBJ","author_id":"2244994945","id":"1524796546306478083"},{"created_at":"2022-05-11T19:16:40.000Z","text":"📢 Join @jessicagarson @alanbenlee and @i_am_daniele tomorrow, May 12 | 5:30 ET / 2:30pm PT as they discuss the future of bots https://t.co/sQ2bIO1fz6","author_id":"2244994945","id":"1524468552404668416"},{"created_at":"2022-05-09T20:12:01.000Z","text":"Do you make bots with the Twitter API? 🤖nnJoin @jessicagarson @alanbenlee and @iamdaniele on Thursday, May 12 | 5:30 ET / 2:30pm PT as they discuss the future of bots and answer any questions you might have. 🎙📆⬇️nnhttps://t.co/2uVt7hCcdG","author_id":"2244994945","id":"1523757705436958720"},{"created_at":"2022-05-06T18:19:54.000Z","text":"If you’d like to apply, or would like to nominate someone else for the program, please feel free to fill out the following form:nnhttps://t.co/LUuWj24HLu","author_id":"2244994945","id":"1522642324781633536"},{"created_at":"2022-05-06T18:19:53.000Z","text":"We’ve gone into more detail on each Insider in our forum post. nnJoin us in congratulating the new additions! 🥳nnhttps://t.co/0r5maYEjPJ","author_id":"2244994945","id":"1522642323535847424"}],"includes":{"users":[{"created_at":"2013-12-14T04:35:55.000Z","name":"Twitter Dev","username":"TwitterDev","id":"2244994945"}]},"meta":{"result_count":5,"newest_id":"1524796546306478083","oldest_id":"1522642323535847424","next_token":"7140dibdnow9c7btw421dyz6jism75z99gyxd8egarsc4"}}

tests/apis/test_timelines.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,32 @@ def test_get_timelines(api, helpers):
3030
assert resp.meta.newest_id == "1364275610764201984"
3131

3232

33+
@responses.activate
34+
def test_get_timelines_reverse_chronological(api_with_user, helpers):
35+
user_id = "2244994945"
36+
timelines_data = helpers.load_json_data(
37+
"testdata/apis/timeline/timeline_reverse_chronological.json"
38+
)
39+
40+
responses.add(
41+
responses.GET,
42+
url=f"https://api.twitter.com/2/users/{user_id}/timelines/reverse_chronological",
43+
json=timelines_data,
44+
)
45+
46+
resp = api_with_user.get_timelines_reverse_chronological(
47+
user_id=user_id,
48+
tweet_fields=["conversation_id", "lang"],
49+
expansions="author_id",
50+
user_fields=["created_at", "entities"],
51+
max_results=5,
52+
)
53+
54+
assert len(resp.data) == 5
55+
assert resp.data[0].id == "1524796546306478083"
56+
assert resp.meta.newest_id == "1524796546306478083"
57+
58+
3359
@responses.activate
3460
def test_get_mentions(api, helpers):
3561
user_id = "2244994945"

tests/streams/test_stream.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import pytest
99
import responses
10+
from responses import matchers
1011

1112
from pytwitter import StreamApi, PyTwitterError
1213

@@ -70,35 +71,37 @@ def callback(request):
7071
else:
7172
return 200, {}, "\r\n"
7273

74+
req_kwargs = {"stream": True}
7375
responses.add(
7476
responses.CallbackResponse(
7577
responses.GET,
7678
url="https://api.twitter.com/2/tweets/sample/stream",
7779
callback=callback,
78-
stream=True,
7980
content_type="application/json",
80-
)
81+
),
82+
match=[matchers.request_kwargs_matcher(req_kwargs)],
8183
)
8284

8385
stream_api = MyStreamApi(bearer_token="bearer token")
8486

8587
stream_api.sample_stream(backfill_minutes=1)
8688

87-
assert stream_api.running == False
89+
assert not stream_api.running
8890
assert stream_api.tweet_max_count == 10
8991

9092

9193
@responses.activate
9294
@patch("time.sleep", return_value=None)
9395
def test_stream_error(patched_time_sleep):
96+
req_kwargs = {"stream": True}
9497
responses.add(
9598
responses.Response(
9699
responses.GET,
97100
url="https://api.twitter.com/2/tweets/search/stream",
98-
stream=True,
99101
content_type="application/json",
100102
status=400,
101-
)
103+
),
104+
match=[matchers.request_kwargs_matcher(req_kwargs)],
102105
)
103106

104107
api = StreamApi(bearer_token="bearer token", max_retries=10)

0 commit comments

Comments
 (0)