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.
212 lines
10 KiB
212 lines
10 KiB
# -*- coding: utf-8 -*- |
|
import cherrypy, pystache |
|
from twister import Twister |
|
from appdir import APPDIR |
|
|
|
stache = pystache.Renderer( |
|
search_dirs='{0}/templates'.format(APPDIR),file_encoding='utf-8',string_encoding='utf-8',file_extension='html') |
|
|
|
### twistparser (format users, urls, etc. in text). Subclasses twitter-text-python |
|
|
|
from ttp import ttp |
|
class TwistParser(ttp.Parser): |
|
def format_tag(self, tag, text): |
|
'''Return formatted HTML for a hashtag.''' |
|
return '<a target="_top" href="{0}/tag/{1}">{2}{3}</a>'.format( |
|
cherrypy.request.base+cherrypy.request.script_name, |
|
ttp.urllib.quote(text.lower().encode('utf-8'),'xmlcharrefreplace'), tag, text) |
|
|
|
def format_username(self, at_char, user): |
|
'''Return formatted HTML for a username.''' |
|
return '<a target="_top" href="{0}/user/{1}">{2}{3}</a>'.format( |
|
cherrypy.request.base+cherrypy.request.script_name, |
|
user, at_char, user.lower()) |
|
|
|
def format_list(self, at_char, user, list_name): |
|
'''We don't have lists, so we see it as "@user" followed by "/something"''' |
|
return '<a target="_top" href="{0}/user/{1}">{2}{3}</a>/{4}'.format( |
|
cherrypy.request.base+cherrypy.request.script_name, |
|
cherrypy.request.base+cherrypy.request.script_name, user, at_char, user.lower(), list_name) |
|
|
|
def format_url(self, url, text): |
|
'''Return formatted HTML for a url.''' |
|
return '<a target="_blank" rel="nofollow" href="{0}">{1}</a>'.format(ttp.escape(url), text) |
|
|
|
twistparser = TwistParser() |
|
|
|
def format_twist(message): |
|
return twistparser.parse(message).html |
|
|
|
def format_trending(twister,num_messages=8): |
|
# ttp would only parse (for example) "#two" in "#two-words". rsplit removes the "-words" (sucks a bit less) |
|
return [html.rsplit('>',1)[0]+'>' for html in [ |
|
format_twist(u'#{0}'.format(t)) for t in twister.get_trending_tags(num_messages) |
|
] if html.find('>')>0] # the if is because ttf doesn't do unicode tags (TODO: find a better ttp fork or find/write some other lib) |
|
|
|
### The Swizzler app |
|
class SwizzlerApp(object): |
|
@cherrypy.expose |
|
def search(self,q=''): |
|
conf = cherrypy.request.app.config['swizzler'] |
|
twister = Twister(conf['rpc_url'],format_twist) |
|
result = {'site_root':cherrypy.request.base+cherrypy.request.script_name} |
|
q = q.strip().split(' ')[0] # ignore anything after the first space if any |
|
if q.startswith('#'): # user said "#sometag", change to "sometag" |
|
q=q[1:] |
|
if q and not q.startswith('@'): # Tag. redirect. |
|
raise cherrypy.HTTPRedirect(result['site_root']+'/tag/{0}'.format(q)) |
|
if q: |
|
result['user_prefix'] = q |
|
result['users'] = twister.get_users_by_partial_name(q[1:],conf['num_messages']) |
|
else: |
|
result['trending'] = format_trending(twister,conf['num_messages']) |
|
return stache.render(stache.load_template('search'),result) |
|
|
|
@cherrypy.expose |
|
def twist(self,username,k): |
|
conf = cherrypy.request.app.config['swizzler'] |
|
twister = Twister(conf['rpc_url'],format_twist) |
|
twist = twister.get_twist(username,k) |
|
twist['style_large'] = True |
|
rts = twister.get_twist_rts(username,k) |
|
result = { |
|
'is_twist':True, |
|
'title':u"@{0}: {1} - Swizzler".format(username,twist['time']), |
|
'twist':twist, |
|
'in_reply_to':twist.get('reply') and twister.get_twist(twist['reply']['username'],twist['reply']['k']) or None, |
|
'replies':twister.get_twist_replies(username,k), |
|
'rts':rts, |
|
'any_rts':not not rts, |
|
'local_users':twister.local_user_menu()['users'], |
|
'info':twister.get_info(), |
|
'site_root':cherrypy.request.base+cherrypy.request.script_name, |
|
} |
|
return stache.render(stache.load_template('twist'),result) |
|
@cherrypy.expose |
|
def user(self,username): |
|
conf = cherrypy.request.app.config['swizzler'] |
|
twister = Twister(conf['rpc_url'],format_twist) |
|
user = twister.get_user_info(username) |
|
messages = twister.get_user_posts(username,conf['num_messages']) |
|
result = { |
|
'is_user':True, |
|
'title':u"{fullname} (@{username}): Profile - Swizzler".format(**user), |
|
'subject':user, |
|
'messages':messages, |
|
'any_messages':not not messages, |
|
'local_users':twister.local_user_menu()['users'], |
|
'info':twister.get_info(), |
|
'site_root':cherrypy.request.base+cherrypy.request.script_name, |
|
} |
|
return stache.render(stache.load_template('standard'),result) |
|
@cherrypy.expose |
|
def user_embed(self,username='nobody',style='normal'): |
|
if username=='nobody': username='' # to enable /nobody/large |
|
conf = cherrypy.request.app.config['swizzler'] |
|
twister = Twister(conf['rpc_url'],format_twist) |
|
user = twister.get_user_info(username) |
|
result = { |
|
'title':'@{0} - Swizzler'.format(username), |
|
'site_root':cherrypy.request.base+cherrypy.request.script_name, |
|
'user':twister.get_user_info(username) |
|
} |
|
result['style_{0}'.format(style)] = True |
|
return stache.render(stache.load_template('user-iframe'),result) |
|
@cherrypy.expose |
|
def tag(self,tag): |
|
conf = cherrypy.request.app.config['swizzler'] |
|
twister = Twister(conf['rpc_url'],format_twist) |
|
messages = twister.get_tag_posts(tag) |
|
result = { |
|
'is_tag':True, |
|
'title':u"#{0} - Swizzler".format(tag), |
|
'subject':{"fullname":tag}, |
|
'messages':messages, |
|
'any_messages':not not messages, |
|
'local_users':twister.local_user_menu()['users'], |
|
'info':twister.get_info(), |
|
'site_root':cherrypy.request.base+cherrypy.request.script_name, |
|
} |
|
return stache.render(stache.load_template('standard'),result) |
|
@cherrypy.expose |
|
def home(self,localusername,mode='feed'): |
|
conf = cherrypy.request.app.config['swizzler'] |
|
twister = Twister(conf['rpc_url'],format_twist) |
|
menu = twister.local_user_menu(localusername) |
|
if mode=='mentions': |
|
messages = twister.get_user_mentions(localusername) |
|
else: |
|
messages = twister.get_user_feed(localusername,conf['num_messages']) |
|
result = { |
|
'is_home':True, |
|
'is_mentions':mode=='mentions', |
|
'is_feed':mode!='mentions', |
|
'title':u"{fullname} (@{username}): {mode} - Swizzler".format(mode=mode=='mentions' and 'Mentions' or 'Home',**menu['active']), |
|
'local_users':menu['users'], |
|
'info':twister.get_info(), |
|
'subject':menu['active'], |
|
'messages':messages, |
|
'any_messages':not not messages, |
|
'site_root':cherrypy.request.base+cherrypy.request.script_name, |
|
} |
|
return stache.render(stache.load_template('standard'),result) |
|
@cherrypy.expose |
|
def messages(self,localusername,remoteusername=None): |
|
conf = cherrypy.request.app.config['swizzler'] |
|
twister = Twister(conf['rpc_url'],format_twist) |
|
localuser = twister.get_user_info(localusername) |
|
remoteuser = remoteusername and twister.get_user_info(remoteusername) or None |
|
threads = remoteusername and twister.get_user_messages(localusername,remoteusername,conf['num_messages']) or twister.get_user_messages(localusername) |
|
result = { |
|
'title':u"{0} (@{1}): direct messages{2}".format( |
|
localuser['fullname'],localuser['username'], |
|
remoteuser and u" with {fullname} (@{username}) - Swizzler".format(**remoteuser) or ""), |
|
'subject':localuser, |
|
'remoteuser':remoteuser, |
|
'threads':threads, |
|
'any_threads':not not threads, |
|
'local_users':twister.local_user_menu()['users'], |
|
'info':twister.get_info(), |
|
'site_root':cherrypy.request.base+cherrypy.request.script_name, |
|
} |
|
return stache.render(stache.load_template('messages'),result) |
|
@cherrypy.expose |
|
def index(self): |
|
conf = cherrypy.request.app.config['swizzler'] |
|
twister = Twister(conf['rpc_url'],format_twist) |
|
messages = twister.get_sponsored_posts(conf['num_messages']) |
|
result = { |
|
'is_user':True, # i.e. we want to display "bio" and not mentions/DMs/profile buttons |
|
'is_sponsored':True, # message template needs to know not to show "permalink" |
|
'title':"Welcome to Swizzler", |
|
'local_users':twister.local_user_menu('')['users'], # '' means: "Nobody" is active |
|
'info':twister.get_info(), |
|
'subject':{ # pseudo-user describing sponsored posts |
|
'fullname':'Sponsored posts', |
|
'bio':format_twist(""" |
|
Mining the twister blockchain protects the #twister-verse from attacks like http://twister.net.co/?p=236 |
|
but unlike doge, we don't have shiny coins to offer "our protectors". |
|
Instead, they enjoy occasional minutes of fame in the form of the sponsored posts you see here. |
|
We #Respect their hard earned crypto-graffiti by appreciating them on coffee/spliff/soy-milk/etc. breaks, because that's how we roll yo. |
|
Start mining today, and all this (AND moral satisfaction) can be yours.""") |
|
}, |
|
'messages':messages, |
|
'any_messages':not not messages, |
|
'site_root':cherrypy.request.base+cherrypy.request.script_name, |
|
} |
|
return stache.render(stache.load_template('standard'),result) |
|
|
|
if __name__ == '__main__': |
|
cherrypy.config.update('{0}/cherrypy.config'.format(APPDIR)) |
|
app = SwizzlerApp() |
|
cherrypy.tree.mount(app,'/',config='{0}/cherrypy.config'.format(APPDIR)) |
|
conf = cherrypy.tree.apps[''].config |
|
u,p = conf['swizzler'].get('browser_user'),conf['swizzler'].get('browser_password') |
|
print u,p |
|
if u and p: |
|
conf['/'].update({ 'tools.basic_auth.on': True, |
|
'tools.basic_auth.realm': 'Swizzler VIP lounge', |
|
'tools.basic_auth.users': {u:p}, |
|
'tools.basic_auth.encrypt': lambda x: x}) |
|
cherrypy.engine.start() |
|
cherrypy.engine.block()
|
|
|