Add full FB API (Graph API) support
This commit is contained in:
		
							
								
								
									
										80
									
								
								feedify.py
									
									
									
									
									
								
							
							
						
						
									
										80
									
								
								feedify.py
									
									
									
									
									
								
							@@ -11,6 +11,8 @@ import lxml.html
 | 
				
			|||||||
import json
 | 
					import json
 | 
				
			||||||
import urlparse
 | 
					import urlparse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def toclass(query):
 | 
					def toclass(query):
 | 
				
			||||||
	pattern = r'\[class=([^\]]+)\]'
 | 
						pattern = r'\[class=([^\]]+)\]'
 | 
				
			||||||
	repl = r'[@class and contains(concat(" ", normalize-space(@class), " "), " \1 ")]'
 | 
						repl = r'[@class and contains(concat(" ", normalize-space(@class), " "), " \1 ")]'
 | 
				
			||||||
@@ -59,7 +61,6 @@ def formatString(string, getter, error=False):
 | 
				
			|||||||
	elif re.search(r'^([^{}<>" ]+)(?:<"([^>]+)">)?(.*)$', string):
 | 
						elif re.search(r'^([^{}<>" ]+)(?:<"([^>]+)">)?(.*)$', string):
 | 
				
			||||||
		match = re.search(r'^([^{}<>" ]+)(?:<"([^>]+)">)?(.*)$', string).groups()
 | 
							match = re.search(r'^([^{}<>" ]+)(?:<"([^>]+)">)?(.*)$', string).groups()
 | 
				
			||||||
		rawValue = getter(match[0])
 | 
							rawValue = getter(match[0])
 | 
				
			||||||
		print repr(rawValue)
 | 
					 | 
				
			||||||
		if not isinstance(rawValue, basestring):
 | 
							if not isinstance(rawValue, basestring):
 | 
				
			||||||
			if match[1] is not None:
 | 
								if match[1] is not None:
 | 
				
			||||||
				out = match[1].join(rawValue)
 | 
									out = match[1].join(rawValue)
 | 
				
			||||||
@@ -76,9 +77,69 @@ def formatString(string, getter, error=False):
 | 
				
			|||||||
	else:
 | 
						else:
 | 
				
			||||||
		return out
 | 
							return out
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def PreWorker(url, cache):
 | 
				
			||||||
 | 
						if urlparse.urlparse(url).netloc == 'graph.facebook.com':
 | 
				
			||||||
 | 
							facebook = cache.new('facebook', True)
 | 
				
			||||||
 | 
							token = urlparse.parse_qs(urlparse.urlparse(url).query)['access_token'][0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if 't'+token not in facebook:
 | 
				
			||||||
 | 
								# this token ain't known, look for info about it
 | 
				
			||||||
 | 
								eurl = "https://graph.facebook.com/debug_token?input_token={token}&access_token={app_token}".format(token=token, app_token=morss.FBAPPTOKEN)
 | 
				
			||||||
 | 
								data = json.loads(urllib2.urlopen(eurl).read())['data']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								app_id = str(data['app_id'])
 | 
				
			||||||
 | 
								user_id = str(data['user_id'])
 | 
				
			||||||
 | 
								expires = data['expires_at']
 | 
				
			||||||
 | 
								short = 'issued_at' not in data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								facebook.set('t'+token, user_id)
 | 
				
			||||||
 | 
								facebook.set('e'+token, expires)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								good = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								# do some woodoo to know if we already have sth better
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if 'u'+user_id not in facebook:
 | 
				
			||||||
 | 
									# grab a new one anyway, new user
 | 
				
			||||||
 | 
									facebook.set('o'+user_id, token)
 | 
				
			||||||
 | 
									good = True
 | 
				
			||||||
 | 
								else:
 | 
				
			||||||
 | 
									# maybe it's a better one
 | 
				
			||||||
 | 
									last = facebook.get('u'+user_id)
 | 
				
			||||||
 | 
									last_expires = facebook.get('e'+last, int)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if expires > last_expires:
 | 
				
			||||||
 | 
										# new is better
 | 
				
			||||||
 | 
										good = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if good and short and app_id == morss.FBAPPID:
 | 
				
			||||||
 | 
									eurl = "https://graph.facebook.com/oauth/access_token?grant_type=fb_exchange_token&client_id={app_id}&client_secret={app_secret}&fb_exchange_token={short_lived_token}".format(app_id=morss.FBAPPID, app_secret=morss.FBSECRET, short_lived_token=token)
 | 
				
			||||||
 | 
									values = urlparse.parse_qs(urllib2.urlopen(eurl).read().strip())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									token = values['access_token'][0]
 | 
				
			||||||
 | 
									expires = int(time.time() + int(values['expires'][0]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									facebook.set('t'+token, user_id)
 | 
				
			||||||
 | 
									facebook.set('e'+token, expires)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if good:
 | 
				
			||||||
 | 
									facebook.set('u'+user_id, token)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							# hey look for a newer token and use it
 | 
				
			||||||
 | 
							token = urlparse.parse_qs(urlparse.urlparse(url).query)['access_token'][0]
 | 
				
			||||||
 | 
							user_id = facebook.get('t'+token)
 | 
				
			||||||
 | 
							last = facebook.get('u'+user_id)
 | 
				
			||||||
 | 
							original = facebook.get('o'+user_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							nurl = url.replace(token, last)
 | 
				
			||||||
 | 
							ncache = url.replace(token, original)
 | 
				
			||||||
 | 
							cache.set('redirect', nurl)
 | 
				
			||||||
 | 
							cache.set('cache', ncache)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Builder(object):
 | 
					class Builder(object):
 | 
				
			||||||
	def __init__(self, link, data=None):
 | 
						def __init__(self, link, data=None, cache=False):
 | 
				
			||||||
		self.link = link
 | 
							self.link = link
 | 
				
			||||||
 | 
							self.cache = cache
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if data is None:
 | 
							if data is None:
 | 
				
			||||||
			data = urllib2.urlopen(link).read()
 | 
								data = urllib2.urlopen(link).read()
 | 
				
			||||||
@@ -167,3 +228,18 @@ class Builder(object):
 | 
				
			|||||||
						feedItem['updated'] = self.string(item, 'item_time')
 | 
											feedItem['updated'] = self.string(item, 'item_time')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					self.feed.items.append(feedItem)
 | 
										self.feed.items.append(feedItem)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if urlparse.urlparse(self.link).netloc == 'graph.facebook.com':
 | 
				
			||||||
 | 
								if self.cache:
 | 
				
			||||||
 | 
									facebook = self.cache.new('facebook', True)
 | 
				
			||||||
 | 
									token = urlparse.parse_qs(urlparse.urlparse(self.link).query)['access_token'][0]
 | 
				
			||||||
 | 
									expires = facebook.get('e'+token, int)
 | 
				
			||||||
 | 
									lifespan = expires - time.time()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if lifespan < 5*24*3600:
 | 
				
			||||||
 | 
										new = self.feed.items.append()
 | 
				
			||||||
 | 
										new.title = "APP AUTHORISATION RENEWAL NEEDED"
 | 
				
			||||||
 | 
										new.link = "https://www.facebook.com/dialog/oauth?client_id={app_id}&redirect_uri=http://test.morss.it/:facebook/".format(app_id=morss.FBAPPID)
 | 
				
			||||||
 | 
										new.desc = "Please renew your Facebook app token for this app to keep working for this feed.<br/><a href='{}'>Go!</a>".format(new.link)
 | 
				
			||||||
 | 
										new.time = cache.get(expires, int)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										56
									
								
								morss.py
									
									
									
									
									
								
							
							
						
						
									
										56
									
								
								morss.py
									
									
									
									
									
								
							@@ -42,6 +42,10 @@ UA_HTML = 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.11) G
 | 
				
			|||||||
MIMETYPE = {	'xml':	['text/xml', 'application/xml', 'application/rss+xml', 'application/rdf+xml', 'application/atom+xml'],
 | 
					MIMETYPE = {	'xml':	['text/xml', 'application/xml', 'application/rss+xml', 'application/rdf+xml', 'application/atom+xml'],
 | 
				
			||||||
				'html':	['text/html', 'application/xhtml+xml']}
 | 
									'html':	['text/html', 'application/xhtml+xml']}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FBAPPID = "<insert yours>"
 | 
				
			||||||
 | 
					FBSECRET = "<insert yours>"
 | 
				
			||||||
 | 
					FBAPPTOKEN = FBAPPID + '|' + FBSECRET
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PROTOCOL = ['http', 'https', 'ftp']
 | 
					PROTOCOL = ['http', 'https', 'ftp']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if 'REQUEST_URI' in os.environ:
 | 
					if 'REQUEST_URI' in os.environ:
 | 
				
			||||||
@@ -360,9 +364,14 @@ def Fill(item, cache, feedurl='/', fast=False):
 | 
				
			|||||||
		else:
 | 
							else:
 | 
				
			||||||
			link = None
 | 
								link = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	# facebook, do nothing for now FIXME
 | 
						# facebook
 | 
				
			||||||
	if urlparse.urlparse(feedurl).netloc == 'graph.facebook.com':
 | 
						if urlparse.urlparse(feedurl).netloc == 'graph.facebook.com':
 | 
				
			||||||
		link = None
 | 
							match = lxml.html.fromstring(item.content).xpath('//a/@href')
 | 
				
			||||||
 | 
							if len(match) and urlparse.urlparse(match[0]).netloc != 'www.facebook.com':
 | 
				
			||||||
 | 
								link = match[0]
 | 
				
			||||||
 | 
								log(link)
 | 
				
			||||||
 | 
							else:
 | 
				
			||||||
 | 
								link = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if link is None:
 | 
						if link is None:
 | 
				
			||||||
		log('no used link')
 | 
							log('no used link')
 | 
				
			||||||
@@ -423,6 +432,9 @@ def Gather(url, cachePath, options):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	log(cache._hash)
 | 
						log(cache._hash)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						# do some useful facebook work
 | 
				
			||||||
 | 
						feedify.PreWorker(url, cache)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if 'redirect' in cache:
 | 
						if 'redirect' in cache:
 | 
				
			||||||
		url = cache.get('redirect')
 | 
							url = cache.get('redirect')
 | 
				
			||||||
		log('url redirect')
 | 
							log('url redirect')
 | 
				
			||||||
@@ -466,7 +478,7 @@ def Gather(url, cachePath, options):
 | 
				
			|||||||
	if style == 'normal':
 | 
						if style == 'normal':
 | 
				
			||||||
		rss = feeds.parse(xml)
 | 
							rss = feeds.parse(xml)
 | 
				
			||||||
	elif style == 'feedify':
 | 
						elif style == 'feedify':
 | 
				
			||||||
		feed = feedify.Builder(url, xml)
 | 
							feed = feedify.Builder(url, xml, cache)
 | 
				
			||||||
		feed.build()
 | 
							feed.build()
 | 
				
			||||||
		rss = feed.feed
 | 
							rss = feed.feed
 | 
				
			||||||
	elif style == 'html':
 | 
						elif style == 'html':
 | 
				
			||||||
@@ -526,7 +538,7 @@ if __name__ == '__main__':
 | 
				
			|||||||
		HOLD = True
 | 
							HOLD = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if 'HTTP_IF_NONE_MATCH' in os.environ:
 | 
							if 'HTTP_IF_NONE_MATCH' in os.environ:
 | 
				
			||||||
			if not options.force and time.time() - int(os.environ['HTTP_IF_NONE_MATCH'][1:-1]) < DELAY:
 | 
								if not options.force and not options.facebook and time.time() - int(os.environ['HTTP_IF_NONE_MATCH'][1:-1]) < DELAY:
 | 
				
			||||||
				print 'Status: 304'
 | 
									print 'Status: 304'
 | 
				
			||||||
				print
 | 
									print
 | 
				
			||||||
				log(url)
 | 
									log(url)
 | 
				
			||||||
@@ -538,6 +550,42 @@ if __name__ == '__main__':
 | 
				
			|||||||
		else:
 | 
							else:
 | 
				
			||||||
			cachePath = os.path.expanduser('~') + '/.cache/morss'
 | 
								cachePath = os.path.expanduser('~') + '/.cache/morss'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if options.facebook:
 | 
				
			||||||
 | 
							facebook = Cache(cachePath, 'facebook', True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							# get real token from code
 | 
				
			||||||
 | 
							code = urlparse.parse_qs(urlparse.urlparse(url).query)['code'][0]
 | 
				
			||||||
 | 
							eurl = "https://graph.facebook.com/oauth/access_token?client_id={app_id}&redirect_uri={redirect_uri}&client_secret={app_secret}&code={code_parameter}".format(app_id=FBAPPID, app_secret=FBSECRET, code_parameter=code, redirect_uri="http://test.morss.it/:facebook/")
 | 
				
			||||||
 | 
							token = urlparse.parse_qs(urllib2.urlopen(eurl).read().strip())['access_token'][0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							# get long-lived access token
 | 
				
			||||||
 | 
							eurl = "https://graph.facebook.com/oauth/access_token?grant_type=fb_exchange_token&client_id={app_id}&client_secret={app_secret}&fb_exchange_token={short_lived_token}".format(app_id=FBAPPID, app_secret=FBSECRET, short_lived_token=token)
 | 
				
			||||||
 | 
							values = urlparse.parse_qs(urllib2.urlopen(eurl).read().strip())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ltoken = values['access_token'][0]
 | 
				
			||||||
 | 
							expires = int(time.time() + int(values['expires'][0]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							# get user id
 | 
				
			||||||
 | 
							iurl = "https://graph.facebook.com/me?fields=id&access_token={token}".format(ltoken)
 | 
				
			||||||
 | 
							user_id = json.loads(urllib2.urlopen(iurl).read())['id']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							# do sth out of it
 | 
				
			||||||
 | 
							facebook.set('t'+ltoken, user_id)
 | 
				
			||||||
 | 
							facebook.set('e'+ltoken, expires)
 | 
				
			||||||
 | 
							facebook.set('u'+user_id, ltoken)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if 'o'+user_id not in token:
 | 
				
			||||||
 | 
								facebook.set('o'+user_id, ltoken)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if 'REQUEST_URI' in os.environ:
 | 
				
			||||||
 | 
								print 'Status: 200'
 | 
				
			||||||
 | 
								print 'Content-Type: text/plain'
 | 
				
			||||||
 | 
								print ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							print "token updated"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							sys.exit(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if 'REQUEST_URI' in os.environ:
 | 
						if 'REQUEST_URI' in os.environ:
 | 
				
			||||||
		print 'Status: 200'
 | 
							print 'Status: 200'
 | 
				
			||||||
		print 'ETag: "%s"' % int(time.time())
 | 
							print 'ETag: "%s"' % int(time.time())
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user