Skip to content

Commit f6742ea

Browse files
authored
Add files via upload
1 parent ef59d24 commit f6742ea

File tree

4 files changed

+170
-3
lines changed

4 files changed

+170
-3
lines changed

twikit/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,4 @@
2525
from .user import User
2626
from .utils import build_query
2727

28-
__version__ = '1.3.12'
28+
__version__ = '1.3.13'

twikit/tweet.py

+2
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ class Tweet:
7474
Related tweets.
7575
hashtags: list[str]
7676
Hashtags included in the tweet text.
77+
poll : Poll
78+
Poll attached to the tweet.
7779
"""
7880

7981
def __init__(self, client: Client, data: dict, user: User = None) -> None:

twikit/twikit_async/client.py

+49-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
from .message import Message
3434
from .notification import Notification
3535
from .trend import Trend
36-
from .tweet import ScheduledTweet, Tweet
36+
from .tweet import Poll, ScheduledTweet, Tweet
3737
from .user import User
3838
from .utils import Result
3939

@@ -813,6 +813,54 @@ async def create_poll(
813813

814814
return response['card_uri']
815815

816+
async def vote(
817+
self,
818+
selected_choice: str,
819+
card_uri: str,
820+
tweet_id: str,
821+
card_name: str
822+
) -> Poll:
823+
"""
824+
Vote on a poll with the selected choice.
825+
Parameters
826+
----------
827+
selected_choice : str
828+
The label of the selected choice for the vote.
829+
card_uri : str
830+
The URI of the poll card.
831+
tweet_id : str
832+
The ID of the original tweet containing the poll.
833+
card_name : str
834+
The name of the poll card.
835+
Returns
836+
-------
837+
Poll
838+
The Poll object representing the updated poll after voting.
839+
"""
840+
data = urlencode({
841+
'twitter:string:card_uri': card_uri,
842+
'twitter:long:original_tweet_id': tweet_id,
843+
'twitter:string:response_card_name': card_name,
844+
'twitter:string:cards_platform': 'Web-12',
845+
'twitter:string:selected_choice': selected_choice
846+
})
847+
print(card_uri, tweet_id, card_name, selected_choice)
848+
849+
headers = self._base_headers | {
850+
'content-type': 'application/x-www-form-urlencoded'
851+
}
852+
response = (await self.http.post(
853+
Endpoint.VOTE,
854+
data=data,
855+
headers=headers
856+
)).json()
857+
858+
card_data = {
859+
'rest_id': response['card']['url'],
860+
'legacy': response['card']
861+
}
862+
return Poll(self, card_data, None)
863+
816864
async def create_tweet(
817865
self,
818866
text: str = '',

twikit/twikit_async/tweet.py

+118-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import re
34
from datetime import datetime
45
from typing import TYPE_CHECKING
56

@@ -159,10 +160,22 @@ def __init__(self, client: Client, data: dict, user: User = None) -> None:
159160
i['text'] for i in hashtags
160161
]
161162

163+
if (
164+
'card' in data and
165+
data['card']['legacy']['name'].startswith('poll')
166+
):
167+
self._poll_data = data['card']
168+
else:
169+
self._poll_data = None
170+
162171
@property
163172
def created_at_datetime(self) -> datetime:
164173
return timestamp_to_datetime(self.created_at)
165174

175+
@property
176+
def poll(self) -> Poll:
177+
return self._poll_data and Poll(self._client, self._poll_data, self)
178+
166179
async def delete(self) -> Response:
167180
"""Deletes the tweet.
168181
@@ -405,4 +418,108 @@ async def delete(self) -> Response:
405418
return await self._client.delete_scheduled_tweet(self.id)
406419

407420
def __repr__(self) -> str:
408-
return f'<ScheduledTweet id="{self.id}">'
421+
return f'<ScheduledTweet id="{self.id}">'
422+
423+
424+
class Poll:
425+
"""Represents a poll associated with a tweet.
426+
Attributes
427+
----------
428+
tweet : Tweet
429+
The tweet associated with the poll.
430+
id : str
431+
The unique identifier of the poll.
432+
name : str
433+
The name of the poll.
434+
choices : list of dict
435+
A list containing dictionaries representing poll choices.
436+
Each dictionary contains 'label' and 'count' keys
437+
for choice label and count.
438+
duration_minutes : int
439+
The duration of the poll in minutes.
440+
end_datetime_utc : str
441+
The end date and time of the poll in UTC format.
442+
last_updated_datetime_utc : str
443+
The last updated date and time of the poll in UTC format.
444+
selected_choice : str | None
445+
Number of the selected choice.
446+
"""
447+
448+
def __init__(
449+
self, client: Client, data: dict, tweet: Tweet | None = None
450+
) -> None:
451+
self._client = client
452+
self.tweet = tweet
453+
454+
legacy = data['legacy']
455+
binding_values = legacy['binding_values']
456+
457+
if isinstance(legacy['binding_values'], list):
458+
binding_values = {
459+
i.get('key'): i.get('value')
460+
for i in legacy['binding_values']
461+
}
462+
463+
self.id: str = data['rest_id']
464+
self.name: str = legacy['name']
465+
466+
choices_number = int(re.findall(
467+
r'poll(\d)choice_text_only', self.name
468+
)[0])
469+
choices = []
470+
471+
for i in range(1, choices_number + 1):
472+
choice_label = binding_values[f'choice{i}_label']
473+
choice_count = binding_values[f'choice{i}_count']
474+
choices.append({
475+
'number': str(i),
476+
'label': choice_label['string_value'],
477+
'count': choice_count.get('string_value', '0')
478+
})
479+
480+
self.choices = choices
481+
482+
duration_minutes = binding_values['duration_minutes']['string_value']
483+
self.duration_minutes = int(duration_minutes)
484+
485+
end = binding_values['end_datetime_utc']['string_value']
486+
updated = binding_values['last_updated_datetime_utc']['string_value']
487+
self.end_datetime_utc: str = end
488+
self.last_updated_datetime_utc: str = updated
489+
490+
counts_are_final = binding_values['counts_are_final']['boolean_value']
491+
self.counts_are_final: bool = counts_are_final
492+
493+
if 'selected_choice' in binding_values:
494+
selected_choice = binding_values['selected_choice']['string_value']
495+
self.selected_choice: str = selected_choice
496+
else:
497+
self.selected_choice = None
498+
499+
async def vote(self, selected_choice: str) -> Poll:
500+
"""
501+
Vote on the poll with the specified selected choice.
502+
Parameters
503+
----------
504+
selected_choice : str
505+
The label of the selected choice for the vote.
506+
Returns
507+
-------
508+
Poll
509+
The Poll object representing the updated poll after voting.
510+
"""
511+
return await self._client.vote(
512+
selected_choice,
513+
self.id,
514+
self.tweet.id,
515+
self.name
516+
)
517+
518+
def __repr__(self) -> str:
519+
return f'<Poll id="{self.id}">'
520+
521+
def __eq__(self, __value: object) -> bool:
522+
return isinstance(__value, Poll) and self.id == __value.id
523+
524+
def __ne__(self, __value: object) -> bool:
525+
return not self == __value

0 commit comments

Comments
 (0)