You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
169 lines
8.3 KiB
169 lines
8.3 KiB
import time |
|
from bitcoinrpc.authproxy import AuthServiceProxy |
|
from functioncache import functioncache,SkipCache |
|
import re |
|
|
|
RE_URL_TWEAK = re.compile('^https?://',re.I) |
|
|
|
def timestamp2iso(t): |
|
return time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(t)) |
|
|
|
class Twister: |
|
def __init__(self,url='http://change:me@127.0.0.1:28332',formatter=None): |
|
self.twister = AuthServiceProxy(url) |
|
self.formatter = formatter |
|
def _format_message(self,message): |
|
return self.formatter and self.formatter(message) or message |
|
def clear_cache(self): #doesn't always work :( Need to figure this out. |
|
db = self.get_user_info._db # It doesn't matter which method. It's the same handler |
|
db.setup(self.get_user_info) # It needs the function to locate know which db (module's name) |
|
db.shelve.clear() |
|
db.shelve.sync() |
|
def _format_reply(self,r): |
|
"gracefully fails if reply is empty" |
|
return r and {"user":self.get_user_info(r['n']),'username':r['n'],'k':r['k']} or {} |
|
def _format_post_info(self,p): |
|
result = { |
|
"height":p['userpost']['height'], |
|
"k":p['userpost']['k'], |
|
"time":timestamp2iso(p['userpost']['time']), |
|
} |
|
if p['userpost'].has_key('rt'): |
|
result.update({ |
|
"message":self._format_message(p['userpost']['rt']['msg']), |
|
"username":p['userpost']['rt']['n'], |
|
#"user":self.get_user_info(p['userpost']['rt']['n']), ### too heavy. we do it in an iframe |
|
"k":p['userpost']['rt']['k'], |
|
"rt_username":p['userpost']['n'], |
|
#"rt_user":self.get_user_info(p['userpost']['n']), ### too heavy. we do it in an iframe |
|
"reply":self._format_reply(p['userpost']['rt'].get('reply',{})), |
|
}) |
|
else: |
|
result.update({ |
|
"message":self._format_message(p['userpost']['msg']), |
|
"username":p['userpost']['n'], |
|
#"user":self.get_user_info(p['userpost']['n']), ### too heavy. we do it in an iframe |
|
"k":p['userpost']['k'], |
|
"reply":self._format_reply(p['userpost'].get('reply',{})), |
|
}) |
|
return result |
|
@functioncache(ignore_instance=True) # Cache forever. One day we'll look at our old avatars and remember how stupid we used to be. |
|
def get_twist(self,username,k): |
|
p = self.twister.dhtget(username,'post{0}'.format(k),'s') |
|
if p: |
|
return self._format_post_info(p[0]['p']['v']) |
|
raise SkipCache("Twist not found @{0}/{1}".format(username,k),{ |
|
"username":"", |
|
#"user":self.get_user_info('nobody'), |
|
"k":0, # maybe something needs this |
|
"lastk":0, # or this |
|
"message":"Twist not found 😭", |
|
"time":"Never" |
|
}) |
|
@functioncache(60,ignore_instance=True) |
|
def get_twist_replies(self,username,k): |
|
return list(reversed([self._format_post_info(r['p']['v']) for r in self.twister.dhtget(username,'replies{0}'.format(k),'m')])) # We show them oldest first |
|
@functioncache(60,ignore_instance=True) |
|
def get_twist_rts(self,username,k): |
|
return [self._format_post_info(r['p']['v']) for r in self.twister.dhtget(username,'rts{0}'.format(k),'m')] |
|
@functioncache(60*15,ignore_instance=True) |
|
def get_user_info(self,username): |
|
if username == 'nobody': |
|
return {"username":"","fullname":"Nobody"} # Username is empty. Easier for mustache. |
|
result = self.twister.dhtget(username,'profile','s') |
|
if not result: |
|
#raise SkipCache("user not found: @{0}".format(username), {"username":username,"fullname":username.capitalize()}) |
|
return {"username":username,"fullname":username.capitalize()} |
|
user = result[0]['p']['v'] |
|
user['username'] = username # handy |
|
if not user.get('fullname'): # happens |
|
user['fullname'] = username.capitalize() # Buddha is in the details |
|
user['bio'] = self._format_message(user.get('bio','')) |
|
# Tweak for "protcol-less" urls |
|
u = user.get('url') |
|
if u and not RE_URL_TWEAK.match(u): |
|
user['url'] = 'http://{0}'.format(u) |
|
try: |
|
user['avatar'] = self.twister.dhtget(username,'avatar','s')[0]['p']['v'] |
|
if user['avatar']=='img/genericPerson.png': # ugly patch |
|
user['avatar'] = None |
|
except: |
|
user['avatar'] = None |
|
#raise SkipCache("couldn't get avatar for @{0}, not caching".format(username),user) |
|
return user |
|
@functioncache(60*5,ignore_instance=True) |
|
def local_user_menu(self,active_user=None): |
|
users = [{'username':'','fullname':'Nobody','active':active_user==''}] |
|
if active_user=='': |
|
active = users[0] |
|
else: |
|
active = None |
|
for u in self.twister.listwalletusers(): |
|
user = self.get_user_info(u) |
|
if active_user==u: |
|
user.update({'active':True}) |
|
active = user |
|
users.append(user) |
|
return {"users":users,"active":active} |
|
@functioncache(60*5,ignore_instance=True) |
|
def get_following(self,localusername): |
|
return [{"username":u} for u in self.twister.getfollowing(localusername)] |
|
@functioncache(60*5,ignore_instance=True) |
|
def get_promoted_posts(self,num=8): |
|
return list(reversed([self._format_post_info(p) for p in self.twister.getspamposts(num)])) # Don't ask me why reversed :) |
|
@functioncache(60,ignore_instance=True) |
|
def get_tag_posts(self,tag): |
|
return [self._format_post_info(p['p']['v']) for p in self.twister.dhtget(tag,'hashtag','m')] |
|
@functioncache(60,ignore_instance=True) |
|
def get_user_feed(self,localusername,num=8): |
|
return [self._format_post_info(p) for p in self.twister.getposts(num,self.get_following(localusername))] |
|
@functioncache(60,ignore_instance=True) |
|
def get_user_mentions(self,localusername): |
|
return [self._format_post_info(p['p']['v']) for p in self.twister.dhtget(localusername,'mention','m')] |
|
@functioncache(60,ignore_instance=True) |
|
def get_user_messages(self,localusername,username=None,num=2): |
|
if username: |
|
raw = self.twister.getdirectmsgs(localusername,num,[{"username":username}]) |
|
else: |
|
raw = self.twister.getdirectmsgs(localusername,num,self.get_following(localusername)) |
|
result =[] |
|
localuser = self.get_user_info(localusername) |
|
for username in raw: |
|
user = self.get_user_info(username) |
|
messages = [] |
|
latest_ts = 0 |
|
for message in raw[username]: |
|
if message['time'] > latest_ts: |
|
latest_ts = message['time'] |
|
message['time'] = timestamp2iso(message['time']) |
|
message['username'] = message['fromMe'] and localusername or username |
|
message['user'] = message['fromMe'] and localuser or user |
|
message['message'] = self._format_message(message['text']) |
|
messages.insert(0,message) # reverse order (newer first) |
|
result.append({'username':username,'user':user,'messages':messages,'latest_ts':latest_ts}) |
|
return sorted(result,key=lambda thread:thread['latest_ts'],reverse=True) |
|
|
|
@functioncache(60,ignore_instance=True) |
|
def get_user_posts(self,username,num=8): |
|
result = [self._format_post_info(p) for p in self.twister.getposts(num,[{'username':username}])] |
|
if result: |
|
return result |
|
else: # We're not following. Let's "knit" the best timeline we can |
|
result = self.twister.dhtget(username,'status','s') |
|
while True: |
|
try: |
|
lastk = result[-1]['p']['v']['userpost']['lastk'] |
|
last = lastk and self.twister.dhtget(username,'post{0}'.format(result[-1]['p']['v']['userpost']['lastk']),'s') |
|
except: |
|
break |
|
if not last: |
|
break |
|
result.append(last[0]) |
|
return [self._format_post_info(s['p']['v']) for s in result] |
|
def get_users_by_partial_name(self,prefix,num=8): |
|
return self.twister.listusernamespartial(prefix,num) |
|
@functioncache(60,ignore_instance=True) |
|
def get_trending_tags(self,num=8): |
|
return self.twister.gettrendinghashtags(num) |
|
def get_info(self): # not cached |
|
return self.twister.getinfo()
|
|
|