Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
  • Loading branch information
d60 authored Apr 23, 2024
1 parent c800d13 commit 1068958
Show file tree
Hide file tree
Showing 8 changed files with 52 additions and 449 deletions.
3 changes: 1 addition & 2 deletions twikit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@
A Python library for interacting with the Twitter API.
"""

__version__ = '1.5.6'
__version__ = '1.5.5'

from .bookmark import BookmarkFolder
from .client import Client
from .community import (Community, CommunityCreator, CommunityMember,
CommunityRule)
Expand Down
219 changes: 21 additions & 198 deletions twikit/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
from fake_useragent import UserAgent
from httpx import Response

from .bookmark import BookmarkFolder
from .community import Community, CommunityMember
from .errors import (
CouldNotTweet,
Expand All @@ -31,7 +30,6 @@
from .tweet import CommunityNote, Poll, ScheduledTweet, Tweet
from .user import User
from .utils import (
BOOKMARK_FOLDER_TIMELINE_FEATURES,
COMMUNITY_TWEETS_FEATURES,
COMMUNITY_NOTE_FEATURES,
JOIN_COMMUNITY_FEATURES,
Expand Down Expand Up @@ -456,10 +454,7 @@ def search_tweet(
product = product.capitalize()

response = self._search(query, product, count, cursor)
instructions = find_dict(response, 'instructions')
if not instructions:
return Result([])
instructions = instructions[0]
instructions = find_dict(response, 'instructions')[0]

if product == 'Media' and cursor is not None:
items = find_dict(instructions, 'moduleItems')[0]
Expand All @@ -486,19 +481,11 @@ def search_tweet(
previous_cursor = item['content']['value']
if not item['entryId'].startswith(('tweet', 'search-grid')):
continue
tweet_info = find_dict(item, 'result')
if not tweet_info:
continue
tweet_info = tweet_info[0]
tweet_info = find_dict(item, 'result')[0]
if 'tweet' in tweet_info:
tweet_info = tweet_info['tweet']
if 'core' not in tweet_info:
continue
if 'result' not in tweet_info['core']['user_results']:
continue
user_info = tweet_info['core']['user_results']['result']
if 'legacy' in tweet_info:
results.append(Tweet(self, tweet_info, User(self, user_info)))
results.append(Tweet(self, tweet_info, User(self, user_info)))

if next_cursor is None:
if product == 'Media':
Expand Down Expand Up @@ -1434,8 +1421,6 @@ def get_tweet_by_id(
show_replies = None
# Reply to reply
for reply in entry['content']['items'][1:]:
if 'tweetcomposer' in reply['entryId']:
continue
if 'tweet' in find_dict(reply, 'result'):
reply = reply['tweet']
if 'tweet' in reply.get('entryId'):
Expand Down Expand Up @@ -2138,18 +2123,14 @@ def delete_retweet(self, tweet_id: str) -> Response:
)
return response

def bookmark_tweet(
self, tweet_id: str, folder_id: str | None = None
) -> Response:
def bookmark_tweet(self, tweet_id: str) -> Response:
"""
Adds the tweet to bookmarks.
Parameters
----------
tweet_id : :class:`str`
The ID of the tweet to be bookmarked.
folder_id : :class:`str` | None, default=None
The ID of the folder to add the bookmark to.
Returns
-------
Expand All @@ -2160,20 +2141,18 @@ def bookmark_tweet(
--------
>>> tweet_id = '...'
>>> client.bookmark_tweet(tweet_id)
See Also
--------
.bookmark_tweet
"""
variables = {'tweet_id': tweet_id}
if folder_id is None:
endpoint = Endpoint.CREATE_BOOKMARK
else:
endpoint = Endpoint.BOOKMARK_TO_FOLDER
variables['bookmark_collection_id'] = folder_id

data = {
'variables': variables,
'variables': {'tweet_id': tweet_id},
'queryId': get_query_id(Endpoint.CREATE_BOOKMARK)
}
response = self.http.post(
endpoint,
Endpoint.CREATE_BOOKMARK,
json=data,
headers=self._base_headers
)
Expand Down Expand Up @@ -2214,18 +2193,17 @@ def delete_bookmark(self, tweet_id: str) -> Response:
return response

def get_bookmarks(
self, count: int = 20,
cursor: str | None = None, folder_id: str | None = None
self, count: int = 20, cursor: str | None = None
) -> Result[Tweet]:
"""
Retrieves bookmarks from the authenticated user's Twitter account.
Parameters
----------
count : :class:`int`, default=20
The number of bookmarks to retrieve.
folder_id : :class:`str` | None, default=None
Folder to retrieve bookmarks.
The number of bookmarks to retrieve (default is 20).
cursor : :class:`str`, default=None
A cursor to paginate through the bookmarks (default is None).
Returns
-------
Expand All @@ -2251,24 +2229,17 @@ def get_bookmarks(
'count': count,
'includePromotedContent': True
}
if folder_id is None:
endpoint = Endpoint.BOOKMARKS
features = FEATURES | {
'graphql_timeline_v2_bookmark_timeline': True
}
else:
endpoint = Endpoint.BOOKMARK_FOLDER_TIMELINE
variables['bookmark_collection_id'] = folder_id
features = BOOKMARK_FOLDER_TIMELINE_FEATURES

if cursor is not None:
variables['cursor'] = cursor
features = FEATURES | {
'graphql_timeline_v2_bookmark_timeline': True
}
params = flatten_params({
'variables': variables,
'features': features
})
response = self.http.get(
endpoint,
Endpoint.BOOKMARKS,
params=params,
headers=self._base_headers
).json()
Expand All @@ -2278,13 +2249,7 @@ def get_bookmarks(
return Result([])
items = items_[0]
next_cursor = items[-1]['content']['value']
if folder_id is None:
previous_cursor = items[-2]['content']['value']
fetch_previous_result = partial(self.get_bookmarks, count,
previous_cursor, folder_id)
else:
previous_cursor = None
fetch_previous_result = None
previous_cursor = items[-2]['content']['value']

results = []
for item in items:
Expand All @@ -2296,9 +2261,9 @@ def get_bookmarks(

return Result(
results,
partial(self.get_bookmarks, count, next_cursor, folder_id),
partial(self.get_bookmarks, count, next_cursor),
next_cursor,
fetch_previous_result,
partial(self.get_bookmarks, count, previous_cursor),
previous_cursor
)

Expand Down Expand Up @@ -2326,148 +2291,6 @@ def delete_all_bookmarks(self) -> Response:
)
return response

def get_bookmark_folders(
self, cursor: str | None = None
) -> Result[BookmarkFolder]:
"""
Retrieves bookmark folders.
Returns
-------
Result[:class:`BookmarkFolder`]
Result object containing a list of bookmark folders.
Examples
--------
>>> folders = client.get_bookmark_folders()
>>> print(folders)
[<BookmarkFolder id="...">, ..., <BookmarkFolder id="...">]
>>> more_folders = folders.next() # Retrieve more folders
"""
variables = {}
if cursor is not None:
variables['cursor'] = cursor
params = flatten_params({'variables': variables})
response = self.http.get(
Endpoint.BOOKMARK_FOLDERS,
params=params,
headers=self._base_headers
).json()

slice = find_dict(response, 'bookmark_collections_slice')[0]
results = []
for item in slice['items']:
results.append(BookmarkFolder(self, item))

if 'next_cursor' in slice['slice_info']:
next_cursor = slice['slice_info']['next_cursor']
fetch_next_result = partial(self.get_bookmark_folders, next_cursor)
else:
next_cursor = None
fetch_next_result = None

return Result(
results,
fetch_next_result,
next_cursor
)

def edit_bookmark_folder(
self, folder_id: str, name: str
) -> BookmarkFolder:
"""
Edits a bookmark folder.
Parameters
----------
folder_id : :class:`str`
ID of the folder to edit.
name : :class:`str`
New name for the folder.
Returns
-------
:class:`BookmarkFolder`
Updated bookmark folder.
Examples
--------
>>> client.edit_bookmark_folder('123456789', 'MyFolder')
"""
variables = {
'bookmark_collection_id': folder_id,
'name': name
}
data = {
'variables': variables,
'queryId': get_query_id(Endpoint.EDIT_BOOKMARK_FOLDER)
}
response = self.http.post(
Endpoint.EDIT_BOOKMARK_FOLDER,
json=data,
headers=self._base_headers
).json()
return BookmarkFolder(
self, response['data']['bookmark_collection_update']
)

def delete_bookmark_folder(self, folder_id: str) -> Response:
"""
Deletes a bookmark folder.
Parameters
----------
folder_id : :class:`str`
ID of the folder to delete.
Returns
-------
:class:`httpx.Response`
Response returned from twitter api.
"""
variables = {
'bookmark_collection_id': folder_id
}
data = {
'variables': variables,
'queryId': get_query_id(Endpoint.DELETE_BOOKMARK_FOLDER)
}
response = self.http.post(
Endpoint.DELETE_BOOKMARK_FOLDER,
json=data,
headers=self._base_headers
)
return response

def create_bookmark_folder(self, name: str) -> BookmarkFolder:
"""Creates a bookmark folder.
Parameters
----------
name : :class:`str`
Name of the folder.
Returns
-------
:class:`BookmarkFolder`
Newly created bookmark folder.
"""
variables = {
'name': name
}
data = {
'variables': variables,
'queryId': get_query_id(Endpoint.CREATE_BOOKMARK_FOLDER)
}
response = self.http.post(
Endpoint.CREATE_BOOKMARK_FOLDER,
json=data,
headers=self._base_headers
).json()
return BookmarkFolder(
self, response['data']['bookmark_collection_create']
)

def follow_user(self, user_id: str) -> Response:
"""
Follows a user.
Expand Down
3 changes: 0 additions & 3 deletions twikit/errors.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
from __future__ import annotations


class TwitterException(Exception):
"""
Base class for Twitter API related exceptions.
Expand Down
10 changes: 4 additions & 6 deletions twikit/tweet.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class Tweet:
The count of favorites or likes for the tweet.
favorited : :class:`bool`
Indicates if the tweet is favorited.
view_count: :class:`int` | None
view_count: :class:`int`
The count of views.
retweet_count : :class:`int`
The count of retweets for the tweet.
Expand All @@ -65,7 +65,7 @@ class Tweet:
Indicates if the tweet is eligible for editing.
edits_remaining : :class:`int`
The remaining number of edits allowed for the tweet.
state : :class:`str` | None
state : :class:`str`
The state of the tweet views.
replies: Result[:class:`Tweet`] | None
Replies to the tweet.
Expand Down Expand Up @@ -147,17 +147,15 @@ def __init__(self, client: Client, data: dict, user: User = None) -> None:
self.reply_count: int = legacy['reply_count']
self.favorite_count: int = legacy['favorite_count']
self.favorited: bool = legacy['favorited']
self.view_count: int = (data['views'].get('count')
if 'views' in data else None)
self.view_count: int = data['views'].get('count')
self.retweet_count: int = legacy['retweet_count']
self.editable_until_msecs: int = data['edit_control'].get(
'editable_until_msecs')
self.is_translatable: bool = data.get('is_translatable')
self.is_edit_eligible: bool = data['edit_control'].get(
'is_edit_eligible')
self.edits_remaining: int = data['edit_control'].get('edits_remaining')
self.state: str = (data['views'].get('state')
if 'views' in data else None)
self.state: str = data['views'].get('state')
self.has_community_notes: bool = data.get('has_birdwatch_notes')

if 'birdwatch_pivot' in data:
Expand Down
1 change: 0 additions & 1 deletion twikit/twikit_async/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
if os.name == 'nt':
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())

from .bookmark import BookmarkFolder
from ..errors import *
from ..utils import build_query
from .client import Client
Expand Down
Loading

0 comments on commit 1068958

Please sign in to comment.