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 9, 2024
1 parent a44e61b commit ede3262
Show file tree
Hide file tree
Showing 10 changed files with 255 additions and 9 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,4 @@ If you have any features you'd like to see added or encounter any issues,
please let me know in the [issues](https://github.com/d60/twikit/issues) section.


Additionally, if you find this library useful, I would appreciate it if you would star this repository or share this library⭐! Thank you very much!
Additionally, if you find this library useful, I would appreciate it if you would star this repository or share this library⭐! Thank you very much!
4 changes: 2 additions & 2 deletions twikit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
from .errors import *
from .message import Message
from .trend import Trend
from .tweet import Poll, ScheduledTweet, Tweet
from .tweet import CommunityNote, Poll, ScheduledTweet, Tweet
from .user import User
from .utils import build_query

__version__ = '1.4.5'
__version__ = '1.4.6'
45 changes: 44 additions & 1 deletion twikit/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@
from .message import Message
from .notification import Notification
from .trend import Trend
from .tweet import Poll, ScheduledTweet, Tweet
from .tweet import CommunityNote, Poll, ScheduledTweet, Tweet
from .user import User
from .utils import (
COMMUNITY_NOTE_FEATURES,
LIST_FEATURES,
FEATURES,
TOKEN,
Expand Down Expand Up @@ -1443,6 +1444,48 @@ def get_favoriters(
tweet_id, count, cursor, Endpoint.FAVORITERS
)

def get_community_note(self, note_id: str) -> CommunityNote:
"""
Fetches a community note by ID.
Parameters
----------
note_id : :class:`str`
The ID of the community note.
Returns
-------
:class:`CommunityNote`
A CommunityNote object representing the fetched community note.
Raises
------
:exc:`TwitterException`
Invalid note ID.
Examples
--------
>>> note_id = '...'
>>> note = client.get_community_note(note_id)
>>> print(note)
<CommunityNote id="...">
"""
params = flatten_params({
'variables': {'note_id': note_id},
'features': COMMUNITY_NOTE_FEATURES
})
response = self.http.get(
Endpoint.FETCH_COMMUNITY_NOTE,
params=params,
headers=self._base_headers
).json()
note_data = response['data']['birdwatch_note_by_rest_id']
if 'data_v1' not in note_data:
raise TwitterException(f'Invalid user id: {note_id}')
return CommunityNote(
self, note_data
)

def get_user_tweets(
self,
user_id: str,
Expand Down
6 changes: 5 additions & 1 deletion twikit/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from typing import TYPE_CHECKING

from .message import Message
from .user import User
from .utils import build_user_data

if TYPE_CHECKING:
from httpx import Response
Expand Down Expand Up @@ -39,7 +41,9 @@ def __init__(self, client: Client, group_id: str, data: dict) -> None:
)

members = data['conversation_timeline']['users'].values()
self.members: list[str] = [i['id_str'] for i in members]
self.members: list[User] = [
User(client, build_user_data(i)) for i in members
]

def get_history(self, max_id: str | None = None) -> Result[GroupMessage]:
"""
Expand Down
71 changes: 71 additions & 0 deletions twikit/tweet.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,16 @@ def __init__(self, client: Client, data: dict, user: User = None) -> None:
'is_edit_eligible')
self.edits_remaining: int = data['edit_control'].get('edits_remaining')
self.state: str = data['views'].get('state')
self.has_community_notes: bool = data.get('has_birdwatch_notes')

if 'birdwatch_pivot' in data:
community_note_data = data['birdwatch_pivot']
self.community_note = {
'id': community_note_data['note']['rest_id'],
'text': community_note_data['subtitle']['text']
}
else:
self.community_note = None

if note_tweet_results:
hashtags_ = find_dict(note_tweet_results, 'hashtags')
Expand Down Expand Up @@ -533,3 +543,64 @@ def __eq__(self, __value: object) -> bool:

def __ne__(self, __value: object) -> bool:
return not self == __value


class CommunityNote:
"""Represents a community note.
Attributes
----------
id : :class:`str`
The ID of the community note.
text : :class:`str`
The text content of the community note.
misleading_tags : list[:class:`str`]
A list of tags indicating misleading information.
trustworthy_sources : :class:`bool`
Indicates if the sources are trustworthy.
helpful_tags : list[:class:`str`]
A list of tags indicating helpful information.
created_at : :class:`int`
The timestamp when the note was created.
can_appeal : :class:`bool`
Indicates if the note can be appealed.
appeal_status : :class:`str`
The status of the appeal.
is_media_note : :class:`bool`
Indicates if the note is related to media content.
media_note_matches : :class:`str`
Matches related to media content.
birdwatch_profile : :class:`dict`
Birdwatch profile associated with the note.
tweet_id : :class:`str`
The ID of the tweet associated with the note.
"""
def __init__(self, client: Client, data: dict) -> None:
self._client = client
self.id: str = data['rest_id']

data_v1 = data['data_v1']
self.text: str = data_v1['summary']['text']
self.misleading_tags: list[str] = data_v1.get('misleading_tags')
self.trustworthy_sources: bool = data_v1.get('trustworthy_sources')
self.helpful_tags: list[str] = data.get('helpful_tags')
self.created_at: int = data.get('created_at')
self.can_appeal: bool = data.get('can_appeal')
self.appeal_status: str = data.get('appeal_status')
self.is_media_note: bool = data.get('is_media_note')
self.media_note_matches: str = data.get('media_note_matches')
self.birdwatch_profile: dict = data.get('birdwatch_profile')
self.tweet_id: str = data['tweet_results']['result']['rest_id']

def update(self) -> None:
new = self._client.get_community_note(self.id)
self.__dict__.update(new.__dict__)

def __repr__(self) -> str:
return f'<CommunityNote id="{self.id}">'

def __eq__(self, __value: object) -> bool:
return isinstance(__value, CommunityNote) and self.id == __value.id

def __ne__(self, __value: object) -> bool:
return not self == __value
2 changes: 1 addition & 1 deletion twikit/twikit_async/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@
from .list import List
from .message import Message
from .trend import Trend
from .tweet import Poll, ScheduledTweet, Tweet
from .tweet import CommunityNote, Poll, ScheduledTweet, Tweet
from .user import User
45 changes: 44 additions & 1 deletion twikit/twikit_async/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
raise_exceptions_from_response
)
from ..utils import (
COMMUNITY_NOTE_FEATURES,
FEATURES,
LIST_FEATURES,
TOKEN,
Expand All @@ -37,7 +38,7 @@
from .message import Message
from .notification import Notification
from .trend import Trend
from .tweet import Poll, ScheduledTweet, Tweet
from .tweet import CommunityNote, Poll, ScheduledTweet, Tweet
from .user import User
from .utils import Flow, Result

Expand Down Expand Up @@ -1459,6 +1460,48 @@ async def get_favoriters(
tweet_id, count, cursor, Endpoint.FAVORITERS
)

async def get_community_note(self, note_id: str) -> CommunityNote:
"""
Fetches a community note by ID.
Parameters
----------
note_id : :class:`str`
The ID of the community note.
Returns
-------
:class:`CommunityNote`
A CommunityNote object representing the fetched community note.
Raises
------
:exc:`TwitterException`
Invalid note ID.
Examples
--------
>>> note_id = '...'
>>> note = client.get_community_note(note_id)
>>> print(note)
<CommunityNote id="...">
"""
params = flatten_params({
'variables': {'note_id': note_id},
'features': COMMUNITY_NOTE_FEATURES
})
response = (await self.http.get(
Endpoint.FETCH_COMMUNITY_NOTE,
params=params,
headers=self._base_headers
)).json()
note_data = response['data']['birdwatch_note_by_rest_id']
if 'data_v1' not in note_data:
raise TwitterException(f'Invalid user id: {note_id}')
return CommunityNote(
self, note_data
)

async def get_user_tweets(
self,
user_id: str,
Expand Down
6 changes: 5 additions & 1 deletion twikit/twikit_async/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from typing import TYPE_CHECKING

from .message import Message
from .user import User
from ..utils import build_user_data

if TYPE_CHECKING:
from httpx import Response
Expand Down Expand Up @@ -39,7 +41,9 @@ def __init__(self, client: Client, group_id: str, data: dict) -> None:
)

members = data['conversation_timeline']['users'].values()
self.members: list[str] = [i['id_str'] for i in members]
self.members: list[User] = [
User(client, build_user_data(i)) for i in members
]

async def get_history(
self, max_id: str | None = None
Expand Down
73 changes: 72 additions & 1 deletion twikit/twikit_async/tweet.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,16 @@ def __init__(self, client: Client, data: dict, user: User = None) -> None:
'is_edit_eligible')
self.edits_remaining: int = data['edit_control'].get('edits_remaining')
self.state: str = data['views'].get('state')
self.has_community_notes: bool = data.get('has_birdwatch_notes')

if 'birdwatch_pivot' in data:
community_note_data = data['birdwatch_pivot']
self.community_note = {
'id': community_note_data['note']['rest_id'],
'text': community_note_data['subtitle']['text']
}
else:
self.community_note = None

if note_tweet_results:
hashtags_ = find_dict(note_tweet_results, 'hashtags')
Expand Down Expand Up @@ -527,4 +537,65 @@ def __eq__(self, __value: object) -> bool:
return isinstance(__value, Poll) and self.id == __value.id

def __ne__(self, __value: object) -> bool:
return not self == __value
return not self == __value


class CommunityNote:
"""Represents a community note.
Attributes
----------
id : :class:`str`
The ID of the community note.
text : :class:`str`
The text content of the community note.
misleading_tags : list[:class:`str`]
A list of tags indicating misleading information.
trustworthy_sources : :class:`bool`
Indicates if the sources are trustworthy.
helpful_tags : list[:class:`str`]
A list of tags indicating helpful information.
created_at : :class:`int`
The timestamp when the note was created.
can_appeal : :class:`bool`
Indicates if the note can be appealed.
appeal_status : :class:`str`
The status of the appeal.
is_media_note : :class:`bool`
Indicates if the note is related to media content.
media_note_matches : :class:`str`
Matches related to media content.
birdwatch_profile : :class:`dict`
Birdwatch profile associated with the note.
tweet_id : :class:`str`
The ID of the tweet associated with the note.
"""
def __init__(self, client: Client, data: dict) -> None:
self._client = client
self.id: str = data['rest_id']

data_v1 = data['data_v1']
self.text: str = data_v1['summary']['text']
self.misleading_tags: list[str] = data_v1.get('misleading_tags')
self.trustworthy_sources: bool = data_v1.get('trustworthy_sources')
self.helpful_tags: list[str] = data.get('helpful_tags')
self.created_at: int = data.get('created_at')
self.can_appeal: bool = data.get('can_appeal')
self.appeal_status: str = data.get('appeal_status')
self.is_media_note: bool = data.get('is_media_note')
self.media_note_matches: str = data.get('media_note_matches')
self.birdwatch_profile: dict = data.get('birdwatch_profile')
self.tweet_id: str = data['tweet_results']['result']['rest_id']

async def update(self) -> None:
new = await self._client.get_community_note(self.id)
self.__dict__.update(new.__dict__)

def __repr__(self) -> str:
return f'<CommunityNote id="{self.id}">'

def __eq__(self, __value: object) -> bool:
return isinstance(__value, CommunityNote) and self.id == __value.id

def __ne__(self, __value: object) -> bool:
return not self == __value
10 changes: 10 additions & 0 deletions twikit/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,15 @@
'responsive_web_graphql_timeline_navigation_enabled': True
}

COMMUNITY_NOTE_FEATURES = {
'responsive_web_birdwatch_media_notes_enabled': True,
'responsive_web_graphql_timeline_navigation_enabled': True,
'rweb_tipjar_consumption_enabled': False,
'responsive_web_graphql_exclude_directive_enabled': True,
'verified_phone_label_enabled': False,
'responsive_web_graphql_skip_user_profile_image_extensions_enabled': False
}


class Endpoint:
"""
Expand Down Expand Up @@ -139,6 +148,7 @@ class Endpoint:
NOTIFICATIONS_MENTIONES = 'https://twitter.com/i/api/2/notifications/mentions.json'
VOTE = 'https://caps.twitter.com/v2/capi/passthrough/1'
REPORT_FLOW = 'https://twitter.com/i/api/1.1/report/flow.json'
FETCH_COMMUNITY_NOTE = 'https://twitter.com/i/api/graphql/fKWPPj271aTM-AB9Xp48IA/BirdwatchFetchOneNote'

T = TypeVar('T')

Expand Down

0 comments on commit ede3262

Please sign in to comment.