# -*- 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 '{2}{3}'.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 '{2}{3}'.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 '{2}{3}/{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 '{1}'.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) 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()