Commit 71c35eb2 authored by mathieui's avatar mathieui

Implement XEP-0118 (Fix #1840)

- Add new theming options
- Show the tune in the roster (both in contact line and infowin)
- add an option to show tunes as info messages
parent eb2e5825
......@@ -340,6 +340,9 @@ user_list_sort = desc
# be displayed using their nick color if true.
display_user_color_in_join_part = false
# Display user tune notifications as information messages or not
display_tune_notifications = false
# if true, chat states will be sent to the people you are talking to.
# Chat states are, for example, messages informing that you are composing
# a message or that you closed the tab, etc
......
......@@ -113,6 +113,11 @@ section of this documentation.
the nick you will use when joining a room with no associated nick
If this is empty, the $USER environnement variable will be used
*display_tune_notifications*:: false
If set to true, notifications about the music your contacts listen to
will be displayed in the info buffer as 'Tune' messages.
*display_user_color_in_join_part*:: false
If set to true, the color of the nick will be used in MUCs information
......
......@@ -278,6 +278,43 @@ def parse_secs_to_str(duration=0):
result = '0s'
return result
def format_tune_string(infos):
"""
Contruct a string from a dict created from an "User tune" event.
:param dict infos: The informations
:return: The formatted string
:rtype: :py:class:`str`
"""
elems = []
track = infos.get('track')
if track:
elems.append(track)
title = infos.get('title')
if title:
elems.append(title)
else:
elems.append('Unknown title')
elems.append('-')
artist = infos.get('artist')
if artist:
elems.append(artist)
else:
elems.append('Unknown artist')
rating = infos.get('rating')
if rating:
elems.append('[ ' + rating + '/10' + ' ]')
length = infos.get('length')
if length:
length = int(length)
secs = length % 60
mins = length // 60
secs = str(secs).zfill(2)
mins = str(mins).zfill(2)
elems.append('[' + mins + ':' + secs + ']')
return ' '.join(elems)
def safeJID(*args, **kwargs):
"""
Construct a :py:class:`sleekxmpp.JID` object from a string.
......
......@@ -70,6 +70,7 @@ class Connection(sleekxmpp.ClientXMPP):
self.register_plugin('xep_0071')
self.register_plugin('xep_0085')
self.register_plugin('xep_0115')
self.register_plugin('xep_0118')
self.register_plugin('xep_0191')
if config.get('send_poezio_info', 'true') == 'true':
info = {'name':'poezio',
......
......@@ -66,6 +66,7 @@ class Contact(object):
self.__item = item
self.folded_states = defaultdict(lambda: True)
self.error = None
self.tune = {}
@property
def groups(self):
......
......@@ -230,6 +230,7 @@ class Core(object):
self.key_func.update(key_func)
# Add handlers
self.xmpp.add_event_handler("user_tune_publish", self.on_tune_event)
self.xmpp.add_event_handler('connected', self.on_connected)
self.xmpp.add_event_handler('disconnected', self.on_disconnected)
self.xmpp.add_event_handler('no_auth', self.on_failed_auth)
......@@ -2598,6 +2599,29 @@ class Core(object):
else:
self.refresh_window()
def on_tune_event(self, message):
contact = roster[message['from'].bare]
if not contact:
return
item = message['pubsub_event']['items']['item']
if item.find('{http://jabber.org/protocol/tune}tune'):
item = item['tune']
contact.tune = {
'artist': item['artist'],
'length': item['length'],
'rating': item['rating'],
'source': item['source'],
'title': item['title'],
'track': item['track'],
'uri': item['uri']
}
else:
contact.tune = {}
if config.get('display_tune_notifications', 'false') == 'true' and contact.tune:
self.information(
'Tune from '+ message['from'].bare + ': ' + common.format_tune_string(contact.tune),
'Tune')
def on_groupchat_message(self, message):
"""
Triggered whenever a message is received from a multi-user chat room.
......
......@@ -2845,12 +2845,14 @@ class RosterInfoTab(Tab):
if isinstance(selected_row, Contact):
cont = selected_row
res = selected_row.get_highest_priority_resource()
msg = 'Contact: %s (%s)\n%s connected resource%s\nCurrent status: %s' % (
msg = 'Contact: %s (%s)\n%s connected resource%s\nCurrent status: %s\nCurrent tune: %s' % (
cont.bare_jid,
res.presence if res else 'unavailable',
len(cont),
'' if len(cont) == 1 else 's',
res.status if res else '',)
res.status if res else '',
common.format_tune_string(cont.tune),
)
elif isinstance(selected_row, Resource):
res = selected_row
msg = 'Resource: %s (%s)\nCurrent status: %s\nPriority: %s' % (
......
......@@ -200,8 +200,10 @@ class Theme(object):
CHAR_COLUMN_ASC = ' ▲'
CHAR_COLUMN_DESC =' ▼'
CHAR_ROSTER_ERROR = '✖'
CHAR_ROSTER_TUNE = '♪'
CHAR_ROSTER_ASKED = '?'
COLOR_ROSTER_TUNE = (6, -1)
COLOR_ROSTER_ERROR = (1, -1)
COLOR_JOIN_CHAR = (4, -1)
COLOR_QUIT_CHAR = (1, -1)
......@@ -218,6 +220,7 @@ class Theme(object):
'roster': (2, -1),
'help': (10, -1),
'headline': (11, -1, 'b'),
'tune': (6, -1),
'default': (7, -1),
}
......
......@@ -31,6 +31,7 @@ from poopt import cut_text
from sleekxmpp import JID
from common import safeJID
import common
import core
import wcwidth
......@@ -1935,6 +1936,8 @@ class RosterWin(Win):
added += len(get_theme().CHAR_ROSTER_ASKED)
if config.get('show_s2s_errors', 'true').lower() == 'true' and contact.error:
added += len(get_theme().CHAR_ROSTER_ERROR)
if contact.tune:
added += len(get_theme().CHAR_ROSTER_TUNE)
if config.getl('show_roster_jids', 'true') == 'false' and contact.name:
display_name = '%s' % contact.name
......@@ -1953,6 +1956,8 @@ class RosterWin(Win):
self.addstr(get_theme().CHAR_ROSTER_ASKED, to_curses_attr(get_theme().COLOR_IMPORTANT_TEXT))
if config.get('show_s2s_errors', 'true').lower() == 'true' and contact.error:
self.addstr(get_theme().CHAR_ROSTER_ERROR, to_curses_attr(get_theme().COLOR_ROSTER_ERROR))
if contact.tune:
self.addstr(get_theme().CHAR_ROSTER_TUNE, to_curses_attr(get_theme().COLOR_ROSTER_TUNE))
self.finish_line()
......@@ -1995,23 +2000,33 @@ class ContactInfoWin(Win):
presence = resource.presence
else:
presence = 'unavailable'
i = 0
self.addstr(0, 0, '%s (%s)'%(jid, presence,), to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
self.finish_line(get_theme().COLOR_INFORMATION_BAR)
self.addstr(1, 0, 'Subscription: %s' % (contact.subscription,))
i += 1
self.addstr(i, 0, 'Subscription: %s' % (contact.subscription,))
self.finish_line()
i += 1
if contact.ask:
if contact.ask == 'asked':
self.addstr('Ask: %s' % (contact.ask,), to_curses_attr(get_theme().COLOR_IMPORTANT_TEXT))
self.addstr(i, 0, 'Ask: %s' % (contact.ask,), to_curses_attr(get_theme().COLOR_IMPORTANT_TEXT))
else:
self.addstr('Ask: %s' % (contact.ask,))
self.addstr(i, 0, 'Ask: %s' % (contact.ask,))
self.finish_line()
i += 1
if resource:
self.addstr(2, 0, 'Status: %s' % (resource.status))
self.addstr(i, 0, 'Status: %s' % (resource.status))
self.finish_line()
i += 1
if contact.error:
self.addstr('Error: %s' % contact.error, to_curses_attr(get_theme().COLOR_ROSTER_ERROR))
self.addstr(i, 0, 'Error: %s' % contact.error, to_curses_attr(get_theme().COLOR_ROSTER_ERROR))
self.finish_line()
i += 1
if contact.tune:
self.addstr(i, 0, 'Current Tune: %s' % common.format_tune_string(contact.tune), to_curses_attr(get_theme().COLOR_NORMAL_TEXT))
i += 1
def draw_group_info(self, group):
"""
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment