Poll/Votes support, closes #17
This commit is contained in:
parent
d680579d6a
commit
3999dbd4a1
35
msgs.py
35
msgs.py
@ -1,3 +1,6 @@
|
||||
from asyncore import poll
|
||||
|
||||
|
||||
failedToScan="Failed to scan your link! This may be due to an incorrect link, private/suspended account, deleted tweet, or Twitter itself might be having issues (Check here: https://api.twitterstat.us/)"
|
||||
failedToScanExtra = "\n\nTwitter gave me this error: "
|
||||
tweetNotFound="Tweet not found."
|
||||
@ -8,4 +11,34 @@ def genLikesDisplay(vnf):
|
||||
|
||||
def genQrtDisplay(qrt):
|
||||
verifiedCheck = "☑️" if ('verified' in qrt and qrt['verified']) else ""
|
||||
return ("\n─────────────\n ➤ QRT of " + qrt['handle'] + " (@" + qrt['screen_name'] + ")"+ verifiedCheck+":\n─────────────\n'" + qrt['desc'] + "'")
|
||||
return ("\n─────────────\n ➤ QRT of " + qrt['handle'] + " (@" + qrt['screen_name'] + ")"+ verifiedCheck+":\n─────────────\n'" + qrt['desc'] + "'")
|
||||
|
||||
def genPollDisplay(poll):
|
||||
pctSplit=10
|
||||
output="\n\n"
|
||||
for choice in poll["choices"]:
|
||||
output+=choice["text"]+"\n"+("█"*int(choice["percent"]/pctSplit)) +" "+str(choice["percent"])+"%\n"
|
||||
return output
|
||||
|
||||
def formatEmbedDesc(type,body,qrt,pollDisplay,likesDisplay):
|
||||
# Trim the embed description to 248 characters, prioritizing poll and likes
|
||||
output = ""
|
||||
if pollDisplay==None:
|
||||
pollDisplay=""
|
||||
|
||||
if type=="" or type=="Video":
|
||||
output = body+pollDisplay
|
||||
elif qrt=={}:
|
||||
output= body+pollDisplay+likesDisplay
|
||||
else:
|
||||
qrtDisplay = genQrtDisplay(qrt)
|
||||
output= body + qrtDisplay + likesDisplay
|
||||
if len(output)>248:
|
||||
# find out how many characters we need to remove
|
||||
diff = len(output)-248
|
||||
print("diff: "+str(diff))
|
||||
# remove the characters from body, add ellipsis
|
||||
body = body[:-(diff+1)]+"…"
|
||||
return formatEmbedDesc(type,body,qrt,pollDisplay,likesDisplay)
|
||||
else:
|
||||
return output
|
18
test_vx.py
18
test_vx.py
@ -12,16 +12,23 @@ testTextTweet="https://twitter.com/jack/status/20"
|
||||
testVideoTweet="https://twitter.com/Twitter/status/1263145271946551300"
|
||||
testMediaTweet="https://twitter.com/Twitter/status/1118295916874739714"
|
||||
testMultiMediaTweet="https://twitter.com/Twitter/status/1293239745695211520"
|
||||
testPollTweet="https://twitter.com/norm/status/651169346518056960"
|
||||
|
||||
textVNF_compare = {'tweet': 'https://twitter.com/jack/status/20', 'url': '', 'description': 'just setting up my twttr', 'screen_name': 'jack', 'type': 'Text', 'images': ['', '', '', '', ''], 'time': 'Tue Mar 21 20:50:14 +0000 2006', 'qrt': {}, 'nsfw': False}
|
||||
videoVNF_compare={'tweet': 'https://twitter.com/Twitter/status/1263145271946551300', 'url': 'https://video.twimg.com/amplify_video/1263145212760805376/vid/1280x720/9jous8HM0_duxL0w.mp4?tag=13', 'description': 'Testing, testing...\n\nA new way to have a convo with exactly who you want. We’re starting with a small % globally, so keep your 👀 out to see it in action. https://t.co/pV53mvjAVT', 'thumbnail': 'http://pbs.twimg.com/media/EYeX7akWsAIP1_1.jpg', 'screen_name': 'Twitter', 'type': 'Video', 'images': ['', '', '', '', ''], 'time': 'Wed May 20 16:31:15 +0000 2020', 'qrt': {}, 'nsfw': False,'verified': True, 'size': {'width': 1920, 'height': 1080}}
|
||||
testMedia_compare={'tweet': 'https://twitter.com/Twitter/status/1118295916874739714', 'url': '', 'description': 'On profile pages, we used to only show someone’s replies, not the original Tweet 🙄 Now we’re showing both so you can follow the conversation more easily! https://t.co/LSBEZYFqmY', 'thumbnail': 'https://pbs.twimg.com/media/D4TS4xeX4AA02DI.jpg', 'screen_name': 'Twitter', 'type': 'Image', 'images': ['https://pbs.twimg.com/media/D4TS4xeX4AA02DI.jpg', '', '', '', '1'], 'time': 'Tue Apr 16 23:31:38 +0000 2019', 'qrt': {}, 'nsfw': False, 'size': {}}
|
||||
testMultiMedia_compare={'tweet': 'https://twitter.com/Twitter/status/1293239745695211520', 'url': '', 'description': 'We tested, you Tweeted, and now we’re rolling it out to everyone! https://t.co/w6Q3Q6DiKz', 'thumbnail': 'https://pbs.twimg.com/media/EfJ-C-JU0AAQL_C.jpg', 'screen_name': 'Twitter', 'type': 'Image', 'images': ['https://pbs.twimg.com/media/EfJ-C-JU0AAQL_C.jpg', 'https://pbs.twimg.com/media/EfJ-aHlU0AAU1kq.jpg', '', '', '2'], 'time': 'Tue Aug 11 17:35:57 +0000 2020', 'qrt': {}, 'nsfw': False, 'verified': True, 'size': {}}
|
||||
|
||||
testPoll_comparePoll={"name":"poll2choice_text_only","binding_values":{"choice1_label":{"type":"STRING","string_value":"Mean one thing"},"choice2_label":{"type":"STRING","string_value":"Mean multiple things"},"end_datetime_utc":{"type":"STRING","string_value":"2015-10-06T22:57:24Z"},"counts_are_final":{"type":"BOOLEAN","boolean_value":True},"choice2_count":{"type":"STRING","string_value":"33554"},"choice1_count":{"type":"STRING","string_value":"124875"},"last_updated_datetime_utc":{"type":"STRING","string_value":"2015-10-06T22:57:31Z"},"duration_minutes":{"type":"STRING","string_value":"1440"}}}
|
||||
testPoll_comparePollVNF={'total_votes': 158429, 'choices': [{'text': 'Mean one thing', 'votes': 124875, 'percent': 78.8}, {'text': 'Mean multiple things', 'votes': 33554, 'percent': 21.2}]}
|
||||
|
||||
def compareDict(original,compare):
|
||||
for key in original:
|
||||
assert key in compare
|
||||
assert compare[key]==original[key]
|
||||
if type(compare[key]) is not dict:
|
||||
assert compare[key]==original[key]
|
||||
else:
|
||||
compareDict(original[key],compare[key])
|
||||
|
||||
## Tweet retrieve tests ##
|
||||
def test_textTweetExtract():
|
||||
@ -66,6 +73,11 @@ def test_multimediaTweetExtract():
|
||||
assert video["media_url_https"]=="https://pbs.twimg.com/media/EfJ-aHlU0AAU1kq.jpg"
|
||||
assert video["type"]=="photo"
|
||||
|
||||
def test_pollTweetExtract():
|
||||
tweet = twExtract.extractStatus("https://twitter.com/norm/status/651169346518056960")
|
||||
assert 'card' in tweet
|
||||
compareDict(testPoll_comparePoll,tweet['card'])
|
||||
|
||||
## VNF conversion test ##
|
||||
|
||||
def test_textTweetVNF():
|
||||
@ -85,6 +97,10 @@ def test_multimediaTweetVNF():
|
||||
vnf = twitfix.link_to_vnf_from_unofficial_api(testMultiMediaTweet)
|
||||
compareDict(testMultiMedia_compare,vnf)
|
||||
|
||||
def test_pollTweetVNF():
|
||||
vnf = twitfix.link_to_vnf_from_unofficial_api(testPollTweet)
|
||||
compareDict(testPoll_comparePollVNF,vnf['poll'])
|
||||
|
||||
## Test adding to cache ; cache should be empty ##
|
||||
def test_addToCache():
|
||||
cache.clearCache()
|
||||
|
61
twitfix.py
61
twitfix.py
@ -248,7 +248,7 @@ def embed_video(video_link, image=0): # Return Embed from any tweet link
|
||||
return message(msgs.failedToScan+msgs.failedToScanExtra+e)
|
||||
return message(msgs.failedToScan)
|
||||
|
||||
def tweetInfo(url, tweet="", desc="", thumb="", uploader="", screen_name="", pfp="", tweetType="", images="", hits=0, likes=0, rts=0, time="", qrt={}, nsfw=False,ttl=None,verified=False,size={}): # Return a dict of video info with default values
|
||||
def tweetInfo(url, tweet="", desc="", thumb="", uploader="", screen_name="", pfp="", tweetType="", images="", hits=0, likes=0, rts=0, time="", qrt={}, nsfw=False,ttl=None,verified=False,size={},poll=None): # Return a dict of video info with default values
|
||||
if (ttl==None):
|
||||
ttl = getDefaultTTL()
|
||||
vnf = {
|
||||
@ -269,8 +269,11 @@ def tweetInfo(url, tweet="", desc="", thumb="", uploader="", screen_name="", pfp
|
||||
"nsfw" : nsfw,
|
||||
"ttl" : ttl,
|
||||
"verified" : verified,
|
||||
"size" : size
|
||||
"size" : size,
|
||||
"poll" : poll
|
||||
}
|
||||
if (poll is None):
|
||||
del vnf['poll']
|
||||
return vnf
|
||||
|
||||
def link_to_vnf_from_tweet_data(tweet,video_link):
|
||||
@ -323,6 +326,11 @@ def link_to_vnf_from_tweet_data(tweet,video_link):
|
||||
for eurl in tweet['entities']['urls']:
|
||||
text = text.replace(eurl["url"],eurl["expanded_url"])
|
||||
|
||||
if 'card' in tweet and tweet['card']['name'].startswith('poll'):
|
||||
poll=getPollObject(tweet['card'])
|
||||
else:
|
||||
poll=None
|
||||
|
||||
vnf = tweetInfo(
|
||||
url,
|
||||
video_link,
|
||||
@ -338,7 +346,8 @@ def link_to_vnf_from_tweet_data(tweet,video_link):
|
||||
images=imgs,
|
||||
nsfw=nsfw,
|
||||
verified=tweet['user']['verified'],
|
||||
size=size
|
||||
size=size,
|
||||
poll=poll
|
||||
)
|
||||
|
||||
return vnf
|
||||
@ -404,19 +413,14 @@ def embed(video_link, vnf, image):
|
||||
urlLink = urllib.parse.quote(video_link)
|
||||
likeDisplay = msgs.genLikesDisplay(vnf)
|
||||
|
||||
try:
|
||||
if vnf['type'] == "":
|
||||
desc = desc
|
||||
elif vnf['type'] == "Video":
|
||||
desc = desc
|
||||
elif vnf['qrt'] == {}: # Check if this is a QRT and modify the description
|
||||
desc = (desc + likeDisplay)
|
||||
else:
|
||||
qrtDisplay = msgs.genQrtDisplay(vnf["qrt"])
|
||||
desc = (desc + qrtDisplay + likeDisplay)
|
||||
except:
|
||||
vnf['likes'] = 0; vnf['rts'] = 0; vnf['time'] = 0
|
||||
print(' ➤ [ X ] Failed QRT check - old VNF object')
|
||||
if 'poll' in vnf:
|
||||
pollDisplay= msgs.genPollDisplay(vnf['poll'])
|
||||
else:
|
||||
pollDisplay=""
|
||||
|
||||
desc=msgs.formatEmbedDesc(vnf['type'],desc,vnf['qrt'],pollDisplay,likeDisplay)
|
||||
|
||||
print(len(desc))
|
||||
appNamePost = ""
|
||||
if vnf['type'] == "Text": # Change the template based on tweet type
|
||||
template = 'text.html'
|
||||
@ -426,10 +430,10 @@ def embed(video_link, vnf, image):
|
||||
image = vnf['images'][image]
|
||||
template = 'image.html'
|
||||
if vnf['type'] == "Video":
|
||||
urlDesc = urllib.parse.quote(textwrap.shorten(desc, width=220, placeholder="..."))
|
||||
#urlDesc = urllib.parse.quote(textwrap.shorten(desc, width=220, placeholder="..."))
|
||||
template = 'video.html'
|
||||
if vnf['type'] == "":
|
||||
urlDesc = urllib.parse.quote(textwrap.shorten(desc, width=220, placeholder="..."))
|
||||
#urlDesc = urllib.parse.quote(textwrap.shorten(desc, width=220, placeholder="..."))
|
||||
template = 'video.html'
|
||||
|
||||
color = "#7FFFD4" # Green
|
||||
@ -477,6 +481,27 @@ def embedCombinedVnf(video_link,vnf):
|
||||
return getTemplate('image.html',vnf,desc,image,video_link,color,urlDesc,urlUser,urlLink,appNameSuffix=" - View original tweet for full quality")
|
||||
|
||||
|
||||
def getPollObject(card):
|
||||
poll={"total_votes":0,"choices":[]}
|
||||
choiceCount=0
|
||||
if (card["name"]=="poll2choice_text_only"):
|
||||
choiceCount=2
|
||||
elif (card["name"]=="poll3choice_text_only"):
|
||||
choiceCount=3
|
||||
elif (card["name"]=="poll4choice_text_only"):
|
||||
choiceCount=4
|
||||
|
||||
for i in range(0,choiceCount):
|
||||
choice = {"text":card["binding_values"][f"choice{i+1}_label"]["string_value"],"votes":int(card["binding_values"][f"choice{i+1}_count"]["string_value"])}
|
||||
poll["total_votes"]+=choice["votes"]
|
||||
poll["choices"].append(choice)
|
||||
# update each choice with a percentage
|
||||
for choice in poll["choices"]:
|
||||
choice["percent"] = round((choice["votes"]/poll["total_votes"])*100,1)
|
||||
|
||||
return poll
|
||||
|
||||
|
||||
def tweetType(tweet): # Are we dealing with a Video, Image, or Text tweet?
|
||||
if 'extended_entities' in tweet:
|
||||
if 'video_info' in tweet['extended_entities']['media'][0]:
|
||||
|
Loading…
x
Reference in New Issue
Block a user