mirror of
https://github.com/twisterarmy/swizzler.git
synced 2025-03-12 13:31:26 +00:00
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
This commit is contained in:
parent
b7231df882
commit
3ab5844376
13
README.md
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
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'])
|
||||||
}
|
}
|
||||||
|
@ -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
Normal file
92
templates/messages.html
Normal file
@ -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. 😭</small></h4>
|
||||||
|
{{/threads}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="sidebar" class="col-md-4">
|
||||||
|
{{> sidebar}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{> footer}}
|
||||||
|
</div> <!-- /container -->
|
||||||
|
</body>
|
||||||
|
</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. 😭</small></h4>
|
<h4><small>Nothing. Nada. Rien de rien. 😭</small></h4>
|
||||||
{{/messages}}
|
{{/messages}}
|
||||||
|
22
twister.py
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…
x
Reference in New Issue
Block a user