Browse Source

Direct messages

Now could be a good time to setup basic auth
if you haven't done it so far :)
See https://github.com/swizzler/swizzler/commit/d959492b
master
The Dod 11 years ago
parent
commit
3ab5844376
  1. 13
      README.md
  2. 38
      swizzler.py
  3. 2
      templates/message.html
  4. 92
      templates/messages.html
  5. 6
      templates/standard.html
  6. 22
      twister.py

13
README.md

@ -10,17 +10,20 @@ Provide a [twister](twister.net.co) web client that:
In short: it's a "twister-client constructor kit". Even if what you do doesn't get merged upstream, who says we should all run the same client? Diversity is fun. In short: it's a "twister-client constructor kit". Even if what you do doesn't get merged upstream, who says we should all run the same client? Diversity is fun.
### Roadmap ### Roadmap
The following functionality is missing before Swizzler becomes a full read-only version Missing features before Swizzler has full read-only functionality
* Direct messages - currently, there are links to that (look like an envelope) but they're 404 ;)
* Search box - for hashtags and users by prefix * Search box - for hashtags and users by prefix
* Basic auth - we don't want trojans to read our direct messages, so I guess this is top priority ;) * <del>Direct messages</del>
* <del>Basic auth</del>
I believe this should take a few days. We're almost there \o/
#### Future #### Future
* *To be defined* * [db instead of cache](https://github.com/swizzler/swizzler/wiki/from-cache-to-db)
* Full [or almost full] functionality of the standard client
* [additional features](https://github.com/swizzler/swizzler/wiki/Ideas-for-features)
* *Insert here stuff I've overlooked*
* Then we take Berlin * Then we take Berlin
---------------- ----------------

38
swizzler.py

@ -67,11 +67,13 @@ class SwizzlerApp(object):
conf = cherrypy.request.app.config['swizzler'] conf = cherrypy.request.app.config['swizzler']
twister = Twister(conf['rpc_url'],format_twist) twister = Twister(conf['rpc_url'],format_twist)
user = twister.get_user_info(username) user = twister.get_user_info(username)
messages = twister.get_user_posts(username,conf['num_messages'])
result = { result = {
'is_user':True, 'is_user':True,
'title':u"{0} (@{1}): Profile - Swizzler".format(user['fullname'],user['username']), 'title':u"{fullname} (@{username}): Profile - Swizzler".format(**user),
'subject':user, 'subject':user,
'messages':twister.get_user_posts(username,conf['num_messages']), 'messages':messages,
'any_messages':not not messages,
'local_users':twister.local_user_menu()['users'], 'local_users':twister.local_user_menu()['users'],
'info':twister.get_info(), 'info':twister.get_info(),
#the filter avoids some utf etc. that ttf can't handle (TODO: fix or replace format_twist) #the filter avoids some utf etc. that ttf can't handle (TODO: fix or replace format_twist)
@ -82,11 +84,13 @@ class SwizzlerApp(object):
def tag(self,tag): def tag(self,tag):
conf = cherrypy.request.app.config['swizzler'] conf = cherrypy.request.app.config['swizzler']
twister = Twister(conf['rpc_url'],format_twist) twister = Twister(conf['rpc_url'],format_twist)
messages = twister.get_tag_posts(tag)
result = { result = {
'is_tag':True, 'is_tag':True,
'title':u"#{0} - Swizzler".format(tag), 'title':u"#{0} - Swizzler".format(tag),
'subject':{"fullname":tag}, 'subject':{"fullname":tag},
'messages':twister.get_tag_posts(tag), 'messages':messages,
'any_messages':not not messages,
'local_users':twister.local_user_menu()['users'], 'local_users':twister.local_user_menu()['users'],
'info':twister.get_info(), 'info':twister.get_info(),
#the filter avoids some utf etc. that ttf can't handle (TODO: fix or replace format_twist) #the filter avoids some utf etc. that ttf can't handle (TODO: fix or replace format_twist)
@ -106,19 +110,42 @@ class SwizzlerApp(object):
'is_home':True, 'is_home':True,
'is_mentions':mode=='mentions', 'is_mentions':mode=='mentions',
'is_feed':mode!='mentions', 'is_feed':mode!='mentions',
'title':u"{0} (@{1}): Home - Swizzler".format(menu['active']['fullname'],menu['active']['username']), 'title':u"{fullname} (@{username}): Home - Swizzler".format(**menu['active']),
'local_users':menu['users'], 'local_users':menu['users'],
'info':twister.get_info(), 'info':twister.get_info(),
'subject':menu['active'], 'subject':menu['active'],
'messages':messages, 'messages':messages,
'any_messages':not not messages,
#the filter avoids some utf etc. that ttf can't handle (TODO: fix or replace format_twist) #the filter avoids some utf etc. that ttf can't handle (TODO: fix or replace format_twist)
'trending':format_trending(twister,conf['num_messages']) 'trending':format_trending(twister,conf['num_messages'])
} }
return stache.render(stache.load_template('standard'),result) return stache.render(stache.load_template('standard'),result)
@cherrypy.expose @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(),
#the filter avoids some utf etc. that ttf can't handle (TODO: fix or replace format_twist)
'trending':format_trending(twister,conf['num_messages'])
}
return stache.render(stache.load_template('messages'),result)
@cherrypy.expose
def index(self): def index(self):
conf = cherrypy.request.app.config['swizzler'] conf = cherrypy.request.app.config['swizzler']
twister = Twister(conf['rpc_url'],format_twist) twister = Twister(conf['rpc_url'],format_twist)
messages = twister.get_sponsored_posts(conf['num_messages'])
result = { result = {
'is_user':True, # i.e. we want to display "bio" and not mentions/DMs/profile buttons 'is_user':True, # i.e. we want to display "bio" and not mentions/DMs/profile buttons
'title':"Welcome to Swizzler", 'title':"Welcome to Swizzler",
@ -133,7 +160,8 @@ Instead, they enjoy occasional minutes of fame in the form of the sponsored post
We #Respect their hard earned crypto-graffiti by appreciating them on coffee/spliff/soy-milk/etc. breaks, because that's how we roll yo. 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.""") Start mining today, and all this (AND moral satisfaction) can be yours.""")
}, },
'messages':twister.get_sponsored_posts(conf['num_messages']), 'messages':messages,
'any_messages':not not messages,
#the filter avoids some utf etc. that ttf can't handle (TODO: fix or replace format_twist) #the filter avoids some utf etc. that ttf can't handle (TODO: fix or replace format_twist)
'trending':format_trending(twister,conf['num_messages']) 'trending':format_trending(twister,conf['num_messages'])
} }

2
templates/message.html

@ -1,7 +1,7 @@
<li class="list-group-item media"> <li class="list-group-item media">
{{#user}} {{#user}}
{{#username}} {{#username}}
<a class="pull-left thumbnail" href="/user/{{.}}" title="@{{.}}'s profile"> <a class="{{#fromMe}}pull-right{{/fromMe}}{{^fromMe}}pull-left{{/fromMe}} thumbnail" href="/user/{{.}}" title="@{{.}}'s profile">
<img class="media-object avatar" src="{{#avatar}}{{.}}{{/avatar}}{{^avatar}}/assets/img/genericPerson.png{{/avatar}}" alt="{{fullname}}"> <img class="media-object avatar" src="{{#avatar}}{{.}}{{/avatar}}{{^avatar}}/assets/img/genericPerson.png{{/avatar}}" alt="{{fullname}}">
</a> </a>
{{/username}} {{/username}}

92
templates/messages.html

@ -0,0 +1,92 @@
<!DOCTYPE html>
<html language="{{lang}}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{title}}</title>
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<link href="/assets/css/bootstrap.min.css" rel="stylesheet">
<link href="/assets/css/bootstrap-theme.min.css" rel="stylesheet">
<link href="/assets/css/swizzler.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row">
<div id="main" class="col-md-8">
<div class="row">
<div id="header" class="col-md-12 well">
{{#subject}}
<div class="media">
<a class="pull-left thumbnail" href="/user/{{username}}">
<img class="media-object avatar" src="{{#avatar}}{{.}}{{/avatar}}{{^avatar}}/assets/img/twister-64.jpg{{/avatar}}" alt="{{fullname}}">
</a>
<div class="media-body">
<h4 class="media-heading">
<a href="/messages/{{username}}{{#remoteuser}}/{{username}}{{/remoteuser}}"
title="@{{username}}'s messages{{#remoteuser}} with {{username}}{{/remoteuser}}"
><span class="glyphicon glyphicon-envelope"></span></a>
{{fullname}}'s direct messages
{{#remoteuser}}with {{fullname}}{{/remoteuser}}
</h4>
{{#remoteuser}}
{{#subject}}
<a href="/messages/{{username}}" title="Back to @{{username}}'s direct message summary"><span
class="glyphicon glyphicon-arrow-left"></span></a>
{{/subject}}
{{/remoteuser}}
<a href="/home/{{username}}" title="@{{username}}'s home"><span class="glyphicon glyphicon-home"></span></a>
<a href="/home/{{username}}/mentions" title="mentions of @{{username}}"><span class="glyphicon glyphicon-bell"></span></a>
<a href="/user/{{username}}" title="@{{username}}'s profile"><span class="glyphicon glyphicon-user"></span></a>
</div>
</div>
{{/subject}}
</div>
</div>
<div class="row">
<div id="messages" class="col-md-12">
{{#any_threads}}
<ul class="list-group media-list">
{{#threads}}
{{^remoteuser}}
{{#user}}
<div class="label label-info">
<a href="/messages/{{subject.username}}/{{username}}" title="See more messages to/from @{{username}}">{{fullname}}</a>
<a href="/messages/{{subject.username}}/{{username}}" title="See more messages to/from @{{username}}"><span
class="glyphicon glyphicon-arrow-right"></span></a>
</div>
{{/user}}
{{/remoteuser}}
{{#remoteuser}}
{{#subject}}
<div class="label label-info">
<a href="/messages/{{subject.username}}" title="Back to @{{username}}'s direct message summary"><span
class="glyphicon glyphicon-arrow-left"></span></a>
<a href="/messages/{{subject.username}}" title="Back to @{{username}}'s direct message summary">Back</a>
</div>
{{/subject}}
{{/remoteuser}}
<ul class="list-group media-list">
{{#messages}}
{{> message}}
{{/messages}}
</ul>
{{/threads}}
</ul>
{{/any_threads}}
{{^threads}}
<h4><small>Nothing. Nada. Rien de rien. &#128557;</small></h4>
{{/threads}}
</div>
</div>
</div>
<div id="sidebar" class="col-md-4">
{{> sidebar}}
</div>
</div>
{{> footer}}
</div> <!-- /container -->
</body>
</html>

6
templates/standard.html

@ -29,10 +29,10 @@
<a href="/home/{{username}}" title="@{{username}}'s home"><span class="glyphicon glyphicon-home"></span></a> {{fullname}} <a href="/home/{{username}}" title="@{{username}}'s home"><span class="glyphicon glyphicon-home"></span></a> {{fullname}}
{{/is_feed}} {{/is_feed}}
{{#is_mentions}} {{#is_mentions}}
<a href="/home/{{username}}/mentions" title="@{{username}}'s home"><span class="glyphicon glyphicon-bell"></span></a> {{fullname}} <a href="/home/{{username}}/mentions" title="mentions of @{{username}}"><span class="glyphicon glyphicon-bell"></span></a> {{fullname}}
{{/is_mentions}} {{/is_mentions}}
{{#is_messages}} {{#is_messages}}
<a href="/messages/{{username}}" title="@{{username}}'s home"><span class="glyphicon glyphicon-envelope"></span></a> {{fullname}} <a href="/messages/{{username}}" title="direct messages to/from @{{username}}"><span class="glyphicon glyphicon-envelope"></span></a> {{fullname}}
{{/is_messages}} {{/is_messages}}
</h4> </h4>
{{^is_feed}} {{^is_feed}}
@ -67,7 +67,7 @@
</div> </div>
<div class="row"> <div class="row">
<div id="messages" class="col-md-12"> <div id="messages" class="col-md-12">
{{#messages}}<ul class="list-group media-list">{{> message}}</ul>{{/messages}} {{#any_messages}}<ul class="list-group media-list">{{#messages}}{{> message}}{{/messages}}</ul>{{/any_messages}}
{{^messages}} {{^messages}}
<h4><small>Nothing. Nada. Rien de rien. &#128557;</small></h4> <h4><small>Nothing. Nada. Rien de rien. &#128557;</small></h4>
{{/messages}} {{/messages}}

22
twister.py

@ -110,6 +110,28 @@ class Twister:
def get_user_mentions(self,localusername): def get_user_mentions(self,localusername):
return [self._format_post_info(p['p']['v']) for p in self.twister.dhtget(localusername,'mention','m')] return [self._format_post_info(p['p']['v']) for p in self.twister.dhtget(localusername,'mention','m')]
@functioncache(60,ignore_instance=True) @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['user'] = message['fromMe'] and localuser or user
message['message'] = self._format_message(message['text'])
messages.insert(0,message) # reverse order (newer first)
result.append({'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): def get_user_posts(self,username,num=8):
result = [self._format_post_info(p) for p in self.twister.getposts(num,[{'username':username}])] result = [self._format_post_info(p) for p in self.twister.getposts(num,[{'username':username}])]
if result: if result:

Loading…
Cancel
Save