Add user embeds and API support, closing #65
This commit is contained in:
parent
8554a733de
commit
88f5ed6780
19
templates/user.html
Normal file
19
templates/user.html
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% block head %}
|
||||||
|
<meta property="og:image" content="{{ pfp }}" />
|
||||||
|
|
||||||
|
<meta name="twitter:card" content="tweet" />
|
||||||
|
|
||||||
|
<meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
|
||||||
|
<meta content="{{ color }}" name="theme-color" />
|
||||||
|
<meta property="og:site_name" content="{{ appname }}">
|
||||||
|
|
||||||
|
<meta name="twitter:title" content="{{ user['name'] }} (@{{ user['screen_name'] }})" />
|
||||||
|
|
||||||
|
<meta name="twitter:image" content="{{ user['profile_image_url'] }}" />
|
||||||
|
<meta name="twitter:creator" content="@{{ user['name'] }}" />
|
||||||
|
|
||||||
|
<meta property="og:description" content="{{ desc }}" />
|
||||||
|
|
||||||
|
<link rel="alternate" href="{{ host }}/oembed.json?desc={{ urlUser }}&user=Twitter&link={{ link }}&ttype=link&provider={{ appname|urlencode }}" type="application/json+oembed" title="{{ user['name'] }}">
|
||||||
|
<meta http-equiv="refresh" content="1; url = {{ tweetLink }}" /> {% endblock %} {% block body %} Redirecting you to the tweet in a moment. <a href="{{ link }}">Or click here.</a> {% endblock %}
|
@ -35,4 +35,10 @@ def test_api_include_rtf_nomedia():
|
|||||||
resp = client.get(testMediaTweet.replace("https://twitter.com","https://api.vxtwitter.com")+"?include_rtf=ifnomedia",headers={"User-Agent":"test"})
|
resp = client.get(testMediaTweet.replace("https://twitter.com","https://api.vxtwitter.com")+"?include_rtf=ifnomedia",headers={"User-Agent":"test"})
|
||||||
jData = resp.get_json()
|
jData = resp.get_json()
|
||||||
assert resp.status_code==200
|
assert resp.status_code==200
|
||||||
assert not any(".rtf" in i for i in jData["mediaURLs"])
|
assert not any(".rtf" in i for i in jData["mediaURLs"])
|
||||||
|
|
||||||
|
def test_api_user():
|
||||||
|
resp = client.get(testUser.replace("https://twitter.com","https://api.vxtwitter.com")+"?include_rtf=true",headers={"User-Agent":"test"})
|
||||||
|
jData = resp.get_json()
|
||||||
|
assert resp.status_code==200
|
||||||
|
assert jData["screen_name"]=="jack"
|
@ -40,6 +40,11 @@ def test_embed_FromScratch():
|
|||||||
client.get(testMediaTweet.replace("https://twitter.com",""),headers={"User-Agent":"test"})
|
client.get(testMediaTweet.replace("https://twitter.com",""),headers={"User-Agent":"test"})
|
||||||
client.get(testMultiMediaTweet.replace("https://twitter.com",""),headers={"User-Agent":"test"})
|
client.get(testMultiMediaTweet.replace("https://twitter.com",""),headers={"User-Agent":"test"})
|
||||||
|
|
||||||
|
def test_embed_User():
|
||||||
|
cache.clearCache()
|
||||||
|
resp = client.get(testUser.replace("https://twitter.com",""),headers={"User-Agent":"test"})
|
||||||
|
assert resp.status_code==200
|
||||||
|
|
||||||
def test_embed_FromCache():
|
def test_embed_FromCache():
|
||||||
cache.clearCache()
|
cache.clearCache()
|
||||||
twitfix.getTweetData(testTextTweet)
|
twitfix.getTweetData(testTextTweet)
|
||||||
|
38
twitfix.py
38
twitfix.py
@ -16,7 +16,7 @@ import twExtract as twExtract
|
|||||||
from cache import addVnfToLinkCache,getVnfFromLinkCache
|
from cache import addVnfToLinkCache,getVnfFromLinkCache
|
||||||
import vxlogging as log
|
import vxlogging as log
|
||||||
from utils import getTweetIdFromUrl, pathregex
|
from utils import getTweetIdFromUrl, pathregex
|
||||||
from vxApi import getApiResponse
|
from vxApi import getApiResponse, getApiUserResponse
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
from PyRTF.Elements import Document
|
from PyRTF.Elements import Document
|
||||||
from PyRTF.document.section import Section
|
from PyRTF.document.section import Section
|
||||||
@ -138,6 +138,17 @@ def renderArticleTweetEmbed(tweetData,appnameSuffix=""):
|
|||||||
color=config['config']['color']
|
color=config['config']['color']
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def renderUserEmbed(userData,appnameSuffix=""):
|
||||||
|
return render_template("user.html",
|
||||||
|
user=userData,
|
||||||
|
host=config['config']['url'],
|
||||||
|
desc=userData["description"],
|
||||||
|
urlEncodedDesc=urllib.parse.quote(userData["description"]),
|
||||||
|
link=f'https://twitter.com/{userData["screen_name"]}',
|
||||||
|
appname=config['config']['appname'],
|
||||||
|
color=config['config']['color']
|
||||||
|
)
|
||||||
|
|
||||||
@app.route('/robots.txt')
|
@app.route('/robots.txt')
|
||||||
def robots():
|
def robots():
|
||||||
return "User-agent: *\nDisallow: /"
|
return "User-agent: *\nDisallow: /"
|
||||||
@ -181,6 +192,11 @@ def getTweetData(twitter_url,include_txt="false",include_rtf="false"):
|
|||||||
addVnfToLinkCache(twitter_url,tweetData)
|
addVnfToLinkCache(twitter_url,tweetData)
|
||||||
return tweetData
|
return tweetData
|
||||||
|
|
||||||
|
def getUserData(twitter_url):
|
||||||
|
rawUserData = twExtract.extractUser(twitter_url,workaroundTokens=config['config']['workaroundTokens'].split(','))
|
||||||
|
userData = getApiUserResponse(rawUserData)
|
||||||
|
return userData
|
||||||
|
|
||||||
def determineEmbedTweet(tweetData):
|
def determineEmbedTweet(tweetData):
|
||||||
# Determine which tweet, i.e main or QRT, to embed the media from.
|
# Determine which tweet, i.e main or QRT, to embed the media from.
|
||||||
# if there is no QRT, return the main tweet => default behavior
|
# if there is no QRT, return the main tweet => default behavior
|
||||||
@ -196,6 +212,7 @@ def determineEmbedTweet(tweetData):
|
|||||||
|
|
||||||
@app.route('/<path:sub_path>') # Default endpoint used by everything
|
@app.route('/<path:sub_path>') # Default endpoint used by everything
|
||||||
def twitfix(sub_path):
|
def twitfix(sub_path):
|
||||||
|
isApiRequest=request.url.startswith("https://api.vx") or request.url.startswith("http://api.vx")
|
||||||
if sub_path in staticFiles:
|
if sub_path in staticFiles:
|
||||||
if 'path' not in staticFiles[sub_path] or staticFiles[sub_path]["path"] == None:
|
if 'path' not in staticFiles[sub_path] or staticFiles[sub_path]["path"] == None:
|
||||||
staticFiles[sub_path]["path"] = sub_path
|
staticFiles[sub_path]["path"] = sub_path
|
||||||
@ -204,9 +221,26 @@ def twitfix(sub_path):
|
|||||||
sub_path = "i/" + sub_path
|
sub_path = "i/" + sub_path
|
||||||
match = pathregex.search(sub_path)
|
match = pathregex.search(sub_path)
|
||||||
if match is None:
|
if match is None:
|
||||||
|
# test for .com/username
|
||||||
|
if sub_path.count("/") == 0:
|
||||||
|
username=sub_path
|
||||||
|
extra=None
|
||||||
|
else:
|
||||||
|
# get first subpath
|
||||||
|
username=sub_path.split("/")[0]
|
||||||
|
extra = sub_path.split("/")[1]
|
||||||
|
if extra in [None,"with_replies","media","likes","highlights","superfollows","media"] and username != "" and username != None:
|
||||||
|
userData = getUserData(f"https://twitter.com/{username}")
|
||||||
|
if isApiRequest:
|
||||||
|
if userData is None:
|
||||||
|
abort(404)
|
||||||
|
return userData
|
||||||
|
else:
|
||||||
|
if userData is None:
|
||||||
|
return message(msgs.failedToScan)
|
||||||
|
return renderUserEmbed(userData)
|
||||||
abort(404)
|
abort(404)
|
||||||
twitter_url = f'https://twitter.com/i/status/{getTweetIdFromUrl(sub_path)}'
|
twitter_url = f'https://twitter.com/i/status/{getTweetIdFromUrl(sub_path)}'
|
||||||
isApiRequest=request.url.startswith("https://api.vx") or request.url.startswith("http://api.vx")
|
|
||||||
|
|
||||||
include_txt="false"
|
include_txt="false"
|
||||||
include_rtf="false"
|
include_rtf="false"
|
||||||
|
15
vxApi.py
15
vxApi.py
@ -3,6 +3,21 @@ from datetime import datetime
|
|||||||
from configHandler import config
|
from configHandler import config
|
||||||
from utils import stripEndTCO
|
from utils import stripEndTCO
|
||||||
|
|
||||||
|
def getApiUserResponse(user):
|
||||||
|
return {
|
||||||
|
"id": user["id"],
|
||||||
|
"screen_name": user["screen_name"],
|
||||||
|
"name": user["name"],
|
||||||
|
"profile_image_url": user["profile_image_url_https"],
|
||||||
|
"description": user["description"],
|
||||||
|
"location": user["location"],
|
||||||
|
"followers_count": user["followers_count"],
|
||||||
|
"following_count": user["friends_count"],
|
||||||
|
"tweet_count": user["statuses_count"],
|
||||||
|
"created_at": user["created_at"],
|
||||||
|
"protected": user["protected"],
|
||||||
|
}
|
||||||
|
|
||||||
def getApiResponse(tweet,include_txt=False,include_rtf=False):
|
def getApiResponse(tweet,include_txt=False,include_rtf=False):
|
||||||
tweetL = tweet["legacy"]
|
tweetL = tweet["legacy"]
|
||||||
if "user_result" in tweet["core"]:
|
if "user_result" in tweet["core"]:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user