From d55cc5872503567775f0d7a7731d6f489bf2299b Mon Sep 17 00:00:00 2001 From: mathieui Date: Sun, 12 Nov 2017 15:03:09 +0100 Subject: yapf -ir --- poezio/tabs/__init__.py | 12 +- poezio/tabs/adhoc_commands_list.py | 29 +- poezio/tabs/basetabs.py | 307 ++++++----- poezio/tabs/bookmarkstab.py | 45 +- poezio/tabs/confirmtab.py | 27 +- poezio/tabs/conversationtab.py | 197 ++++--- poezio/tabs/data_forms.py | 6 +- poezio/tabs/listtab.py | 50 +- poezio/tabs/muclisttab.py | 25 +- poezio/tabs/muctab.py | 1064 +++++++++++++++++++++--------------- poezio/tabs/privatetab.py | 199 ++++--- poezio/tabs/rostertab.py | 524 +++++++++++------- poezio/tabs/xmltab.py | 146 ++--- 13 files changed, 1562 insertions(+), 1069 deletions(-) (limited to 'poezio/tabs') diff --git a/poezio/tabs/__init__.py b/poezio/tabs/__init__.py index 01f65aa3..81e9f115 100644 --- a/poezio/tabs/__init__.py +++ b/poezio/tabs/__init__.py @@ -13,8 +13,10 @@ from poezio.tabs.adhoc_commands_list import AdhocCommandsListTab from poezio.tabs.data_forms import DataFormsTab from poezio.tabs.bookmarkstab import BookmarksTab -__all__ = ['Tab', 'ChatTab', 'GapTab', 'OneToOneTab', 'STATE_PRIORITY', - 'SHOW_NAME', 'RosterInfoTab', 'MucTab', 'NS_MUC_USER', 'PrivateTab', - 'ConfirmTab', 'ConversationTab', 'StaticConversationTab', - 'DynamicConversationTab', 'XMLTab', 'ListTab', 'MucListTab', - 'AdhocCommandsListTab', 'DataFormsTab', 'BookmarksTab'] +__all__ = [ + 'Tab', 'ChatTab', 'GapTab', 'OneToOneTab', 'STATE_PRIORITY', 'SHOW_NAME', + 'RosterInfoTab', 'MucTab', 'NS_MUC_USER', 'PrivateTab', 'ConfirmTab', + 'ConversationTab', 'StaticConversationTab', 'DynamicConversationTab', + 'XMLTab', 'ListTab', 'MucListTab', 'AdhocCommandsListTab', 'DataFormsTab', + 'BookmarksTab' +] diff --git a/poezio/tabs/adhoc_commands_list.py b/poezio/tabs/adhoc_commands_list.py index 6db654c9..a1b186be 100644 --- a/poezio/tabs/adhoc_commands_list.py +++ b/poezio/tabs/adhoc_commands_list.py @@ -11,42 +11,51 @@ from poezio.tabs import ListTab from slixmpp.plugins.xep_0030.stanza.items import DiscoItem + class AdhocCommandsListTab(ListTab): plugin_commands = {} plugin_keys = {} def __init__(self, core, jid): - ListTab.__init__(self, core, jid.full, - "“Enter”: execute selected command.", - 'Ad-hoc commands of JID %s (Loading)' % jid, - (('Node', 0), ('Description', 1))) + ListTab.__init__( + self, core, jid.full, "“Enter”: execute selected command.", + 'Ad-hoc commands of JID %s (Loading)' % jid, (('Node', 0), + ('Description', 1))) self.key_func['^M'] = self.execute_selected_command def execute_selected_command(self): if not self.listview or not self.listview.get_selected_row(): return node, name, jid = self.listview.get_selected_row() - session = {'next': self.core.handler.next_adhoc_step, - 'error': self.core.handler.adhoc_error} + session = { + 'next': self.core.handler.next_adhoc_step, + 'error': self.core.handler.adhoc_error + } self.core.xmpp.plugin['xep_0050'].start_command(jid, node, session) def get_columns_sizes(self): - return {'Node': int(self.width * 3 / 8), - 'Description': int(self.width * 5 / 8)} + return { + 'Node': int(self.width * 3 / 8), + 'Description': int(self.width * 5 / 8) + } def on_list_received(self, iq): """ Fill the listview with the value from the received iq """ if iq['type'] == 'error': - self.set_error(iq['error']['type'], iq['error']['code'], iq['error']['text']) + self.set_error(iq['error']['type'], iq['error']['code'], + iq['error']['text']) return + def get_items(): substanza = iq['disco_items'] for item in substanza['substanzas']: if isinstance(item, DiscoItem): yield item - items = [(item['node'], item['name'] or '', item['jid']) for item in get_items()] + + items = [(item['node'], item['name'] or '', item['jid']) + for item in get_items()] self.listview.set_lines(items) self.info_header.message = 'Ad-hoc commands of JID %s' % self.name if self.core.current_tab() is self: diff --git a/poezio/tabs/basetabs.py b/poezio/tabs/basetabs.py index 0570be27..29404036 100644 --- a/poezio/tabs/basetabs.py +++ b/poezio/tabs/basetabs.py @@ -35,60 +35,61 @@ from poezio.decorators import command_args_parser # getters for tab colors (lambdas, so that they are dynamic) STATE_COLORS = { - 'disconnected': lambda: get_theme().COLOR_TAB_DISCONNECTED, - 'scrolled': lambda: get_theme().COLOR_TAB_SCROLLED, - 'nonempty': lambda: get_theme().COLOR_TAB_NONEMPTY, - 'joined': lambda: get_theme().COLOR_TAB_JOINED, - 'message': lambda: get_theme().COLOR_TAB_NEW_MESSAGE, - 'composing': lambda: get_theme().COLOR_TAB_COMPOSING, - 'highlight': lambda: get_theme().COLOR_TAB_HIGHLIGHT, - 'private': lambda: get_theme().COLOR_TAB_PRIVATE, - 'normal': lambda: get_theme().COLOR_TAB_NORMAL, - 'current': lambda: get_theme().COLOR_TAB_CURRENT, - 'attention': lambda: get_theme().COLOR_TAB_ATTENTION, - } + 'disconnected': lambda: get_theme().COLOR_TAB_DISCONNECTED, + 'scrolled': lambda: get_theme().COLOR_TAB_SCROLLED, + 'nonempty': lambda: get_theme().COLOR_TAB_NONEMPTY, + 'joined': lambda: get_theme().COLOR_TAB_JOINED, + 'message': lambda: get_theme().COLOR_TAB_NEW_MESSAGE, + 'composing': lambda: get_theme().COLOR_TAB_COMPOSING, + 'highlight': lambda: get_theme().COLOR_TAB_HIGHLIGHT, + 'private': lambda: get_theme().COLOR_TAB_PRIVATE, + 'normal': lambda: get_theme().COLOR_TAB_NORMAL, + 'current': lambda: get_theme().COLOR_TAB_CURRENT, + 'attention': lambda: get_theme().COLOR_TAB_ATTENTION, +} VERTICAL_STATE_COLORS = { - 'disconnected': lambda: get_theme().COLOR_VERTICAL_TAB_DISCONNECTED, - 'scrolled': lambda: get_theme().COLOR_VERTICAL_TAB_SCROLLED, - 'nonempty': lambda: get_theme().COLOR_VERTICAL_TAB_NONEMPTY, - 'joined': lambda: get_theme().COLOR_VERTICAL_TAB_JOINED, - 'message': lambda: get_theme().COLOR_VERTICAL_TAB_NEW_MESSAGE, - 'composing': lambda: get_theme().COLOR_VERTICAL_TAB_COMPOSING, - 'highlight': lambda: get_theme().COLOR_VERTICAL_TAB_HIGHLIGHT, - 'private': lambda: get_theme().COLOR_VERTICAL_TAB_PRIVATE, - 'normal': lambda: get_theme().COLOR_VERTICAL_TAB_NORMAL, - 'current': lambda: get_theme().COLOR_VERTICAL_TAB_CURRENT, - 'attention': lambda: get_theme().COLOR_VERTICAL_TAB_ATTENTION, - } - + 'disconnected': lambda: get_theme().COLOR_VERTICAL_TAB_DISCONNECTED, + 'scrolled': lambda: get_theme().COLOR_VERTICAL_TAB_SCROLLED, + 'nonempty': lambda: get_theme().COLOR_VERTICAL_TAB_NONEMPTY, + 'joined': lambda: get_theme().COLOR_VERTICAL_TAB_JOINED, + 'message': lambda: get_theme().COLOR_VERTICAL_TAB_NEW_MESSAGE, + 'composing': lambda: get_theme().COLOR_VERTICAL_TAB_COMPOSING, + 'highlight': lambda: get_theme().COLOR_VERTICAL_TAB_HIGHLIGHT, + 'private': lambda: get_theme().COLOR_VERTICAL_TAB_PRIVATE, + 'normal': lambda: get_theme().COLOR_VERTICAL_TAB_NORMAL, + 'current': lambda: get_theme().COLOR_VERTICAL_TAB_CURRENT, + 'attention': lambda: get_theme().COLOR_VERTICAL_TAB_ATTENTION, +} # priority of the different tab states when using Alt+e # higher means more priority, < 0 means not selectable STATE_PRIORITY = { - 'normal': -1, - 'current': -1, - 'disconnected': 0, - 'nonempty': 0.1, - 'scrolled': 0.5, - 'joined': 0.8, - 'composing': 0.9, - 'message': 1, - 'highlight': 2, - 'private': 2, - 'attention': 3 - } + 'normal': -1, + 'current': -1, + 'disconnected': 0, + 'nonempty': 0.1, + 'scrolled': 0.5, + 'joined': 0.8, + 'composing': 0.9, + 'message': 1, + 'highlight': 2, + 'private': 2, + 'attention': 3 +} SHOW_NAME = { - 'dnd': 'busy', - 'away': 'away', - 'xa': 'not available', - 'chat': 'chatty', - '': 'available' - } + 'dnd': 'busy', + 'away': 'away', + 'xa': 'not available', + 'chat': 'chatty', + '': 'available' +} + class Tab(object): plugin_commands = {} plugin_keys = {} + def __init__(self, core): self.core = core if not hasattr(self, 'name'): @@ -99,10 +100,9 @@ class Tab(object): self._prev_state = None self.need_resize = False - self.key_func = {} # each tab should add their keys in there - # and use them in on_input - self.commands = {} # and their own commands - + self.key_func = {} # each tab should add their keys in there + # and use them in on_input + self.commands = {} # and their own commands @property def size(self): @@ -148,9 +148,13 @@ class Tab(object): elif STATE_PRIORITY[value] < STATE_PRIORITY[self._state] and \ value not in ('current', 'disconnected') and \ not (self._state == 'scrolled' and value == 'disconnected'): - log.debug("Did not set state because of lower priority, asked: %s, kept: %s", value, self._state) - elif self._state == 'disconnected' and value not in ('joined', 'current'): - log.debug('Did not set state because disconnected tabs remain visible') + log.debug( + "Did not set state because of lower priority, asked: %s, kept: %s", + value, self._state) + elif self._state == 'disconnected' and value not in ('joined', + 'current'): + log.debug( + 'Did not set state because disconnected tabs remain visible') else: self._state = value if self._state == 'current': @@ -194,11 +198,22 @@ class Tab(object): shortdesc = command.get('shortdesc', '') completion = command.get('completion') usage = command.get('usage', '') - self.register_command(name, func, desc=desc, shortdesc=shortdesc, - completion=completion, usage=usage) - - - def register_command(self, name, func, *, desc='', shortdesc='', completion=None, usage=''): + self.register_command( + name, + func, + desc=desc, + shortdesc=shortdesc, + completion=completion, + usage=usage) + + def register_command(self, + name, + func, + *, + desc='', + shortdesc='', + completion=None, + usage=''): """ Add a command """ @@ -234,7 +249,8 @@ class Tab(object): whitespace = the_input.text.find(' ') if whitespace == -1: whitespace = len(the_input.text) - the_input.text = the_input.text[:whitespace-1] + the_input.text[whitespace:] + the_input.text = the_input.text[:whitespace - + 1] + the_input.text[whitespace:] the_input.new_completion(words, 0) hit_copy = set(the_input.hit_list) if len(hit_copy) == 1: @@ -249,10 +265,10 @@ class Tab(object): command = self.commands[command_name] elif command_name in self.core.commands: command = self.core.commands[command_name] - else: # Unknown command, cannot complete + else: # Unknown command, cannot complete return False if command.comp is None: - return False # There's no completion function + return False # There's no completion function else: comp = command.comp(the_input) if comp: @@ -269,11 +285,11 @@ class Tab(object): if txt.startswith('/') and not txt.startswith('//') and\ not txt.startswith('/me '): command = txt.strip().split()[0][1:] - arg = txt[2+len(command):] # jump the '/' and the ' ' + arg = txt[2 + len(command):] # jump the '/' and the ' ' func = None - if command in self.commands: # check tab-specific commands + if command in self.commands: # check tab-specific commands func = self.commands[command].func - elif command in self.core.commands: # check global commands + elif command in self.core.commands: # check global commands func = self.core.commands[command].func else: low = command.lower() @@ -286,9 +302,8 @@ class Tab(object): error_handled = self.missing_command_callback(low) if not error_handled: self.core.information("Unknown command (%s)" % - (command), - 'Error') - if command in ('correct', 'say'): # hack + (command), 'Error') + if command in ('correct', 'say'): # hack arg = xhtml.convert_simple_to_full_colors(arg) else: arg = xhtml.clean_text_simple(arg) @@ -422,8 +437,8 @@ class Tab(object): def __del__(self): log.debug('------ Closing tab %s', self.__class__.__name__) -class GapTab(Tab): +class GapTab(Tab): def __bool__(self): return False @@ -435,7 +450,9 @@ class GapTab(Tab): return '' def refresh(self): - log.debug('WARNING: refresh() called on a gap tab, this should not happen') + log.debug( + 'WARNING: refresh() called on a gap tab, this should not happen') + class ChatTab(Tab): """ @@ -447,6 +464,7 @@ class ChatTab(Tab): plugin_commands = {} plugin_keys = {} message_type = 'chat' + def __init__(self, core, jid=''): Tab.__init__(self, core) self.name = jid @@ -454,7 +472,7 @@ class ChatTab(Tab): self._remote_wants_chatstates = False self.directed_presence = None self._text_buffer = TextBuffer() - self.chatstate = None # can be "active", "composing", "paused", "gone", "inactive" + self.chatstate = None # can be "active", "composing", "paused", "gone", "inactive" # We keep a reference of the event that will set our chatstate to "paused", so that # we can delete it or change it if we need to self.timed_event_paused = None @@ -464,18 +482,24 @@ class ChatTab(Tab): self.key_func['M-h'] = self.scroll_separator self.key_func['M-/'] = self.last_words_completion self.key_func['^M'] = self.on_enter - self.register_command('say', self.command_say, - usage='', - shortdesc='Send the message.') - self.register_command('xhtml', self.command_xhtml, - usage='', - shortdesc='Send custom XHTML.') - self.register_command('clear', self.command_clear, - shortdesc='Clear the current buffer.') - self.register_command('correct', self.command_correct, - desc='Fix the last message with whatever you want.', - shortdesc='Correct the last message.', - completion=self.completion_correct) + self.register_command( + 'say', + self.command_say, + usage='', + shortdesc='Send the message.') + self.register_command( + 'xhtml', + self.command_xhtml, + usage='', + shortdesc='Send custom XHTML.') + self.register_command( + 'clear', self.command_clear, shortdesc='Clear the current buffer.') + self.register_command( + 'correct', + self.command_correct, + desc='Fix the last message with whatever you want.', + shortdesc='Correct the last message.', + completion=self.completion_correct) self.chat_state = None self.update_commands() self.update_keys() @@ -508,22 +532,39 @@ class ChatTab(Tab): if not logger.log_message(name, nickname, txt, date=time, typ=typ): self.core.information('Unable to write in the log file', 'Error') - def add_message(self, txt, time=None, nickname=None, forced_user=None, - nick_color=None, identifier=None, jid=None, history=None, - typ=1, highlight=False): + def add_message(self, + txt, + time=None, + nickname=None, + forced_user=None, + nick_color=None, + identifier=None, + jid=None, + history=None, + typ=1, + highlight=False): self.log_message(txt, nickname, time=time, typ=typ) - self._text_buffer.add_message(txt, time=time, - nickname=nickname, - highlight=highlight, - nick_color=nick_color, - history=history, - user=forced_user, - identifier=identifier, - jid=jid) - - def modify_message(self, txt, old_id, new_id, user=None, jid=None, nickname=None): + self._text_buffer.add_message( + txt, + time=time, + nickname=nickname, + highlight=highlight, + nick_color=nick_color, + history=history, + user=forced_user, + identifier=identifier, + jid=jid) + + def modify_message(self, + txt, + old_id, + new_id, + user=None, + jid=None, + nickname=None): self.log_message(txt, nickname, typ=1) - message = self._text_buffer.modify_message(txt, old_id, new_id, time=time, user=user, jid=jid) + message = self._text_buffer.modify_message( + txt, old_id, new_id, time=time, user=user, jid=jid) if message: self.text_win.modify_message(old_id, message) self.core.refresh_window() @@ -535,7 +576,7 @@ class ChatTab(Tab): Complete the input with words recently said """ # build the list of the recent words - char_we_dont_want = string.punctuation+' ’„“”…«»' + char_we_dont_want = string.punctuation + ' ’„“”…«»' words = list() for msg in self._text_buffer.messages[:-40:-1]: if not msg: @@ -571,7 +612,8 @@ class ChatTab(Tab): if not arg: return try: - body = xhtml.clean_text(xhtml.xhtml_to_poezio_colors(arg, force=True)) + body = xhtml.clean_text( + xhtml.xhtml_to_poezio_colors(arg, force=True)) ET.fromstring(arg) except: self.core.information('Could not send custom xhtml', 'Error') @@ -604,7 +646,8 @@ class ChatTab(Tab): Send an empty chatstate message """ if self.check_send_chat_state(): - if state in ('active', 'inactive', 'gone') and self.inactive and not always_send: + if state in ('active', 'inactive', + 'gone') and self.inactive and not always_send: return if (config.get_by_tabname('send_chat_states', self.general_jid) and self.remote_wants_chatstates is not False): @@ -642,7 +685,8 @@ class ChatTab(Tab): # First, cancel the delay if it already exists, before rescheduling # it at a new date self.cancel_paused_delay() - new_event = timed_events.DelayedEvent(4, self.send_chat_state, 'paused') + new_event = timed_events.DelayedEvent(4, self.send_chat_state, + 'paused') self.core.add_timed_event(new_event) self.timed_event_paused = new_event @@ -671,7 +715,10 @@ class ChatTab(Tab): def completion_correct(self, the_input): if self.last_sent_message and the_input.get_argument_position() == 1: - return Completion(the_input.auto_completion, [self.last_sent_message['body']], '', quotify=False) + return Completion( + the_input.auto_completion, [self.last_sent_message['body']], + '', + quotify=False) @property def inactive(self): @@ -703,23 +750,23 @@ class ChatTab(Tab): return self.text_win.scroll_down(1) def on_scroll_up(self): - return self.text_win.scroll_up(self.text_win.height-1) + return self.text_win.scroll_up(self.text_win.height - 1) def on_scroll_down(self): - return self.text_win.scroll_down(self.text_win.height-1) + return self.text_win.scroll_down(self.text_win.height - 1) def on_half_scroll_up(self): - return self.text_win.scroll_up((self.text_win.height-1) // 2) + return self.text_win.scroll_up((self.text_win.height - 1) // 2) def on_half_scroll_down(self): - return self.text_win.scroll_down((self.text_win.height-1) // 2) + return self.text_win.scroll_down((self.text_win.height - 1) // 2) @refresh_wrapper.always def scroll_separator(self): self.text_win.scroll_to_separator() -class OneToOneTab(ChatTab): +class OneToOneTab(ChatTab): def __init__(self, core, jid=''): ChatTab.__init__(self, core, jid) @@ -735,18 +782,18 @@ class OneToOneTab(ChatTab): self.remote_supports_attention = True self.remote_supports_receipts = True self.check_features() - self.register_command('unquery', self.command_unquery, - shortdesc='Close the tab.') - self.register_command('close', self.command_unquery, - shortdesc='Close the tab.') + self.register_command( + 'unquery', self.command_unquery, shortdesc='Close the tab.') + self.register_command( + 'close', self.command_unquery, shortdesc='Close the tab.') def remote_user_color(self): return dump_tuple(get_theme().COLOR_REMOTE_USER) def update_status(self, status): old_status = self.__status - if not (old_status.show != status.show or - old_status.message != status.message): + if not (old_status.show != status.show + or old_status.message != status.message): return self.__status = status hide_status_change = config.get_by_tabname('hide_status_change', @@ -820,16 +867,20 @@ class OneToOneTab(ChatTab): message['chat_sate'] = 'active' message.send() body = xhtml.xhtml_to_poezio_colors(xhtml_data, force=True) - self._text_buffer.add_message(body, nickname=self.core.own_nick, - identifier=message['id'],) + self._text_buffer.add_message( + body, + nickname=self.core.own_nick, + identifier=message['id'], + ) self.refresh() def check_features(self): "check the features supported by the other party" if safeJID(self.get_dest_jid()).resource: self.core.xmpp.plugin['xep_0030'].get_info( - jid=self.get_dest_jid(), timeout=5, - callback=self.features_checked) + jid=self.get_dest_jid(), + timeout=5, + callback=self.features_checked) @command_args_parser.raw def command_attention(self, message): @@ -872,12 +923,14 @@ class OneToOneTab(ChatTab): "Check for the 'attention' features" if 'urn:xmpp:attention:0' in features: self.remote_supports_attention = True - self.register_command('attention', self.command_attention, - usage='[message]', - shortdesc='Request the attention.', - desc='Attention: Request the attention of ' - 'the contact. Can also send a message' - ' along with the attention.') + self.register_command( + 'attention', + self.command_attention, + usage='[message]', + shortdesc='Request the attention.', + desc='Attention: Request the attention of ' + 'the contact. Can also send a message' + ' along with the attention.') else: self.remote_supports_attention = False return self.remote_supports_attention @@ -888,10 +941,12 @@ class OneToOneTab(ChatTab): if 'correct' in self.commands: del self.commands['correct'] elif 'correct' not in self.commands: - self.register_command('correct', self.command_correct, - desc='Fix the last message with whatever you want.', - shortdesc='Correct the last message.', - completion=self.completion_correct) + self.register_command( + 'correct', + self.command_correct, + desc='Fix the last message with whatever you want.', + shortdesc='Correct the last message.', + completion=self.completion_correct) return 'correct' in self.commands def _feature_receipts(self, features): @@ -905,8 +960,7 @@ class OneToOneTab(ChatTab): def features_checked(self, iq): "Features check callback" features = iq['disco_info'].get_features() or [] - before = ('correct' in self.commands, - self.remote_supports_attention, + before = ('correct' in self.commands, self.remote_supports_attention, self.remote_supports_receipts) correct = self._feature_correct(features) attention = self._feature_attention(features) @@ -918,7 +972,7 @@ class OneToOneTab(ChatTab): self.__initial_disco = True if not (correct or attention or receipts): - return # don’t display anything + return # don’t display anything ok = get_theme().CHAR_OK nope = get_theme().CHAR_EMPTY @@ -933,4 +987,3 @@ class OneToOneTab(ChatTab): msg = msg % (color, correct, attention, receipts) self.add_message(msg, typ=0) self.core.refresh_window() - diff --git a/poezio/tabs/bookmarkstab.py b/poezio/tabs/bookmarkstab.py index 452edd21..498d2f00 100644 --- a/poezio/tabs/bookmarkstab.py +++ b/poezio/tabs/bookmarkstab.py @@ -17,19 +17,17 @@ class BookmarksTab(Tab): a 4 widgets to set the jid/password/autojoin/storage method """ plugin_commands = {} + def __init__(self, core, bookmarks: BookmarkList): Tab.__init__(self, core) self.name = "Bookmarks" self.bookmarks = bookmarks self.new_bookmarks = [] self.removed_bookmarks = [] - self.header_win = windows.ColumnHeaderWin(('room@server/nickname', - 'password', - 'autojoin', - 'storage')) - self.bookmarks_win = windows.BookmarksWin(self.bookmarks, - self.height-4, - self.width, 1, 0) + self.header_win = windows.ColumnHeaderWin( + ('room@server/nickname', 'password', 'autojoin', 'storage')) + self.bookmarks_win = windows.BookmarksWin( + self.bookmarks, self.height - 4, self.width, 1, 0) self.help_win = windows.HelpText('Ctrl+Y: save, Ctrl+G: cancel, ' '↑↓: change lines, tab: change ' 'column, M-a: add bookmark, C-k' @@ -46,7 +44,8 @@ class BookmarksTab(Tab): self.update_commands() def add_bookmark(self): - new_bookmark = Bookmark(safeJID('room@example.tld/nick'), method='local') + new_bookmark = Bookmark( + safeJID('room@example.tld/nick'), method='local') self.new_bookmarks.append(new_bookmark) self.bookmarks_win.add_bookmark(new_bookmark) @@ -70,14 +69,16 @@ class BookmarksTab(Tab): def on_save(self): self.bookmarks_win.save() if find_duplicates(self.new_bookmarks): - self.core.information('Duplicate bookmarks in list (saving aborted)', 'Error') + self.core.information( + 'Duplicate bookmarks in list (saving aborted)', 'Error') return for bm in self.new_bookmarks: if safeJID(bm.jid): if not self.bookmarks[bm.jid]: self.bookmarks.append(bm) else: - self.core.information('Invalid JID for bookmark: %s/%s' % (bm.jid, bm.nick), 'Error') + self.core.information('Invalid JID for bookmark: %s/%s' % + (bm.jid, bm.nick), 'Error') return for bm in self.removed_bookmarks: @@ -89,6 +90,7 @@ class BookmarksTab(Tab): self.core.information('Bookmarks saved.', 'Info') else: self.core.information('Remote bookmarks not saved.', 'Error') + self.bookmarks.save(self.core.xmpp, callback=send_cb) self.core.close_tab(self) return True @@ -105,19 +107,23 @@ class BookmarksTab(Tab): def resize(self): self.need_resize = False self.header_win.resize_columns({ - 'room@server/nickname': self.width//3, - 'password': self.width//3, - 'autojoin': self.width//6, - 'storage': self.width//6 - }) + 'room@server/nickname': + self.width // 3, + 'password': + self.width // 3, + 'autojoin': + self.width // 6, + 'storage': + self.width // 6 + }) info_height = self.core.information_win_size tab_height = Tab.tab_win_height() self.header_win.resize(1, self.width, 0, 0) self.bookmarks_win.resize(self.height - 3 - tab_height - info_height, - self.width, 1, 0) + self.width, 1, 0) self.help_win.resize(1, self.width, self.height - 1, 0) self.info_header.resize(1, self.width, - self.height - 2 - tab_height - info_height, 0) + self.height - 2 - tab_height - info_height, 0) def on_info_win_size_changed(self): if self.core.information_win_size >= self.height - 3: @@ -125,9 +131,9 @@ class BookmarksTab(Tab): info_height = self.core.information_win_size tab_height = Tab.tab_win_height() self.bookmarks_win.resize(self.height - 3 - tab_height - info_height, - self.width, 1, 0) + self.width, 1, 0) self.info_header.resize(1, self.width, - self.height - 2 - tab_height - info_height, 0) + self.height - 2 - tab_height - info_height, 0) def refresh(self): if self.need_resize: @@ -147,4 +153,3 @@ def find_duplicates(bm_list): return True jids.add(bookmark.jid) return False - diff --git a/poezio/tabs/confirmtab.py b/poezio/tabs/confirmtab.py index 39f630cb..28c26122 100644 --- a/poezio/tabs/confirmtab.py +++ b/poezio/tabs/confirmtab.py @@ -13,7 +13,13 @@ class ConfirmTab(Tab): plugin_commands = {} plugin_keys = {} - def __init__(self, core, name, text, short_message, callback, critical=False): + def __init__(self, + core, + name, + text, + short_message, + callback, + critical=False): """Parameters: name: The name of the tab text: the text shown in the tab @@ -26,7 +32,8 @@ class ConfirmTab(Tab): Tab.__init__(self, core) self.state = 'highlight' self.name = name - self.default_help_message = windows.HelpText("Choose with arrow keys and press enter") + self.default_help_message = windows.HelpText( + "Choose with arrow keys and press enter") self.input = self.default_help_message self.infowin_top = windows.ConfirmStatusWin(short_message, critical) self.infowin_bottom = windows.ConfirmStatusWin(short_message, critical) @@ -76,10 +83,12 @@ class ConfirmTab(Tab): tab_win_height = Tab.tab_win_height() self.infowin_top.resize(1, self.width, 0, 0) - self.infowin_bottom.resize(1, self.width, self.height - 2 - info_win_height - tab_win_height, 0) + self.infowin_bottom.resize( + 1, self.width, self.height - 2 - info_win_height - tab_win_height, + 0) self.dialog.resize(self.height - 3 - info_win_height - tab_win_height, self.width, 1, 0) - self.input.resize(1, self.width, self.height-1, 0) + self.input.resize(1, self.width, self.height - 1, 0) def close(self, arg=None): self.done = True @@ -95,8 +104,10 @@ class ConfirmTab(Tab): return self.key_func[key]() def on_info_win_size_changed(self): - if self.core.information_win_size >= self.height-3: + if self.core.information_win_size >= self.height - 3: return - self.dialog.resize(self.height-3-self.core.information_win_size - Tab.tab_win_height(), self.width, 1, 0) - self.infowin_bottom.resize(1, self.width, self.height-2-self.core.information_win_size - Tab.tab_win_height(), 0) - + self.dialog.resize(self.height - 3 - self.core.information_win_size - + Tab.tab_win_height(), self.width, 1, 0) + self.infowin_bottom.resize( + 1, self.width, self.height - 2 - self.core.information_win_size - + Tab.tab_win_height(), 0) diff --git a/poezio/tabs/conversationtab.py b/poezio/tabs/conversationtab.py index cc6d716d..e8fcd720 100644 --- a/poezio/tabs/conversationtab.py +++ b/poezio/tabs/conversationtab.py @@ -30,6 +30,7 @@ from poezio.text_buffer import CorrectionError from poezio.theming import get_theme, dump_tuple from poezio.decorators import command_args_parser + class ConversationTab(OneToOneTab): """ The tab containg a normal conversation (not from a MUC) @@ -39,12 +40,13 @@ class ConversationTab(OneToOneTab): plugin_keys = {} additional_information = {} message_type = 'chat' + def __init__(self, core, jid): OneToOneTab.__init__(self, core, jid) self.nick = None self.nick_sent = False self.state = 'normal' - self.name = jid # a conversation tab is linked to one specific full jid OR bare jid + self.name = jid # a conversation tab is linked to one specific full jid OR bare jid self.text_win = windows.TextWin() self._text_buffer.add_window(self.text_win) self.upper_bar = windows.ConversationStatusMessageWin() @@ -52,21 +54,30 @@ class ConversationTab(OneToOneTab): # keys self.key_func['^I'] = self.completion # commands - self.register_command('version', self.command_version, - desc='Get the software version of the current interlocutor (usually its XMPP client and Operating System).', - shortdesc='Get the software version of the user.') - self.register_command('info', self.command_info, - shortdesc='Get the status of the contact.') - self.register_command('last_activity', self.command_last_activity, - usage='[jid]', - desc='Get the last activity of the given or the current contact.', - shortdesc='Get the activity.', - completion=self.core.completion.last_activity) - self.register_command('add', self.command_add, - desc='Add the current JID to your roster, ask them to' - ' allow you to see his presence, and allow them to' - ' see your presence.', - shortdesc='Add a user to your roster.') + self.register_command( + 'version', + self.command_version, + desc= + 'Get the software version of the current interlocutor (usually its XMPP client and Operating System).', + shortdesc='Get the software version of the user.') + self.register_command( + 'info', + self.command_info, + shortdesc='Get the status of the contact.') + self.register_command( + 'last_activity', + self.command_last_activity, + usage='[jid]', + desc='Get the last activity of the given or the current contact.', + shortdesc='Get the activity.', + completion=self.core.completion.last_activity) + self.register_command( + 'add', + self.command_add, + desc='Add the current JID to your roster, ask them to' + ' allow you to see his presence, and allow them to' + ' see your presence.', + shortdesc='Add a user to your roster.') self.update_commands() self.update_keys() @@ -114,8 +125,12 @@ class ConversationTab(OneToOneTab): msg['replace']['id'] = self.last_sent_message['id'] if config.get_by_tabname('group_corrections', self.name): try: - self.modify_message(msg['body'], self.last_sent_message['id'], msg['id'], jid=self.core.xmpp.boundjid, - nickname=self.core.own_nick) + self.modify_message( + msg['body'], + self.last_sent_message['id'], + msg['id'], + jid=self.core.xmpp.boundjid, + nickname=self.core.own_nick) replaced = True except CorrectionError: log.error('Unable to correct a message', exc_info=True) @@ -125,8 +140,8 @@ class ConversationTab(OneToOneTab): msg.enable('html') msg['html']['body'] = xhtml.poezio_colors_to_html(msg['body']) msg['body'] = xhtml.clean_text(msg['body']) - if (config.get_by_tabname('send_chat_states', self.general_jid) and - self.remote_wants_chatstates is not False): + if (config.get_by_tabname('send_chat_states', self.general_jid) + and self.remote_wants_chatstates is not False): needed = 'inactive' if self.inactive else 'active' msg['chat_state'] = needed if attention and self.remote_supports_attention: @@ -138,12 +153,13 @@ class ConversationTab(OneToOneTab): self.input.refresh() return if not replaced: - self.add_message(msg['body'], - nickname=self.core.own_nick, - nick_color=get_theme().COLOR_OWN_NICK, - identifier=msg['id'], - jid=self.core.xmpp.boundjid, - typ=1) + self.add_message( + msg['body'], + nickname=self.core.own_nick, + nick_color=get_theme().COLOR_OWN_NICK, + identifier=msg['id'], + jid=self.core.xmpp.boundjid, + typ=1) self.last_sent_message = msg if self.remote_supports_receipts: @@ -164,9 +180,12 @@ class ConversationTab(OneToOneTab): def callback(iq): if iq['type'] != 'result': if iq['error']['type'] == 'auth': - self.core.information('You are not allowed to see the activity of this contact.', 'Error') + self.core.information( + 'You are not allowed to see the activity of this contact.', + 'Error') else: - self.core.information('Error retrieving the activity', 'Error') + self.core.information('Error retrieving the activity', + 'Error') return seconds = iq['last_activity']['seconds'] status = iq['last_activity']['status'] @@ -174,19 +193,21 @@ class ConversationTab(OneToOneTab): msg = '\x19%s}The last activity of %s was %s ago%s' if not safeJID(from_).user: msg = '\x19%s}The uptime of %s is %s.' % ( - dump_tuple(get_theme().COLOR_INFORMATION_TEXT), - from_, - common.parse_secs_to_str(seconds)) + dump_tuple(get_theme().COLOR_INFORMATION_TEXT), from_, + common.parse_secs_to_str(seconds)) else: msg = '\x19%s}The last activity of %s was %s ago%s' % ( dump_tuple(get_theme().COLOR_INFORMATION_TEXT), from_, common.parse_secs_to_str(seconds), - (' and his/her last status was %s' % status) if status else '',) + (' and his/her last status was %s' % status) + if status else '', + ) self.add_message(msg) self.core.refresh_window() - self.core.xmpp.plugin['xep_0012'].get_last_activity(self.get_dest_jid(), callback=callback) + self.core.xmpp.plugin['xep_0012'].get_last_activity( + self.get_dest_jid(), callback=callback) @refresh_wrapper.conditional @command_args_parser.ignored @@ -201,12 +222,20 @@ class ConversationTab(OneToOneTab): else: resource = None if resource: - status = ('Status: %s' % resource.status) if resource.status else '' - self._text_buffer.add_message("\x19%(info_col)s}Show: %(show)s, %(status)s\x19o" % { - 'show': resource.presence or 'available', 'status': status, 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)}) + status = ( + 'Status: %s' % resource.status) if resource.status else '' + self._text_buffer.add_message( + "\x19%(info_col)s}Show: %(show)s, %(status)s\x19o" % { + 'show': resource.presence or 'available', + 'status': status, + 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT) + }) return True else: - self._text_buffer.add_message("\x19%(info_col)s}No information available\x19o" % {'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)}) + self._text_buffer.add_message( + "\x19%(info_col)s}No information available\x19o" % { + 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT) + }) return True @command_args_parser.quoted(0, 1) @@ -214,14 +243,18 @@ class ConversationTab(OneToOneTab): """ /version [jid] """ + def callback(res): if not res: - return self.core.information('Could not get the software version from %s' % (jid,), 'Warning') - version = '%s is running %s version %s on %s' % (jid, - res.get('name') or 'an unknown software', - res.get('version') or 'unknown', - res.get('os') or 'an unknown platform') + return self.core.information( + 'Could not get the software version from %s' % (jid, ), + 'Warning') + version = '%s is running %s version %s on %s' % ( + jid, res.get('name') or 'an unknown software', + res.get('version') or 'unknown', + res.get('os') or 'an unknown platform') self.core.information(version, 'Info') + if args: return self.core.command.version(args[0]) jid = safeJID(self.name) @@ -229,8 +262,7 @@ class ConversationTab(OneToOneTab): if jid in roster: resource = roster[jid].get_highest_priority_resource() jid = resource.jid if resource else jid - fixes.get_version(self.core.xmpp, jid, - callback=callback) + fixes.get_version(self.core.xmpp, jid, callback=callback) @command_args_parser.ignored def command_add(self): @@ -258,16 +290,15 @@ class ConversationTab(OneToOneTab): tab_win_height = Tab.tab_win_height() bar_height = 1 - self.text_win.resize(self.height - 2 - bar_height - info_win_height - - tab_win_height, - self.width, bar_height, 0) + self.text_win.resize( + self.height - 2 - bar_height - info_win_height - tab_win_height, + self.width, bar_height, 0) self.text_win.rebuild_everything(self._text_buffer) if display_bar: self.upper_bar.resize(1, self.width, 0, 0) - self.get_info_header().resize(1, self.width, - self.height - 2 - info_win_height - - tab_win_height, - 0) + self.get_info_header().resize( + 1, self.width, self.height - 2 - info_win_height - tab_win_height, + 0) self.input.resize(1, self.width, self.height - 1, 0) def refresh(self): @@ -279,8 +310,11 @@ class ConversationTab(OneToOneTab): self.text_win.refresh() if display_bar: - self.upper_bar.refresh(self.get_dest_jid(), roster[self.get_dest_jid()]) - self.get_info_header().refresh(self.get_dest_jid(), roster[self.get_dest_jid()], self.text_win, self.chatstate, ConversationTab.additional_information) + self.upper_bar.refresh(self.get_dest_jid(), + roster[self.get_dest_jid()]) + self.get_info_header().refresh( + self.get_dest_jid(), roster[self.get_dest_jid()], self.text_win, + self.chatstate, ConversationTab.additional_information) if display_info_win: self.info_win.refresh() @@ -288,8 +322,9 @@ class ConversationTab(OneToOneTab): self.input.refresh() def refresh_info_header(self): - self.get_info_header().refresh(self.get_dest_jid(), roster[self.get_dest_jid()], - self.text_win, self.chatstate, ConversationTab.additional_information) + self.get_info_header().refresh( + self.get_dest_jid(), roster[self.get_dest_jid()], self.text_win, + self.chatstate, ConversationTab.additional_information) self.input.refresh() def get_nick(self): @@ -307,7 +342,9 @@ class ConversationTab(OneToOneTab): self.key_func[key]() return False self.input.do_command(key, raw=raw) - empty_after = self.input.get_text() == '' or (self.input.get_text().startswith('/') and not self.input.get_text().startswith('//')) + empty_after = self.input.get_text() == '' or ( + self.input.get_text().startswith('/') + and not self.input.get_text().startswith('//')) self.send_composing_chat_state(empty_after) return False @@ -347,15 +384,18 @@ class ConversationTab(OneToOneTab): curses.curs_set(1) if (config.get_by_tabname('send_chat_states', self.general_jid) and (not self.input.get_text() - or not self.input.get_text().startswith('//'))): + or not self.input.get_text().startswith('//'))): if resource: self.send_chat_state('active') def on_info_win_size_changed(self): - if self.core.information_win_size >= self.height-3: + if self.core.information_win_size >= self.height - 3: return - self.text_win.resize(self.height-3-self.core.information_win_size - Tab.tab_win_height(), self.width, 1, 0) - self.get_info_header().resize(1, self.width, self.height-2-self.core.information_win_size - Tab.tab_win_height(), 0) + self.text_win.resize(self.height - 3 - self.core.information_win_size - + Tab.tab_win_height(), self.width, 1, 0) + self.get_info_header().resize( + 1, self.width, self.height - 2 - self.core.information_win_size - + Tab.tab_win_height(), 0) def get_text_window(self): return self.text_win @@ -375,12 +415,14 @@ class ConversationTab(OneToOneTab): res.append((0, contact.name)) return res + class DynamicConversationTab(ConversationTab): """ A conversation tab associated with one bare JID that can be “locked” to a full jid, and unlocked, as described in the XEP-0296. Only one DynamicConversationTab can be opened for a given jid. """ + def __init__(self, core, jid, resource=None): self.locked_resource = None self.name = safeJID(jid).bare @@ -388,8 +430,10 @@ class DynamicConversationTab(ConversationTab): self.lock(resource) ConversationTab.__init__(self, core, jid) self.info_header = windows.DynamicConversationInfoWin() - self.register_command('unlock', self.unlock_command, - shortdesc='Unlock the conversation from a particular resource.') + self.register_command( + 'unlock', + self.unlock_command, + shortdesc='Unlock the conversation from a particular resource.') self.resize() def get_info_header(self): @@ -399,7 +443,7 @@ class DynamicConversationTab(ConversationTab): """ Lock the tab to the resource. """ - assert(resource) + assert (resource) if resource != self.locked_resource: self.locked_resource = resource info = '\x19%s}' % dump_tuple(get_theme().COLOR_INFORMATION_TEXT) @@ -407,10 +451,11 @@ class DynamicConversationTab(ConversationTab): message = ('%(info)sConversation locked to ' '%(jid_c)s%(jid)s/%(resource)s%(info)s.') % { - 'info': info, - 'jid_c': jid_c, - 'jid': self.name, - 'resource': resource} + 'info': info, + 'jid_c': jid_c, + 'jid': self.name, + 'resource': resource + } self.add_message(message, typ=0) self.check_features() @@ -432,9 +477,10 @@ class DynamicConversationTab(ConversationTab): if from_: message = ('%(info)sConversation unlocked (received activity' ' from %(jid_c)s%(jid)s%(info)s).') % { - 'info': info, - 'jid_c': jid_c, - 'jid': from_} + 'info': info, + 'jid_c': jid_c, + 'jid': from_ + } self.add_message(message, typ=0) else: message = '%sConversation unlocked.' % info @@ -466,8 +512,8 @@ class DynamicConversationTab(ConversationTab): else: displayed_jid = self.name self.get_info_header().refresh(displayed_jid, roster[self.name], - self.text_win, self.chatstate, - ConversationTab.additional_information) + self.text_win, self.chatstate, + ConversationTab.additional_information) if display_info_win: self.info_win.refresh() @@ -483,16 +529,19 @@ class DynamicConversationTab(ConversationTab): else: displayed_jid = self.name self.get_info_header().refresh(displayed_jid, roster[self.name], - self.text_win, self.chatstate, ConversationTab.additional_information) + self.text_win, self.chatstate, + ConversationTab.additional_information) self.input.refresh() + class StaticConversationTab(ConversationTab): """ A conversation tab associated with one Full JID. It cannot be locked to an different resource or unlocked. """ + def __init__(self, core, jid): - assert(safeJID(jid).resource) + assert (safeJID(jid).resource) ConversationTab.__init__(self, core, jid) self.info_header = windows.ConversationInfoWin() self.resize() diff --git a/poezio/tabs/data_forms.py b/poezio/tabs/data_forms.py index c73f4922..d216d4ca 100644 --- a/poezio/tabs/data_forms.py +++ b/poezio/tabs/data_forms.py @@ -8,12 +8,14 @@ log = logging.getLogger(__name__) from poezio import windows from poezio.tabs import Tab + class DataFormsTab(Tab): """ A tab contaning various window type, displaying a form that the user needs to fill. """ plugin_commands = {} + def __init__(self, core, form, on_cancel, on_send, kwargs): Tab.__init__(self, core) self._form = form @@ -24,7 +26,8 @@ class DataFormsTab(Tab): for field in self._form: self.fields.append(field) self.topic_win = windows.Topic() - self.form_win = windows.FormWin(form, self.height-4, self.width, 1, 0) + self.form_win = windows.FormWin(form, self.height - 4, self.width, 1, + 0) self.help_win = windows.HelpText("Ctrl+Y: send form, Ctrl+G: cancel") self.help_win_dyn = windows.HelpText() self.key_func['KEY_UP'] = self.form_win.go_to_previous_input @@ -72,4 +75,3 @@ class DataFormsTab(Tab): self.help_win.refresh() self.help_win_dyn.refresh(self.form_win.get_help_message()) self.form_win.refresh() - diff --git a/poezio/tabs/listtab.py b/poezio/tabs/listtab.py index ed40241b..84dcc38b 100644 --- a/poezio/tabs/listtab.py +++ b/poezio/tabs/listtab.py @@ -49,8 +49,7 @@ class ListTab(Tab): self.key_func['KEY_LEFT'] = self.list_header.sel_column_left self.key_func['KEY_RIGHT'] = self.list_header.sel_column_right self.key_func[' '] = self.sort_by - self.register_command('close', self.close, - shortdesc='Close this tab.') + self.register_command('close', self.close, shortdesc='Close this tab.') self.resize() self.update_keys() self.update_commands() @@ -64,7 +63,6 @@ class ListTab(Tab): """ raise NotImplementedError - def refresh(self): if self.need_resize: self.resize() @@ -90,26 +88,27 @@ class ListTab(Tab): info_win_height = self.core.information_win_size tab_win_height = Tab.tab_win_height() - self.info_header.resize(1, self.width, - self.height - 2 - info_win_height - - tab_win_height, - 0) + self.info_header.resize( + 1, self.width, self.height - 2 - info_win_height - tab_win_height, + 0) column_size = self.get_columns_sizes() self.list_header.resize_columns(column_size) self.list_header.resize(1, self.width, 0, 0) self.listview.resize_columns(column_size) - self.listview.resize(self.height - 3 - info_win_height - tab_win_height, - self.width, 1, 0) - self.input.resize(1, self.width, self.height-1, 0) + self.listview.resize( + self.height - 3 - info_win_height - tab_win_height, self.width, 1, + 0) + self.input.resize(1, self.width, self.height - 1, 0) def on_slash(self): """ '/' is pressed, activate the input """ curses.curs_set(1) - self.input = windows.CommandInput("", self.reset_help_message, self.execute_slash_command) - self.input.resize(1, self.width, self.height-1, 0) - self.input.do_command("/") # we add the slash + self.input = windows.CommandInput("", self.reset_help_message, + self.execute_slash_command) + self.input.resize(1, self.width, self.height - 1, 0) + self.input.do_command("/") # we add the slash def close(self, arg=None): self.core.close_tab(self) @@ -118,7 +117,11 @@ class ListTab(Tab): """ If there's an error (retrieving the values etc) """ - self._error_message = 'Error: %(code)s - %(msg)s: %(body)s' % {'msg':msg, 'body':body, 'code':code} + self._error_message = 'Error: %(code)s - %(msg)s: %(body)s' % { + 'msg': msg, + 'body': body, + 'code': code + } self.info_header.message = self._error_message self.info_header.refresh() curses.doupdate() @@ -126,14 +129,12 @@ class ListTab(Tab): def sort_by(self): if self.list_header.get_order(): self.listview.sort_by_column( - col_name=self.list_header.get_sel_column(), - asc=False) + col_name=self.list_header.get_sel_column(), asc=False) self.list_header.set_order(False) self.list_header.refresh() else: self.listview.sort_by_column( - col_name=self.list_header.get_sel_column(), - asc=True) + col_name=self.list_header.get_sel_column(), asc=True) self.list_header.set_order(True) self.list_header.refresh() self.core.doupdate() @@ -144,7 +145,7 @@ class ListTab(Tab): return True curses.curs_set(0) self.input = self.default_help_message - self.input.resize(1, self.width, self.height-1, 0) + self.input.resize(1, self.width, self.height - 1, 0) return True def execute_slash_command(self, txt): @@ -167,10 +168,13 @@ class ListTab(Tab): return self.key_func[key]() def on_info_win_size_changed(self): - if self.core.information_win_size >= self.height-3: + if self.core.information_win_size >= self.height - 3: return - self.info_header.resize(1, self.width, self.height-2-self.core.information_win_size - Tab.tab_win_height(), 0) - self.listview.resize(self.height-3-self.core.information_win_size - Tab.tab_win_height(), self.width, 1, 0) + self.info_header.resize( + 1, self.width, self.height - 2 - self.core.information_win_size - + Tab.tab_win_height(), 0) + self.listview.resize(self.height - 3 - self.core.information_win_size - + Tab.tab_win_height(), self.width, 1, 0) def on_lose_focus(self): self.state = 'normal' @@ -197,5 +201,3 @@ class ListTab(Tab): def matching_names(self): return [(2, self.name)] - - diff --git a/poezio/tabs/muclisttab.py b/poezio/tabs/muclisttab.py index f14f4172..005f3fe1 100644 --- a/poezio/tabs/muclisttab.py +++ b/poezio/tabs/muclisttab.py @@ -11,6 +11,7 @@ from poezio.tabs import ListTab from slixmpp.plugins.xep_0030.stanza.items import DiscoItem + class MucListTab(ListTab): """ A tab listing rooms from a specific server, displaying various information, @@ -20,8 +21,7 @@ class MucListTab(ListTab): plugin_keys = {} def __init__(self, core, server): - ListTab.__init__(self, core, server.full, - "“j”: join room.", + ListTab.__init__(self, core, server.full, "“j”: join room.", 'Chatroom list on server %s (Loading)' % server, (('node-part', 0), ('name', 2), ('users', 3))) self.key_func['j'] = self.join_selected @@ -29,10 +29,12 @@ class MucListTab(ListTab): self.key_func['^M'] = self.join_selected def get_columns_sizes(self): - return {'node-part': int(self.width* 2 / 8), - 'name': int(self.width * 5 / 8), - 'users': self.width - int(self.width * 2 / 8) - - int(self.width * 5 / 8)} + return { + 'node-part': int(self.width * 2 / 8), + 'name': int(self.width * 5 / 8), + 'users': + self.width - int(self.width * 2 / 8) - int(self.width * 5 / 8) + } def join_selected_no_focus(self): return @@ -43,16 +45,18 @@ class MucListTab(ListTab): Used with command_list """ if iq['type'] == 'error': - self.set_error(iq['error']['type'], iq['error']['code'], iq['error']['text']) + self.set_error(iq['error']['type'], iq['error']['code'], + iq['error']['text']) return + def get_items(): substanza = iq['disco_items'] for item in substanza['substanzas']: if isinstance(item, DiscoItem): yield (item['jid'], item['node'], item['name']) - items = [(item[0].split('@')[0], - item[0], - item[2] or '', '') for item in get_items()] + + items = [(item[0].split('@')[0], item[0], item[2] or '', '') + for item in get_items()] self.listview.set_lines(items) self.info_header.message = 'Chatroom list on server %s' % self.name if self.core.current_tab() is self: @@ -67,4 +71,3 @@ class MucListTab(ListTab): if not row: return self.core.command.join(row[1]) - diff --git a/poezio/tabs/muctab.py b/poezio/tabs/muctab.py index 4438e015..94b521ba 100644 --- a/poezio/tabs/muctab.py +++ b/poezio/tabs/muctab.py @@ -34,7 +34,6 @@ from poezio.theming import get_theme, dump_tuple from poezio.user import User from poezio.core.structs import Completion, Status - NS_MUC_USER = 'http://jabber.org/protocol/muc#user' STATUS_XPATH = '{%s}x/{%s}status' % (NS_MUC_USER, NS_MUC_USER) @@ -129,11 +128,14 @@ class MucTab(ChatTab): seconds = delta.seconds + delta.days * 24 * 3600 else: seconds = 0 - muc.join_groupchat(self.core, self.name, self.own_nick, - self.password, - status=status.message, - show=status.show, - seconds=seconds) + muc.join_groupchat( + self.core, + self.name, + self.own_nick, + self.password, + status=status.message, + show=status.show, + seconds=seconds) def leave_room(self, message): if self.joined: @@ -152,8 +154,10 @@ class MucTab(ChatTab): 'You (\x19%(color)s}%(nick)s\x19%(info_col)s})' ' left the room' ' (\x19o%(reason)s\x19%(info_col)s})') % { - 'info_col': info_col, 'reason': message, - 'spec': char_quit, 'color': color, + 'info_col': info_col, + 'reason': message, + 'spec': char_quit, + 'color': color, 'color_spec': spec_col, 'nick': self.own_nick, } @@ -162,60 +166,77 @@ class MucTab(ChatTab): 'You (\x19%(color)s}%(nick)s\x19%(info_col)s})' ' left the room') % { 'info_col': info_col, - 'spec': char_quit, 'color': color, + 'spec': char_quit, + 'color': color, 'color_spec': spec_col, 'nick': self.own_nick, } self.add_message(msg, typ=2) self.disconnect() - muc.leave_groupchat(self.core.xmpp, self.name, self.own_nick, message) + muc.leave_groupchat(self.core.xmpp, self.name, self.own_nick, + message) self.core.disable_private_tabs(self.name, reason=msg) else: - muc.leave_groupchat(self.core.xmpp, self.name, self.own_nick, message) + muc.leave_groupchat(self.core.xmpp, self.name, self.own_nick, + message) def change_affiliation(self, nick_or_jid, affiliation, reason=''): """ Change the affiliation of a nick or JID """ + def callback(iq): if iq['type'] == 'error': - self.core.information("Could not set affiliation '%s' for '%s'." % ( - affiliation, nick_or_jid), "Warning") + self.core.information( + "Could not set affiliation '%s' for '%s'." % + (affiliation, nick_or_jid), "Warning") + if not self.joined: return valid_affiliations = ('outcast', 'none', 'member', 'admin', 'owner') if affiliation not in valid_affiliations: - return self.core.information('The affiliation must be one of ' + ', '.join(valid_affiliations), + return self.core.information('The affiliation must be one of ' + + ', '.join(valid_affiliations), 'Error') if nick_or_jid in [user.nick for user in self.users]: - res = muc.set_user_affiliation(self.core.xmpp, self.name, - affiliation, nick=nick_or_jid, - callback=callback, reason=reason) + res = muc.set_user_affiliation( + self.core.xmpp, + self.name, + affiliation, + nick=nick_or_jid, + callback=callback, + reason=reason) else: - res = muc.set_user_affiliation(self.core.xmpp, self.name, - affiliation, jid=safeJID(nick_or_jid), - callback=callback, reason=reason) + res = muc.set_user_affiliation( + self.core.xmpp, + self.name, + affiliation, + jid=safeJID(nick_or_jid), + callback=callback, + reason=reason) def change_role(self, nick, role, reason=''): """ Change the role of a nick """ + def callback(iq): if iq['type'] == 'error': - self.core.information("Could not set role '%s' for '%s'." % ( - role, nick), "Warning") + self.core.information("Could not set role '%s' for '%s'." % + (role, nick), "Warning") + valid_roles = ('none', 'visitor', 'participant', 'moderator') if not self.joined or role not in valid_roles: - return self.core.information('The role must be one of ' + ', '.join(valid_roles), - 'Error') + return self.core.information( + 'The role must be one of ' + ', '.join(valid_roles), 'Error') if not safeJID(self.name + '/' + nick): return self.core.information('Invalid nick', 'Info') - muc.set_user_role(self.core.xmpp, self.name, nick, reason, role, - callback=callback) + muc.set_user_role( + self.core.xmpp, self.name, nick, reason, role, callback=callback) @refresh_wrapper.conditional def print_info(self, nick): @@ -228,26 +249,24 @@ class MucTab(ChatTab): inf = '\x19' + dump_tuple(theme.COLOR_INFORMATION_TEXT) + '}' if user.jid: user_jid = '%s (\x19%s}%s\x19o%s)' % ( - inf, - dump_tuple(theme.COLOR_MUC_JID), - user.jid, - inf) + inf, dump_tuple(theme.COLOR_MUC_JID), user.jid, inf) else: user_jid = '' info = ('\x19%(user_col)s}%(nick)s\x19o%(jid)s%(info)s: show: ' '\x19%(show_col)s}%(show)s\x19o%(info)s, affiliation: ' '\x19%(role_col)s}%(affiliation)s\x19o%(info)s, role: ' '\x19%(role_col)s}%(role)s\x19o%(status)s') % { - 'user_col': dump_tuple(user.color), - 'nick': nick, - 'jid': user_jid, - 'info': inf, - 'show_col': dump_tuple(theme.color_show(user.show)), - 'show': user.show or 'Available', - 'role_col': dump_tuple(theme.color_role(user.role)), - 'affiliation': user.affiliation or 'None', - 'role': user.role or 'None', - 'status': '\n%s' % user.status if user.status else ''} + 'user_col': dump_tuple(user.color), + 'nick': nick, + 'jid': user_jid, + 'info': inf, + 'show_col': dump_tuple(theme.color_show(user.show)), + 'show': user.show or 'Available', + 'role_col': dump_tuple(theme.color_role(user.role)), + 'affiliation': user.affiliation or 'None', + 'role': user.role or 'None', + 'status': '\n%s' % user.status if user.status else '' + } self.add_message(info, typ=0) return True @@ -266,21 +285,24 @@ class MucTab(ChatTab): user = self.get_user_by_name(self.topic_from) if user: user_text = dump_tuple(user.color) - user_string = '\x19%s}(set by \x19%s}%s\x19%s})' % ( - info_text, user_text, user.nick, info_text) + user_string = '\x19%s}(set by \x19%s}%s\x19%s})' % (info_text, + user_text, + user.nick, + info_text) else: user_string = self.topic_from else: user_string = '' self._text_buffer.add_message( - "\x19%s}The subject of the room is: \x19%s}%s %s" % - (info_text, norm_text, self.topic, user_string)) + "\x19%s}The subject of the room is: \x19%s}%s %s" % + (info_text, norm_text, self.topic, user_string)) @refresh_wrapper.always def recolor(self, random_colors=False): """Recolor the current MUC users""" - deterministic = config.get_by_tabname('deterministic_nick_colors', self.name) + deterministic = config.get_by_tabname('deterministic_nick_colors', + self.name) if deterministic: for user in self.users: if user is self.own_user: @@ -321,14 +343,16 @@ class MucTab(ChatTab): return False if color == 'unset': if config.remove_and_save(nick, 'muc_colors'): - self.core.information('Color for nick %s unset' % (nick), 'Info') + self.core.information('Color for nick %s unset' % (nick), + 'Info') else: if color == 'random': color = random.choice(list(xhtml.colors)) if user: user.change_color(color) config.set_and_save(nick, color, 'muc_colors') - nick_color_aliases = config.get_by_tabname('nick_color_aliases', self.name) + nick_color_aliases = config.get_by_tabname('nick_color_aliases', + self.name) if nick_color_aliases: # if any user in the room has a nick which is an alias of the # nick, update its color @@ -347,9 +371,9 @@ class MucTab(ChatTab): return False self.input.do_command(key, raw=raw) empty_after = self.input.get_text() == '' - empty_after = empty_after or (self.input.get_text().startswith('/') - and not - self.input.get_text().startswith('//')) + empty_after = empty_after or ( + self.input.get_text().startswith('/') + and not self.input.get_text().startswith('//')) self.send_composing_chat_state(empty_after) return False @@ -381,7 +405,8 @@ class MucTab(ChatTab): and not config.get('show_useless_separator')): self.text_win.remove_line_separator() curses.curs_set(1) - if self.joined and config.get_by_tabname('send_chat_states', + if self.joined and config.get_by_tabname( + 'send_chat_states', self.general_jid) and not self.input.get_text(): self.send_chat_state('active') @@ -416,7 +441,8 @@ class MucTab(ChatTab): """ Batch-process all the initial presences """ - deterministic = config.get_by_tabname('deterministic_nick_colors', self.name) + deterministic = config.get_by_tabname('deterministic_nick_colors', + self.name) for stanza in self.presence_buffer: try: @@ -437,10 +463,11 @@ class MucTab(ChatTab): """ Presence received while we are not in the room (before code=110) """ - from_nick, from_room, affiliation, show, status, role, jid, typ = dissect_presence(presence) + from_nick, from_room, affiliation, show, status, role, jid, typ = dissect_presence( + presence) user_color = self.search_for_color(from_nick) - new_user = User(from_nick, affiliation, show, - status, role, jid, deterministic, user_color) + new_user = User(from_nick, affiliation, show, status, role, jid, + deterministic, user_color) self.users.append(new_user) self.core.events.trigger('muc_join', presence, self) if own: @@ -475,44 +502,43 @@ class MucTab(ChatTab): info_col = dump_tuple(get_theme().COLOR_INFORMATION_TEXT) warn_col = dump_tuple(get_theme().COLOR_WARNING_TEXT) spec_col = dump_tuple(get_theme().COLOR_JOIN_CHAR) - enable_message = ( - '\x19%(color_spec)s}%(spec)s\x19%(info_col)s} You ' - '(\x19%(nick_col)s}%(nick)s\x19%(info_col)s}) joined' - ' the room') % { - 'nick': from_nick, - 'spec': get_theme().CHAR_JOIN, - 'color_spec': spec_col, - 'nick_col': color, - 'info_col': info_col, - } + enable_message = ('\x19%(color_spec)s}%(spec)s\x19%(info_col)s} You ' + '(\x19%(nick_col)s}%(nick)s\x19%(info_col)s}) joined' + ' the room') % { + 'nick': from_nick, + 'spec': get_theme().CHAR_JOIN, + 'color_spec': spec_col, + 'nick_col': color, + 'info_col': info_col, + } self.add_message(enable_message, typ=2) self.core.enable_private_tabs(self.name, enable_message) if '201' in status_codes: self.add_message( - '\x19%(info_col)s}Info: The room ' - 'has been created' % - {'info_col': info_col}, + '\x19%(info_col)s}Info: The room ' + 'has been created' % {'info_col': info_col}, typ=0) if '170' in status_codes: self.add_message( - '\x19%(warn_col)s}Warning:\x19%(info_col)s}' - ' This room is publicly logged' % - {'info_col': info_col, - 'warn_col': warn_col}, + '\x19%(warn_col)s}Warning:\x19%(info_col)s}' + ' This room is publicly logged' % + {'info_col': info_col, + 'warn_col': warn_col}, typ=0) if '100' in status_codes: self.add_message( - '\x19%(warn_col)s}Warning:\x19%(info_col)s}' - ' This room is not anonymous.' % - {'info_col': info_col, - 'warn_col': warn_col}, + '\x19%(warn_col)s}Warning:\x19%(info_col)s}' + ' This room is not anonymous.' % + {'info_col': info_col, + 'warn_col': warn_col}, typ=0) def handle_presence_joined(self, presence, status_codes): """ Handle new presences when we are already in the room """ - from_nick, from_room, affiliation, show, status, role, jid, typ = dissect_presence(presence) + from_nick, from_room, affiliation, show, status, role, jid, typ = dissect_presence( + presence) change_nick = '303' in status_codes kick = '307' in status_codes and typ == 'unavailable' ban = '301' in status_codes and typ == 'unavailable' @@ -523,8 +549,8 @@ class MucTab(ChatTab): if not user and typ != "unavailable": user_color = self.search_for_color(from_nick) self.core.events.trigger('muc_join', presence, self) - self.on_user_join(from_nick, affiliation, show, status, role, - jid, user_color) + self.on_user_join(from_nick, affiliation, show, status, role, jid, + user_color) elif user is None: log.error('BUG: User %s in %s is None', from_nick, self.name) return @@ -533,13 +559,13 @@ class MucTab(ChatTab): self.on_user_nick_change(presence, user, from_nick, from_room) elif ban: self.core.events.trigger('muc_ban', presence, self) - self.core.on_user_left_private_conversation(from_room, - user, status) + self.core.on_user_left_private_conversation( + from_room, user, status) self.on_user_banned(presence, user, from_nick) elif kick: self.core.events.trigger('muc_kick', presence, self) - self.core.on_user_left_private_conversation(from_room, - user, status) + self.core.on_user_left_private_conversation( + from_room, user, status) self.on_user_kicked(presence, user, from_nick) elif shutdown: self.core.events.trigger('muc_shutdown', presence, self) @@ -549,38 +575,40 @@ class MucTab(ChatTab): self.on_non_member_kicked() # user quit elif typ == 'unavailable': - self.on_user_leave_groupchat(user, jid, status, - from_nick, from_room) + self.on_user_leave_groupchat(user, jid, status, from_nick, + from_room) # status change else: - self.on_user_change_status(user, from_nick, from_room, - affiliation, role, show, status) + self.on_user_change_status(user, from_nick, from_room, affiliation, + role, show, status) def on_non_member_kicked(self): """We have been kicked because the MUC is members-only""" self.add_message( - '\x19%(info_col)s}You have been kicked because you ' - 'are not a member and the room is now members-only.' % { - 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)}, + '\x19%(info_col)s}You have been kicked because you ' + 'are not a member and the room is now members-only.' % + {'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)}, typ=2) self.disconnect() def on_muc_shutdown(self): """We have been kicked because the MUC service is shutting down""" self.add_message( - '\x19%(info_col)s}You have been kicked because the' - ' MUC service is shutting down.' % { - 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)}, + '\x19%(info_col)s}You have been kicked because the' + ' MUC service is shutting down.' % + {'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)}, typ=2) self.disconnect() - def on_user_join(self, from_nick, affiliation, show, status, role, jid, color): + def on_user_join(self, from_nick, affiliation, show, status, role, jid, + color): """ When a new user joins the groupchat """ - deterministic = config.get_by_tabname('deterministic_nick_colors', self.name) - user = User(from_nick, affiliation, - show, status, role, jid, deterministic, color) + deterministic = config.get_by_tabname('deterministic_nick_colors', + self.name) + user = User(from_nick, affiliation, show, status, role, jid, + deterministic, color) bisect.insort_left(self.users, user) hide_exit_join = config.get_by_tabname('hide_exit_join', self.general_jid) @@ -596,27 +624,30 @@ class MucTab(ChatTab): if not jid.full: msg = ('\x19%(color_spec)s}%(spec)s \x19%(color)s}%(nick)s' '\x19%(info_col)s} joined the room') % { - 'nick': from_nick, 'spec': char_join, - 'color': color, - 'info_col': info_col, - 'color_spec': spec_col, - } + 'nick': from_nick, + 'spec': char_join, + 'color': color, + 'info_col': info_col, + 'color_spec': spec_col, + } else: msg = ('\x19%(color_spec)s}%(spec)s \x19%(color)s}%(nick)s' '\x19%(info_col)s} (\x19%(jid_color)s}%(jid)s\x19' '%(info_col)s}) joined the room') % { - 'spec': char_join, 'nick': from_nick, - 'color':color, 'jid':jid.full, - 'info_col': info_col, - 'jid_color': dump_tuple(get_theme().COLOR_MUC_JID), - 'color_spec': spec_col, - } + 'spec': char_join, + 'nick': from_nick, + 'color': color, + 'jid': jid.full, + 'info_col': info_col, + 'jid_color': dump_tuple(get_theme().COLOR_MUC_JID), + 'color_spec': spec_col, + } self.add_message(msg, typ=2) self.core.on_user_rejoined_private_conversation(self.name, from_nick) def on_user_nick_change(self, presence, user, from_nick, from_room): - new_nick = presence.xml.find('{%s}x/{%s}item' % (NS_MUC_USER, NS_MUC_USER) - ).attrib['nick'] + new_nick = presence.xml.find('{%s}x/{%s}item' % + (NS_MUC_USER, NS_MUC_USER)).attrib['nick'] if user.nick == self.own_nick: self.own_nick = new_nick # also change our nick in all private discussions of this room @@ -624,8 +655,8 @@ class MucTab(ChatTab): else: color = config.get_by_tabname(new_nick, 'muc_colors') if color != '': - deterministic = config.get_by_tabname('deterministic_nick_colors', - self.name) + deterministic = config.get_by_tabname( + 'deterministic_nick_colors', self.name) user.change_color(color, deterministic) user.change_nick(new_nick) self.users.remove(user) @@ -637,11 +668,15 @@ class MucTab(ChatTab): else: color = 3 info_col = dump_tuple(get_theme().COLOR_INFORMATION_TEXT) - self.add_message('\x19%(color)s}%(old)s\x19%(info_col)s} is' - ' now known as \x19%(color)s}%(new)s' % { - 'old':from_nick, 'new':new_nick, - 'color':color, 'info_col': info_col}, - typ=2) + self.add_message( + '\x19%(color)s}%(old)s\x19%(info_col)s} is' + ' now known as \x19%(color)s}%(new)s' % { + 'old': from_nick, + 'new': new_nick, + 'color': color, + 'info_col': info_col + }, + typ=2) # rename the private tabs if needed self.core.rename_private_tabs(self.name, from_nick, user) @@ -662,16 +697,20 @@ class MucTab(ChatTab): info_col = dump_tuple(get_theme().COLOR_INFORMATION_TEXT) char_kick = get_theme().CHAR_KICK - if from_nick == self.own_nick: # we are banned + if from_nick == self.own_nick: # we are banned if by: kick_msg = ('\x191}%(spec)s \x193}You\x19%(info_col)s}' ' have been banned by \x194}%(by)s') % { - 'spec': char_kick, 'by': by, - 'info_col': info_col} + 'spec': char_kick, + 'by': by, + 'info_col': info_col + } else: kick_msg = ('\x191}%(spec)s \x193}You\x19' '%(info_col)s} have been banned.') % { - 'spec': char_kick, 'info_col': info_col} + 'spec': char_kick, + 'info_col': info_col + } self.core.disable_private_tabs(self.name, reason=kick_msg) self.disconnect() self.refresh_tab_win() @@ -684,11 +723,9 @@ class MucTab(ChatTab): if delay <= 0: muc.join_groupchat(self.core, self.name, self.own_nick) else: - self.core.add_timed_event(timed_events.DelayedEvent( - delay, - muc.join_groupchat, - self.core, - self.name, + self.core.add_timed_event( + timed_events.DelayedEvent(delay, muc.join_groupchat, + self.core, self.name, self.own_nick)) else: @@ -702,18 +739,26 @@ class MucTab(ChatTab): kick_msg = ('\x191}%(spec)s \x19%(color)s}' '%(nick)s\x19%(info_col)s} ' 'has been banned by \x194}%(by)s') % { - 'spec': char_kick, 'nick': from_nick, - 'color': color, 'by': by, - 'info_col': info_col} + 'spec': char_kick, + 'nick': from_nick, + 'color': color, + 'by': by, + 'info_col': info_col + } else: kick_msg = ('\x191}%(spec)s \x19%(color)s}%(nick)s' '\x19%(info_col)s} has been banned') % { - 'spec': char_kick, 'nick': from_nick, - 'color': color, 'info_col': info_col} + 'spec': char_kick, + 'nick': from_nick, + 'color': color, + 'info_col': info_col + } if reason is not None and reason.text: kick_msg += ('\x19%(info_col)s} Reason: \x196}' '%(reason)s\x19%(info_col)s}') % { - 'reason': reason.text, 'info_col': info_col} + 'reason': reason.text, + 'info_col': info_col + } self.add_message(kick_msg, typ=2) def on_user_kicked(self, presence, user, from_nick): @@ -730,18 +775,21 @@ class MucTab(ChatTab): char_kick = get_theme().CHAR_KICK if actor_elem is not None: by = actor_elem.get('nick') or actor_elem.get('jid') - if from_nick == self.own_nick: # we are kicked + if from_nick == self.own_nick: # we are kicked if by: kick_msg = ('\x191}%(spec)s \x193}You\x19' '%(info_col)s} have been kicked' ' by \x193}%(by)s') % { - 'spec': char_kick, 'by': by, - 'info_col': info_col} + 'spec': char_kick, + 'by': by, + 'info_col': info_col + } else: kick_msg = ('\x191}%(spec)s \x193}You\x19%(info_col)s}' ' have been kicked.') % { - 'spec': char_kick, - 'info_col': info_col} + 'spec': char_kick, + 'info_col': info_col + } self.core.disable_private_tabs(self.name, reason=kick_msg) self.disconnect() self.refresh_tab_win() @@ -755,12 +803,10 @@ class MucTab(ChatTab): if delay <= 0: muc.join_groupchat(self.core, self.name, self.own_nick) else: - self.core.add_timed_event(timed_events.DelayedEvent( - delay, - muc.join_groupchat, - self.core, - self.name, - self.own_nick)) + self.core.add_timed_event( + timed_events.DelayedEvent(delay, muc.join_groupchat, + self.core, self.name, + self.own_nick)) else: if config.get_by_tabname('display_user_color_in_join_part', self.general_jid): @@ -771,17 +817,26 @@ class MucTab(ChatTab): kick_msg = ('\x191}%(spec)s \x19%(color)s}%(nick)s' '\x19%(info_col)s} has been kicked by ' '\x193}%(by)s') % { - 'spec': char_kick, 'nick':from_nick, - 'color':color, 'by':by, 'info_col': info_col} + 'spec': char_kick, + 'nick': from_nick, + 'color': color, + 'by': by, + 'info_col': info_col + } else: kick_msg = ('\x191}%(spec)s \x19%(color)s}%(nick)s' '\x19%(info_col)s} has been kicked') % { - 'spec': char_kick, 'nick': from_nick, - 'color':color, 'info_col': info_col} + 'spec': char_kick, + 'nick': from_nick, + 'color': color, + 'info_col': info_col + } if reason is not None and reason.text: kick_msg += ('\x19%(info_col)s} Reason: \x196}' '%(reason)s') % { - 'reason': reason.text, 'info_col': info_col} + 'reason': reason.text, + 'info_col': info_col + } self.add_message(kick_msg, typ=2) def on_user_leave_groupchat(self, user, jid, status, from_nick, from_room): @@ -812,35 +867,39 @@ class MucTab(ChatTab): leave_msg = ('\x19%(color_spec)s}%(spec)s \x19%(color)s}' '%(nick)s\x19%(info_col)s} has left the ' 'room') % { - 'nick':from_nick, 'color':color, - 'spec':get_theme().CHAR_QUIT, - 'info_col': info_col, - 'color_spec': spec_col} + 'nick': from_nick, + 'color': color, + 'spec': get_theme().CHAR_QUIT, + 'info_col': info_col, + 'color_spec': spec_col + } else: jid_col = dump_tuple(get_theme().COLOR_MUC_JID) leave_msg = ('\x19%(color_spec)s}%(spec)s \x19%(color)s}' '%(nick)s\x19%(info_col)s} (\x19%(jid_col)s}' '%(jid)s\x19%(info_col)s}) has left the ' 'room') % { - 'spec':get_theme().CHAR_QUIT, - 'nick':from_nick, 'color':color, - 'jid':jid.full, 'info_col': info_col, - 'color_spec': spec_col, - 'jid_col': jid_col} + 'spec': get_theme().CHAR_QUIT, + 'nick': from_nick, + 'color': color, + 'jid': jid.full, + 'info_col': info_col, + 'color_spec': spec_col, + 'jid_col': jid_col + } if status: leave_msg += ' (\x19o%s\x19%s})' % (status, info_col) self.add_message(leave_msg, typ=2) - self.core.on_user_left_private_conversation(from_room, user, - status) + self.core.on_user_left_private_conversation(from_room, user, status) - def on_user_change_status( - self, user, from_nick, from_room, affiliation, role, show, status): + def on_user_change_status(self, user, from_nick, from_room, affiliation, + role, show, status): """ When an user changes her status """ # build the message - display_message = False # flag to know if something significant enough - # to be displayed has changed + display_message = False # flag to know if something significant enough + # to be displayed has changed if config.get_by_tabname('display_user_color_in_join_part', self.general_jid): color = dump_tuple(user.color) @@ -848,12 +907,15 @@ class MucTab(ChatTab): color = 3 if from_nick == self.own_nick: msg = '\x19%(color)s}You\x19%(info_col)s} changed: ' % { - 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT), - 'color': color} + 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT), + 'color': color + } else: msg = '\x19%(color)s}%(nick)s\x19%(info_col)s} changed: ' % { - 'nick': from_nick, 'color': color, - 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)} + 'nick': from_nick, + 'color': color, + 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT) + } if affiliation != user.affiliation: msg += 'affiliation: %s, ' % affiliation display_message = True @@ -873,7 +935,7 @@ class MucTab(ChatTab): display_message = True if not display_message: return - msg = msg[:-2] # remove the last ", " + msg = msg[:-2] # remove the last ", " hide_status_change = config.get_by_tabname('hide_status_change', self.general_jid) if hide_status_change < -1: @@ -891,8 +953,8 @@ class MucTab(ChatTab): role != user.role): # display the message in the room self._text_buffer.add_message(msg) - self.core.on_user_changed_status_in_private('%s/%s' % - (from_room, from_nick), + self.core.on_user_changed_status_in_private('%s/%s' % (from_room, + from_nick), Status(show, status)) self.users.remove(user) # finally, effectively change the user status @@ -922,7 +984,7 @@ class MucTab(ChatTab): Log the messages in the archives, if it needs to be """ - if time is None and self.joined: # don't log the history messages + if time is None and self.joined: # don't log the history messages if not logger.log_message(self.name, nickname, txt, typ=typ): self.core.information('Unable to write in the log file', 'Error') @@ -965,32 +1027,44 @@ class MucTab(ChatTab): args['user'] = kwargs['forced_user'] if (not time and nickname and nickname != self.own_nick - and self.state != 'current'): - if (self.state != 'highlight' and - config.get_by_tabname('notify_messages', self.name)): + and self.state != 'current'): + if (self.state != 'highlight' + and config.get_by_tabname('notify_messages', self.name)): self.state = 'message' if time and not txt.startswith('/me'): txt = '\x19%(info_col)s}%(txt)s' % { - 'txt': txt, - 'info_col': dump_tuple(get_theme().COLOR_LOG_MSG)} + 'txt': txt, + 'info_col': dump_tuple(get_theme().COLOR_LOG_MSG) + } elif not nickname: txt = '\x19%(info_col)s}%(txt)s' % { - 'txt': txt, - 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)} - elif not kwargs.get('highlight'): # TODO + 'txt': txt, + 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT) + } + elif not kwargs.get('highlight'): # TODO args['highlight'] = self.do_highlight(txt, time, nickname) time = time or datetime.now() self._text_buffer.add_message(txt, time, nickname, **args) return args.get('highlight', False) - def modify_message(self, txt, old_id, new_id, - time=None, nickname=None, user=None, jid=None): + def modify_message(self, + txt, + old_id, + new_id, + time=None, + nickname=None, + user=None, + jid=None): self.log_message(txt, nickname, time=time, typ=1) highlight = self.do_highlight(txt, time, nickname, corrected=True) - message = self._text_buffer.modify_message(txt, old_id, new_id, - highlight=highlight, - time=time, user=user, - jid=jid) + message = self._text_buffer.modify_message( + txt, + old_id, + new_id, + highlight=highlight, + time=time, + user=user, + jid=jid) if message: self.text_win.modify_message(old_id, message) return highlight @@ -1000,11 +1074,13 @@ class MucTab(ChatTab): return [(1, safeJID(self.name).user), (3, self.name)] def enable_self_ping_event(self): - delay = config.get_by_tabname("self_ping_delay", self.general_jid, default=0) - if delay <= 0: # use 0 or some negative value to disable it + delay = config.get_by_tabname( + "self_ping_delay", self.general_jid, default=0) + if delay <= 0: # use 0 or some negative value to disable it return self.disable_self_ping_event() - self.self_ping_event = timed_events.DelayedEvent(delay, self.send_self_ping) + self.self_ping_event = timed_events.DelayedEvent( + delay, self.send_self_ping) self.core.add_timed_event(self.self_ping_event) def disable_self_ping_event(self): @@ -1014,16 +1090,17 @@ class MucTab(ChatTab): def send_self_ping(self): to = self.name + "/" + self.own_nick - self.core.xmpp.plugin['xep_0199'].send_ping(jid=to, - callback=self.on_self_ping_result, - timeout_callback=self.on_self_ping_failed, - timeout=60) + self.core.xmpp.plugin['xep_0199'].send_ping( + jid=to, + callback=self.on_self_ping_result, + timeout_callback=self.on_self_ping_failed, + timeout=60) def on_self_ping_result(self, iq): if iq["type"] == "error" and iq["error"]["condition"] != "feature-not-implemented": self.command_cycle(iq["error"]["text"] or "not in this room") self.core.refresh_window() - else: # Re-send a self-ping in a few seconds + else: # Re-send a self-ping in a few seconds self.enable_self_ping_event() def search_for_color(self, nick): @@ -1035,7 +1112,8 @@ class MucTab(ChatTab): color = config.get_by_tabname(nick, 'muc_colors') if color != '': return color - nick_color_aliases = config.get_by_tabname('nick_color_aliases', self.name) + nick_color_aliases = config.get_by_tabname('nick_color_aliases', + self.name) if nick_color_aliases: nick_alias = re.sub('^_*(.*?)_*$', '\\1', nick) color = config.get_by_tabname(nick_alias, 'muc_colors') @@ -1088,26 +1166,23 @@ class MucTab(ChatTab): tab_win_height = Tab.tab_win_height() info_win_height = self.core.information_win_size - - self.user_win.resize(self.height - 3 - info_win_height - - tab_win_height, - self.width - (self.width // 10) * 9 - 1, - 1, - (self.width // 10) * 9 + 1) - self.v_separator.resize(self.height - 3 - info_win_height - tab_win_height, - 1, 1, 9 * (self.width // 10)) + self.user_win.resize( + self.height - 3 - info_win_height - tab_win_height, self.width - + (self.width // 10) * 9 - 1, 1, (self.width // 10) * 9 + 1) + self.v_separator.resize( + self.height - 3 - info_win_height - tab_win_height, 1, 1, + 9 * (self.width // 10)) self.topic_win.resize(1, self.width, 0, 0) - self.text_win.resize(self.height - 3 - info_win_height - - tab_win_height, - text_width, 1, 0) + self.text_win.resize( + self.height - 3 - info_win_height - tab_win_height, text_width, 1, + 0) self.text_win.rebuild_everything(self._text_buffer) - self.info_header.resize(1, self.width, - self.height - 2 - info_win_height - - tab_win_height, - 0) - self.input.resize(1, self.width, self.height-1, 0) + self.info_header.resize( + 1, self.width, self.height - 2 - info_win_height - tab_win_height, + 0) + self.input.resize(1, self.width, self.height - 1, 0) def refresh(self): if self.need_resize: @@ -1131,40 +1206,40 @@ class MucTab(ChatTab): self.input.refresh() def on_info_win_size_changed(self): - if self.core.information_win_size >= self.height-3: + if self.core.information_win_size >= self.height - 3: return if config.get("hide_user_list"): text_width = self.width else: - text_width = (self.width//10)*9 - self.user_win.resize(self.height - 3 - self.core.information_win_size - - Tab.tab_win_height(), - self.width - (self.width // 10) * 9 - 1, - 1, + text_width = (self.width // 10) * 9 + self.user_win.resize(self.height - 3 - self.core.information_win_size - + Tab.tab_win_height(), + self.width - (self.width // 10) * 9 - 1, 1, (self.width // 10) * 9 + 1) - self.v_separator.resize(self.height - 3 - self.core.information_win_size - Tab.tab_win_height(), - 1, 1, 9 * (self.width // 10)) - self.text_win.resize(self.height - 3 - self.core.information_win_size - - Tab.tab_win_height(), - text_width, 1, 0) - self.info_header.resize(1, self.width, - self.height-2-self.core.information_win_size - - Tab.tab_win_height(), - 0) + self.v_separator.resize( + self.height - 3 - self.core.information_win_size - + Tab.tab_win_height(), 1, 1, 9 * (self.width // 10)) + self.text_win.resize(self.height - 3 - self.core.information_win_size - + Tab.tab_win_height(), text_width, 1, 0) + self.info_header.resize( + 1, self.width, self.height - 2 - self.core.information_win_size - + Tab.tab_win_height(), 0) + def do_highlight(self, txt, time, nickname, corrected=False): """ Set the tab color and returns the nick color """ highlighted = False - if (not time or corrected) and nickname and nickname != self.own_nick and self.joined: + if (not time or corrected + ) and nickname and nickname != self.own_nick and self.joined: if re.search(r'\b' + self.own_nick.lower() + r'\b', txt.lower()): if self.state != 'current': self.state = 'highlight' highlighted = True else: - highlight_words = config.get_by_tabname('highlight_on', - self.general_jid) + highlight_words = config.get_by_tabname( + 'highlight_on', self.general_jid) highlight_words = highlight_words.split(':') for word in highlight_words: if word and word.lower() in txt.lower(): @@ -1205,11 +1280,11 @@ class MucTab(ChatTab): """ /configure """ + def on_form_received(form): if not form: self.core.information( - 'Could not retrieve the configuration form', - 'Error') + 'Could not retrieve the configuration form', 'Error') return self.core.open_new_form(form, self.cancel_config, self.send_config) @@ -1254,17 +1329,18 @@ class MucTab(ChatTab): """ /version """ + def callback(res): if not res: return self.core.information('Could not get the software ' - 'version from %s' % (jid,), + 'version from %s' % (jid, ), 'Warning') version = '%s is running %s version %s on %s' % ( - jid, - res.get('name') or 'an unknown software', - res.get('version') or 'unknown', - res.get('os') or 'an unknown platform') + jid, res.get('name') or 'an unknown software', + res.get('version') or 'unknown', + res.get('os') or 'an unknown platform') self.core.information(version, 'Info') + if args is None: return self.core.command.help('version') nick = args[0] @@ -1273,8 +1349,7 @@ class MucTab(ChatTab): jid = safeJID(jid + '/' + nick) else: jid = safeJID(nick) - fixes.get_version(self.core.xmpp, jid, - callback=callback) + fixes.get_version(self.core.xmpp, jid, callback=callback) @command_args_parser.quoted(1) def command_nick(self, args): @@ -1290,8 +1365,7 @@ class MucTab(ChatTab): current_status = self.core.get_status() if not safeJID(self.name + '/' + nick): return self.core.information('Invalid nick', 'Info') - muc.change_nick(self.core, self.name, nick, - current_status.message, + muc.change_nick(self.core, self.name, nick, current_status.message, current_status.show) @command_args_parser.quoted(0, 1, ['']) @@ -1323,7 +1397,7 @@ class MucTab(ChatTab): /query [message] """ if args is None: - return self.core.command.help('query') + return self.core.command.help('query') nick = args[0] r = None for user in self.users: @@ -1332,7 +1406,7 @@ class MucTab(ChatTab): if r and len(args) == 2: msg = args[1] self.core.current_tab().command_say( - xhtml.convert_simple_to_full_colors(msg)) + xhtml.convert_simple_to_full_colors(msg)) if not r: self.core.information("Cannot find user: %s" % nick, 'Error') @@ -1355,11 +1429,11 @@ class MucTab(ChatTab): return aff = { - 'owner': get_theme().CHAR_AFFILIATION_OWNER, - 'admin': get_theme().CHAR_AFFILIATION_ADMIN, - 'member': get_theme().CHAR_AFFILIATION_MEMBER, - 'none': get_theme().CHAR_AFFILIATION_NONE, - } + 'owner': get_theme().CHAR_AFFILIATION_OWNER, + 'admin': get_theme().CHAR_AFFILIATION_ADMIN, + 'member': get_theme().CHAR_AFFILIATION_MEMBER, + 'none': get_theme().CHAR_AFFILIATION_NONE, + } colors = {} colors["visitor"] = dump_tuple(get_theme().COLOR_USER_VISITOR) @@ -1372,8 +1446,9 @@ class MucTab(ChatTab): affiliation = aff.get(user.affiliation, get_theme().CHAR_AFFILIATION_NONE) color = colors.get(user.role, color_other) - buff.append('\x19%s}%s\x19o\x19%s}%s\x19o' % ( - color, affiliation, dump_tuple(user.color), user.nick)) + buff.append('\x19%s}%s\x19o\x19%s}%s\x19o' % + (color, affiliation, dump_tuple(user.color), + user.nick)) buff.append('\n') message = ' '.join(buff) @@ -1414,6 +1489,7 @@ class MucTab(ChatTab): Changes the role of an user roles can be: none, visitor, participant, moderator """ + def callback(iq): if iq['type'] == 'error': self.core.room_error(iq, self.name) @@ -1431,6 +1507,7 @@ class MucTab(ChatTab): Changes the affiliation of an user affiliations can be: outcast, none, member, admin, owner """ + def callback(iq): if iq['type'] == 'error': self.core.room_error(iq, self.name) @@ -1542,9 +1619,8 @@ class MucTab(ChatTab): after = config.get('after_completion') + ' ' input_pos = self.input.pos if ' ' not in self.input.get_text()[:input_pos] or ( - self.input.last_completion and - self.input.get_text()[:input_pos] == - self.input.last_completion + after): + self.input.last_completion and self.input.get_text() + [:input_pos] == self.input.last_completion + after): add_after = after else: if not config.get('add_space_after_completion'): @@ -1553,9 +1629,9 @@ class MucTab(ChatTab): add_after = ' ' self.input.auto_completion(word_list, add_after, quotify=False) empty_after = self.input.get_text() == '' - empty_after = empty_after or (self.input.get_text().startswith('/') - and not - self.input.get_text().startswith('//')) + empty_after = empty_after or ( + self.input.get_text().startswith('/') + and not self.input.get_text().startswith('//')) self.send_composing_chat_state(empty_after) def completion_version(self, the_input): @@ -1584,15 +1660,18 @@ class MucTab(ChatTab): def completion_nick(self, the_input): """Completion for /nick""" - nicks = [os.environ.get('USER'), - config.get('default_nick'), - self.core.get_bookmark_nickname(self.name)] + nicks = [ + os.environ.get('USER'), + config.get('default_nick'), + self.core.get_bookmark_nickname(self.name) + ] nicks = [i for i in nicks if i] return Completion(the_input.auto_completion, nicks, '', quotify=False) def completion_recolor(self, the_input): if the_input.get_argument_position() == 1: - return Completion(the_input.new_completion, ['random'], 1, '', quotify=False) + return Completion( + the_input.new_completion, ['random'], 1, '', quotify=False) return True def completion_color(self, the_input): @@ -1602,13 +1681,15 @@ class MucTab(ChatTab): userlist = [user.nick for user in self.users] if self.own_nick in userlist: userlist.remove(self.own_nick) - return Completion(the_input.new_completion, userlist, 1, '', quotify=True) + return Completion( + the_input.new_completion, userlist, 1, '', quotify=True) elif n == 2: colors = [i for i in xhtml.colors if i] colors.sort() colors.append('unset') colors.append('random') - return Completion(the_input.new_completion, colors, 2, '', quotify=False) + return Completion( + the_input.new_completion, colors, 2, '', quotify=False) def completion_ignore(self, the_input): """Completion for /ignore""" @@ -1625,11 +1706,12 @@ class MucTab(ChatTab): userlist = [user.nick for user in self.users] if self.own_nick in userlist: userlist.remove(self.own_nick) - return Completion(the_input.new_completion, userlist, 1, '', quotify=True) + return Completion( + the_input.new_completion, userlist, 1, '', quotify=True) elif n == 2: possible_roles = ['none', 'visitor', 'participant', 'moderator'] - return Completion(the_input.new_completion, possible_roles, 2, '', - quotify=True) + return Completion( + the_input.new_completion, possible_roles, 2, '', quotify=True) def completion_affiliation(self, the_input): """Completion for /affiliation""" @@ -1642,22 +1724,30 @@ class MucTab(ChatTab): if self.core.xmpp.boundjid.bare in jidlist: jidlist.remove(self.core.xmpp.boundjid.bare) userlist.extend(jidlist) - return Completion(the_input.new_completion, userlist, 1, '', quotify=True) + return Completion( + the_input.new_completion, userlist, 1, '', quotify=True) elif n == 2: - possible_affiliations = ['none', 'member', 'admin', - 'owner', 'outcast'] - return Completion(the_input.new_completion, possible_affiliations, 2, '', - quotify=True) + possible_affiliations = [ + 'none', 'member', 'admin', 'owner', 'outcast' + ] + return Completion( + the_input.new_completion, + possible_affiliations, + 2, + '', + quotify=True) def completion_invite(self, the_input): """Completion for /invite""" n = the_input.get_argument_position(quoted=True) if n == 1: - return Completion(the_input.new_completion, roster.jids(), 1, quotify=True) + return Completion( + the_input.new_completion, roster.jids(), 1, quotify=True) def completion_topic(self, the_input): if the_input.get_argument_position() == 1: - return Completion(the_input.auto_completion, [self.topic], '', quotify=False) + return Completion( + the_input.auto_completion, [self.topic], '', quotify=False) def completion_quoted(self, the_input): """Nick completion, but with quotes""" @@ -1668,13 +1758,15 @@ class MucTab(ChatTab): if user.nick != self.own_nick: word_list.append(user.nick) - return Completion(the_input.new_completion, word_list, 1, quotify=True) + return Completion( + the_input.new_completion, word_list, 1, quotify=True) def completion_unignore(self, the_input): if the_input.get_argument_position() == 1: users = [user.nick for user in self.ignores] return Completion(the_input.auto_completion, users, quotify=False) + ########################## REGISTER STUFF ############################## def register_keys(self): @@ -1687,183 +1779,261 @@ class MucTab(ChatTab): def register_commands(self): "Register tab-specific commands" - self.register_commands_batch([ - { - 'name': 'ignore', - 'func': self.command_ignore, - 'usage': '', - 'desc': 'Ignore a specified nickname.', - 'shortdesc': 'Ignore someone', - 'completion': self.completion_unignore - }, - { - 'name': 'unignore', - 'func': self.command_unignore, - 'usage': '', - 'desc': 'Remove the specified nickname from the ignore list.', - 'shortdesc': 'Unignore someone.', - 'completion': self.completion_unignore - }, - { - 'name': 'kick', - 'func': self.command_kick, - 'usage': ' [reason]', - 'desc': ('Kick the user with the specified nickname.' - ' You also can give an optional reason.'), - 'shortdesc': 'Kick someone.', - 'completion': self.completion_quoted - }, - { - 'name': 'ban', - 'func': self.command_ban, - 'usage': ' [reason]', - 'desc': ('Ban the user with the specified nickname.' - ' You also can give an optional reason.'), - 'shortdesc': 'Ban someone', - 'completion': self.completion_quoted - }, - { - 'name': 'role', - 'func': self.command_role, - 'usage': ' [reason]', - 'desc': ('Set the role of an user. Roles can be:' - ' none, visitor, participant, moderator.' - ' You also can give an optional reason.'), - 'shortdesc': 'Set the role of an user.', - 'completion': self.completion_role - }, - { - 'name': 'affiliation', - 'func': self.command_affiliation, - 'usage': ' ', - 'desc': ('Set the affiliation of an user. Affiliations can be:' - ' outcast, none, member, admin, owner.'), - 'shortdesc': 'Set the affiliation of an user.', - 'completion': self.completion_affiliation - }, - { - 'name': 'topic', - 'func': self.command_topic, - 'usage': '', - 'desc': 'Change the subject of the room.', - 'shortdesc': 'Change the subject.', - 'completion': self.completion_topic - }, - { - 'name': 'subject', - 'func': self.command_topic, - 'usage': '', - 'desc': 'Change the subject of the room.', - 'shortdesc': 'Change the subject.', - 'completion': self.completion_topic - }, - { - 'name': 'query', - 'func': self.command_query, - 'usage': ' [message]', - 'desc': ('Open a private conversation with . This nick' - ' has to be present in the room you\'re currently in.' - ' If you specified a message after the nickname, it ' - 'will immediately be sent to this user.'), - 'shortdesc': 'Query a user.', - 'completion': self.completion_quoted - }, - { - 'name': 'part', - 'func': self.command_part, - 'usage': '[message]', - 'desc': ('Disconnect from a room. You can' - ' specify an optional message.'), - 'shortdesc': 'Leave the room.' - }, - { - 'name': 'close', - 'func': self.command_close, - 'usage': '[message]', - 'desc': ('Disconnect from a room and close the tab.' - ' You can specify an optional message if ' - 'you are still connected.'), - 'shortdesc': 'Close the tab.' - }, - { - 'name': 'nick', - 'func': self.command_nick, - 'usage': '', - 'desc': 'Change your nickname in the current room.', - 'shortdesc': 'Change your nickname.', - 'completion': self.completion_nick - }, - { - 'name':'recolor', - 'func': self.command_recolor, - 'usage': '[random]', - 'desc': ('Re-assign a color to all participants of the' - ' current room, based on the last time they talked.' - ' Use this if the participants currently talking ' - 'have too many identical colors. Use /recolor random' - ' for a non-deterministic result.'), - 'shortdesc': 'Change the nicks colors.', - 'completion': self.completion_recolor - }, - { - 'name': 'color', - 'func': self.command_color, - 'usage': ' ', - 'desc': ('Fix a color for a nick. Use "unset" instead of a ' - 'color to remove the attribution'), - 'shortdesc': 'Fix a color for a nick.', - 'completion': self.completion_recolor - }, - { - 'name': 'cycle', - 'func': self.command_cycle, - 'usage': '[message]', - 'desc': 'Leave the current room and rejoin it immediately.', - 'shortdesc': 'Leave and re-join the room.' - }, - { - 'name': 'info', - 'func': self.command_info, - 'usage': '', - 'desc': ('Display some information about the user ' - 'in the MUC: its/his/her role, affiliation,' - ' status and status message.'), - 'shortdesc': 'Show an user\'s infos.', - 'completion': self.completion_info - }, - { - 'name': 'configure', - 'func': self.command_configure, - 'desc': 'Configure the current room, through a form.', - 'shortdesc': 'Configure the room.' - }, - { - 'name': 'version', - 'func': self.command_version, - 'usage': '', - 'desc': ('Get the software version of the given JID' - ' or nick in room (usually its XMPP client' - ' and Operating System).'), - 'shortdesc': 'Get the software version of a jid.', - 'completion': self.completion_version - }, - { - 'name': 'names', - 'func': self.command_names, - 'desc': 'Get the users in the room with their roles.', - 'shortdesc': 'List the users.' - }, - { - 'name': 'invite', - 'func': self.command_invite, - 'desc': 'Invite a contact to this room', - 'usage': ' [reason]', - 'shortdesc': 'Invite a contact to this room', - 'completion': self.completion_invite - } - ]) + self.register_commands_batch([{ + 'name': 'ignore', + 'func': self.command_ignore, + 'usage': '', + 'desc': 'Ignore a specified nickname.', + 'shortdesc': 'Ignore someone', + 'completion': self.completion_unignore + }, { + 'name': + 'unignore', + 'func': + self.command_unignore, + 'usage': + '', + 'desc': + 'Remove the specified nickname from the ignore list.', + 'shortdesc': + 'Unignore someone.', + 'completion': + self.completion_unignore + }, { + 'name': + 'kick', + 'func': + self.command_kick, + 'usage': + ' [reason]', + 'desc': ('Kick the user with the specified nickname.' + ' You also can give an optional reason.'), + 'shortdesc': + 'Kick someone.', + 'completion': + self.completion_quoted + }, { + 'name': + 'ban', + 'func': + self.command_ban, + 'usage': + ' [reason]', + 'desc': ('Ban the user with the specified nickname.' + ' You also can give an optional reason.'), + 'shortdesc': + 'Ban someone', + 'completion': + self.completion_quoted + }, { + 'name': + 'role', + 'func': + self.command_role, + 'usage': + ' [reason]', + 'desc': ('Set the role of an user. Roles can be:' + ' none, visitor, participant, moderator.' + ' You also can give an optional reason.'), + 'shortdesc': + 'Set the role of an user.', + 'completion': + self.completion_role + }, { + 'name': + 'affiliation', + 'func': + self.command_affiliation, + 'usage': + ' ', + 'desc': ('Set the affiliation of an user. Affiliations can be:' + ' outcast, none, member, admin, owner.'), + 'shortdesc': + 'Set the affiliation of an user.', + 'completion': + self.completion_affiliation + }, { + 'name': + 'topic', + 'func': + self.command_topic, + 'usage': + '', + 'desc': + 'Change the subject of the room.', + 'shortdesc': + 'Change the subject.', + 'completion': + self.completion_topic + }, { + 'name': + 'subject', + 'func': + self.command_topic, + 'usage': + '', + 'desc': + 'Change the subject of the room.', + 'shortdesc': + 'Change the subject.', + 'completion': + self.completion_topic + }, { + 'name': + 'query', + 'func': + self.command_query, + 'usage': + ' [message]', + 'desc': ('Open a private conversation with . This nick' + ' has to be present in the room you\'re currently in.' + ' If you specified a message after the nickname, it ' + 'will immediately be sent to this user.'), + 'shortdesc': + 'Query a user.', + 'completion': + self.completion_quoted + }, { + 'name': + 'part', + 'func': + self.command_part, + 'usage': + '[message]', + 'desc': ('Disconnect from a room. You can' + ' specify an optional message.'), + 'shortdesc': + 'Leave the room.' + }, { + 'name': + 'close', + 'func': + self.command_close, + 'usage': + '[message]', + 'desc': ('Disconnect from a room and close the tab.' + ' You can specify an optional message if ' + 'you are still connected.'), + 'shortdesc': + 'Close the tab.' + }, { + 'name': + 'nick', + 'func': + self.command_nick, + 'usage': + '', + 'desc': + 'Change your nickname in the current room.', + 'shortdesc': + 'Change your nickname.', + 'completion': + self.completion_nick + }, { + 'name': + 'recolor', + 'func': + self.command_recolor, + 'usage': + '[random]', + 'desc': ('Re-assign a color to all participants of the' + ' current room, based on the last time they talked.' + ' Use this if the participants currently talking ' + 'have too many identical colors. Use /recolor random' + ' for a non-deterministic result.'), + 'shortdesc': + 'Change the nicks colors.', + 'completion': + self.completion_recolor + }, { + 'name': + 'color', + 'func': + self.command_color, + 'usage': + ' ', + 'desc': ('Fix a color for a nick. Use "unset" instead of a ' + 'color to remove the attribution'), + 'shortdesc': + 'Fix a color for a nick.', + 'completion': + self.completion_recolor + }, { + 'name': + 'cycle', + 'func': + self.command_cycle, + 'usage': + '[message]', + 'desc': + 'Leave the current room and rejoin it immediately.', + 'shortdesc': + 'Leave and re-join the room.' + }, { + 'name': + 'info', + 'func': + self.command_info, + 'usage': + '', + 'desc': ('Display some information about the user ' + 'in the MUC: its/his/her role, affiliation,' + ' status and status message.'), + 'shortdesc': + 'Show an user\'s infos.', + 'completion': + self.completion_info + }, { + 'name': + 'configure', + 'func': + self.command_configure, + 'desc': + 'Configure the current room, through a form.', + 'shortdesc': + 'Configure the room.' + }, { + 'name': + 'version', + 'func': + self.command_version, + 'usage': + '', + 'desc': ('Get the software version of the given JID' + ' or nick in room (usually its XMPP client' + ' and Operating System).'), + 'shortdesc': + 'Get the software version of a jid.', + 'completion': + self.completion_version + }, { + 'name': + 'names', + 'func': + self.command_names, + 'desc': + 'Get the users in the room with their roles.', + 'shortdesc': + 'List the users.' + }, { + 'name': + 'invite', + 'func': + self.command_invite, + 'desc': + 'Invite a contact to this room', + 'usage': + ' [reason]', + 'shortdesc': + 'Invite a contact to this room', + 'completion': + self.completion_invite + }]) + + +class PresenceError(Exception): + pass -class PresenceError(Exception): pass def dissect_presence(presence): """ diff --git a/poezio/tabs/privatetab.py b/poezio/tabs/privatetab.py index ec3ed74a..735522e1 100644 --- a/poezio/tabs/privatetab.py +++ b/poezio/tabs/privatetab.py @@ -27,6 +27,7 @@ from poezio.logger import logger from poezio.theming import get_theme, dump_tuple from poezio.decorators import command_args_parser + class PrivateTab(OneToOneTab): """ The tab containg a private conversation (someone from a MUC) @@ -35,6 +36,7 @@ class PrivateTab(OneToOneTab): plugin_commands = {} additional_information = {} plugin_keys = {} + def __init__(self, core, name, nick): OneToOneTab.__init__(self, core, name) self.own_nick = nick @@ -46,12 +48,18 @@ class PrivateTab(OneToOneTab): # keys self.key_func['^I'] = self.completion # commands - self.register_command('info', self.command_info, - desc='Display some information about the user in the MUC: its/his/her role, affiliation, status and status message.', - shortdesc='Info about the user.') - self.register_command('version', self.command_version, - desc='Get the software version of the current interlocutor (usually its XMPP client and Operating System).', - shortdesc='Get the software version of a jid.') + self.register_command( + 'info', + self.command_info, + desc= + 'Display some information about the user in the MUC: its/his/her role, affiliation, status and status message.', + shortdesc='Info about the user.') + self.register_command( + 'version', + self.command_version, + desc= + 'Get the software version of the current interlocutor (usually its XMPP client and Operating System).', + shortdesc='Get the software version of a jid.') self.resize() self.parent_muc = self.core.get_tab_by_name(safeJID(name).bare, MucTab) self.on = True @@ -61,7 +69,7 @@ class PrivateTab(OneToOneTab): def remote_user_color(self): user = self.parent_muc.get_user_by_name(safeJID(self.name).resource) if user: - return dump_tuple(user.color); + return dump_tuple(user.color) return super().remote_user_color() @property @@ -87,14 +95,16 @@ class PrivateTab(OneToOneTab): del PrivateTab.additional_information[plugin_name] def load_logs(self, log_nb): - logs = logger.get_logs(safeJID(self.name).full.replace('/', '\\'), log_nb) + logs = logger.get_logs( + safeJID(self.name).full.replace('/', '\\'), log_nb) return logs def log_message(self, txt, nickname, time=None, typ=1): """ Log the messages in the archives. """ - if not logger.log_message(self.name, nickname, txt, date=time, typ=typ): + if not logger.log_message( + self.name, nickname, txt, date=time, typ=typ): self.core.information('Unable to write in the log file', 'Error') def on_close(self): @@ -120,7 +130,9 @@ class PrivateTab(OneToOneTab): else: add_after = '' self.input.auto_completion(word_list, add_after, quotify=False) - empty_after = self.input.get_text() == '' or (self.input.get_text().startswith('/') and not self.input.get_text().startswith('//')) + empty_after = self.input.get_text() == '' or ( + self.input.get_text().startswith('/') + and not self.input.get_text().startswith('//')) self.send_composing_chat_state(empty_after) @command_args_parser.raw @@ -145,8 +157,13 @@ class PrivateTab(OneToOneTab): msg['replace']['id'] = self.last_sent_message['id'] if config.get_by_tabname('group_corrections', self.name): try: - self.modify_message(msg['body'], self.last_sent_message['id'], msg['id'], - user=user, jid=self.core.xmpp.boundjid, nickname=self.own_nick) + self.modify_message( + msg['body'], + self.last_sent_message['id'], + msg['id'], + user=user, + jid=self.core.xmpp.boundjid, + nickname=self.own_nick) replaced = True except: log.error('Unable to correct a message', exc_info=True) @@ -157,8 +174,8 @@ class PrivateTab(OneToOneTab): msg.enable('html') msg['html']['body'] = xhtml.poezio_colors_to_html(msg['body']) msg['body'] = xhtml.clean_text(msg['body']) - if (config.get_by_tabname('send_chat_states', self.general_jid) and - self.remote_wants_chatstates is not False): + if (config.get_by_tabname('send_chat_states', self.general_jid) + and self.remote_wants_chatstates is not False): needed = 'inactive' if self.inactive else 'active' msg['chat_state'] = needed if attention and self.remote_supports_attention: @@ -170,13 +187,14 @@ class PrivateTab(OneToOneTab): self.input.refresh() return if not replaced: - self.add_message(msg['body'], - nickname=self.own_nick or self.core.own_nick, - forced_user=user, - nick_color=get_theme().COLOR_OWN_NICK, - identifier=msg['id'], - jid=self.core.xmpp.boundjid, - typ=1) + self.add_message( + msg['body'], + nickname=self.own_nick or self.core.own_nick, + forced_user=user, + nick_color=get_theme().COLOR_OWN_NICK, + identifier=msg['id'], + jid=self.core.xmpp.boundjid, + typ=1) self.last_sent_message = msg if self.remote_supports_receipts: @@ -191,19 +209,22 @@ class PrivateTab(OneToOneTab): """ /version """ + def callback(res): if not res: - return self.core.information('Could not get the software version from %s' % (jid,), 'Warning') - version = '%s is running %s version %s on %s' % (jid, - res.get('name') or 'an unknown software', - res.get('version') or 'unknown', - res.get('os') or 'an unknown platform') + return self.core.information( + 'Could not get the software version from %s' % (jid, ), + 'Warning') + version = '%s is running %s version %s on %s' % ( + jid, res.get('name') or 'an unknown software', + res.get('version') or 'unknown', + res.get('os') or 'an unknown platform') self.core.information(version, 'Info') + if args: return self.core.command.version(args[0]) jid = safeJID(self.name) - fixes.get_version(self.core.xmpp, jid, - callback=callback) + fixes.get_version(self.core.xmpp, jid, callback=callback) @command_args_parser.quoted(0, 1) def command_info(self, arg): @@ -226,14 +247,14 @@ class PrivateTab(OneToOneTab): info_win_height = self.core.information_win_size tab_win_height = Tab.tab_win_height() - self.text_win.resize(self.height - 2 - info_win_height - tab_win_height, - self.width, 0, 0) + self.text_win.resize( + self.height - 2 - info_win_height - tab_win_height, self.width, 0, + 0) self.text_win.rebuild_everything(self._text_buffer) - self.info_header.resize(1, self.width, - self.height - 2 - info_win_height - - tab_win_height, - 0) - self.input.resize(1, self.width, self.height-1, 0) + self.info_header.resize( + 1, self.width, self.height - 2 - info_win_height - tab_win_height, + 0) + self.input.resize(1, self.width, self.height - 1, 0) def refresh(self): if self.need_resize: @@ -251,7 +272,8 @@ class PrivateTab(OneToOneTab): self.input.refresh() def refresh_info_header(self): - self.info_header.refresh(self.name, self.text_win, self.chatstate, PrivateTab.additional_information) + self.info_header.refresh(self.name, self.text_win, self.chatstate, + PrivateTab.additional_information) self.input.refresh() def get_nick(self): @@ -264,7 +286,9 @@ class PrivateTab(OneToOneTab): self.input.do_command(key, raw=raw) if not self.on: return False - empty_after = self.input.get_text() == '' or (self.input.get_text().startswith('/') and not self.input.get_text().startswith('//')) + empty_after = self.input.get_text() == '' or ( + self.input.get_text().startswith('/') + and not self.input.get_text().startswith('//')) tab = self.core.get_tab_by_name(safeJID(self.name).bare, MucTab) if tab and tab.joined: self.send_composing_chat_state(empty_after) @@ -279,8 +303,8 @@ class PrivateTab(OneToOneTab): self.text_win.remove_line_separator() self.text_win.add_line_separator(self._text_buffer) tab = self.core.get_tab_by_name(safeJID(self.name).bare, MucTab) - if tab and tab.joined and config.get_by_tabname('send_chat_states', - self.general_jid) and self.on: + if tab and tab.joined and config.get_by_tabname( + 'send_chat_states', self.general_jid) and self.on: self.send_chat_state('inactive') self.check_scrolled() @@ -288,15 +312,20 @@ class PrivateTab(OneToOneTab): self.state = 'current' curses.curs_set(1) tab = self.core.get_tab_by_name(safeJID(self.name).bare, MucTab) - if tab and tab.joined and config.get_by_tabname('send_chat_states', - self.general_jid,) and not self.input.get_text() and self.on: + if tab and tab.joined and config.get_by_tabname( + 'send_chat_states', + self.general_jid, + ) and not self.input.get_text() and self.on: self.send_chat_state('active') def on_info_win_size_changed(self): - if self.core.information_win_size >= self.height-3: + if self.core.information_win_size >= self.height - 3: return - self.text_win.resize(self.height-2-self.core.information_win_size - Tab.tab_win_height(), self.width, 0, 0) - self.info_header.resize(1, self.width, self.height-2-self.core.information_win_size - Tab.tab_win_height(), 0) + self.text_win.resize(self.height - 2 - self.core.information_win_size - + Tab.tab_win_height(), self.width, 0, 0) + self.info_header.resize( + 1, self.width, self.height - 2 - self.core.information_win_size - + Tab.tab_win_height(), 0) def get_text_window(self): return self.text_win @@ -307,13 +336,16 @@ class PrivateTab(OneToOneTab): The user changed her nick in the corresponding muc: update the tab’s name and display a message. """ - self.add_message('\x19%(nick_col)s}%(old)s\x19%(info_col)s} is now ' - 'known as \x19%(nick_col)s}%(new)s' % { - 'old':old_nick, 'new': user.nick, - 'nick_col': dump_tuple(user.color), - 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)}, - typ=2) - new_jid = safeJID(self.name).bare+'/'+user.nick + self.add_message( + '\x19%(nick_col)s}%(old)s\x19%(info_col)s} is now ' + 'known as \x19%(nick_col)s}%(new)s' % { + 'old': old_nick, + 'new': user.nick, + 'nick_col': dump_tuple(user.color), + 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT) + }, + typ=2) + new_jid = safeJID(self.name).bare + '/' + user.nick self.name = new_jid return self.core.current_tab() is self @@ -323,28 +355,36 @@ class PrivateTab(OneToOneTab): The user left the associated MUC """ self.deactivate() - if config.get_by_tabname('display_user_color_in_join_part', self.general_jid): + if config.get_by_tabname('display_user_color_in_join_part', + self.general_jid): color = dump_tuple(user.color) else: color = dump_tuple(get_theme().COLOR_REMOTE_USER) if not status_message: - self.add_message('\x19%(quit_col)s}%(spec)s \x19%(nick_col)s}' - '%(nick)s\x19%(info_col)s} has left the room' % { - 'nick': user.nick, 'spec': get_theme().CHAR_QUIT, - 'nick_col': color, - 'quit_col': dump_tuple(get_theme().COLOR_QUIT_CHAR), - 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)}, - typ=2) + self.add_message( + '\x19%(quit_col)s}%(spec)s \x19%(nick_col)s}' + '%(nick)s\x19%(info_col)s} has left the room' % { + 'nick': user.nick, + 'spec': get_theme().CHAR_QUIT, + 'nick_col': color, + 'quit_col': dump_tuple(get_theme().COLOR_QUIT_CHAR), + 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT) + }, + typ=2) else: - self.add_message('\x19%(quit_col)s}%(spec)s \x19%(nick_col)s}' - '%(nick)s\x19%(info_col)s} has left the room' - ' (%(status)s)' % { 'status': status_message, - 'nick': user.nick, 'spec': get_theme().CHAR_QUIT, - 'nick_col': color, - 'quit_col': dump_tuple(get_theme().COLOR_QUIT_CHAR), - 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)}, - typ=2) + self.add_message( + '\x19%(quit_col)s}%(spec)s \x19%(nick_col)s}' + '%(nick)s\x19%(info_col)s} has left the room' + ' (%(status)s)' % { + 'status': status_message, + 'nick': user.nick, + 'spec': get_theme().CHAR_QUIT, + 'nick_col': color, + 'quit_col': dump_tuple(get_theme().COLOR_QUIT_CHAR), + 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT) + }, + typ=2) return self.core.current_tab() is self @refresh_wrapper.conditional @@ -361,12 +401,16 @@ class PrivateTab(OneToOneTab): user = tab.get_user_by_name(nick) if user: color = dump_tuple(user.color) - self.add_message('\x19%(join_col)s}%(spec)s \x19%(color)s}%(nick)s\x19' - '%(info_col)s} joined the room' % {'nick':nick, - 'color': color, 'spec':get_theme().CHAR_JOIN, - 'join_col': dump_tuple(get_theme().COLOR_JOIN_CHAR), - 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)}, - typ=2) + self.add_message( + '\x19%(join_col)s}%(spec)s \x19%(color)s}%(nick)s\x19' + '%(info_col)s} joined the room' % { + 'nick': nick, + 'color': color, + 'spec': get_theme().CHAR_JOIN, + 'join_col': dump_tuple(get_theme().COLOR_JOIN_CHAR), + 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT) + }, + typ=2) return self.core.current_tab() is self def activate(self, reason=None): @@ -386,7 +430,10 @@ class PrivateTab(OneToOneTab): def add_error(self, error_message): error = '\x19%s}%s\x19o' % (dump_tuple(get_theme().COLOR_CHAR_NACK), error_message) - self.add_message(error, highlight=True, nickname='Error', - nick_color=get_theme().COLOR_ERROR_MSG, typ=2) + self.add_message( + error, + highlight=True, + nickname='Error', + nick_color=get_theme().COLOR_ERROR_MSG, + typ=2) self.core.refresh_window() - diff --git a/poezio/tabs/rostertab.py b/poezio/tabs/rostertab.py index 731120be..c079d550 100644 --- a/poezio/tabs/rostertab.py +++ b/poezio/tabs/rostertab.py @@ -36,6 +36,7 @@ class RosterInfoTab(Tab): """ plugin_commands = {} plugin_keys = {} + def __init__(self, core): Tab.__init__(self, core) self.name = "Roster" @@ -45,7 +46,8 @@ class RosterInfoTab(Tab): self.roster_win = windows.RosterWin() self.contact_info_win = windows.ContactInfoWin() self.avatar_win = windows.ImageWin() - self.default_help_message = windows.HelpText("Enter commands with “/”. “o”: toggle offline show") + self.default_help_message = windows.HelpText( + "Enter commands with “/”. “o”: toggle offline show") self.input = self.default_help_message self.state = 'normal' self.key_func['^I'] = self.completion @@ -68,82 +70,110 @@ class RosterInfoTab(Tab): self.key_func["s"] = self.start_search self.key_func["S"] = self.start_search_slow self.key_func["n"] = self.change_contact_name - self.register_command('deny', self.command_deny, - usage='[jid]', - desc='Deny your presence to the provided JID (or the ' - 'selected contact in your roster), who is asking' - 'you to be in his/here roster.', - shortdesc='Deny a user your presence.', - completion=self.completion_deny) - self.register_command('accept', self.command_accept, - usage='[jid]', - desc='Allow the provided JID (or the selected contact ' - 'in your roster), to see your presence.', - shortdesc='Allow a user your presence.', - completion=self.completion_deny) - self.register_command('add', self.command_add, - usage='', - desc='Add the specified JID to your roster, ask them to' - ' allow you to see his presence, and allow them to' - ' see your presence.', - shortdesc='Add a user to your roster.') - self.register_command('name', self.command_name, - usage=' [name]', - shortdesc='Set the given JID\'s name.', - completion=self.completion_name) - self.register_command('groupadd', self.command_groupadd, - usage='[ ]|', - desc='Add the given JID or selected line to the given group.', - shortdesc='Add a user to a group', - completion=self.completion_groupadd) - self.register_command('groupmove', self.command_groupmove, - usage=' ', - desc='Move the given JID from the old group to the new group.', - shortdesc='Move a user to another group.', - completion=self.completion_groupmove) - self.register_command('groupremove', self.command_groupremove, - usage=' ', - desc='Remove the given JID from the given group.', - shortdesc='Remove a user from a group.', - completion=self.completion_groupremove) - self.register_command('remove', self.command_remove, - usage='[jid]', - desc='Remove the specified JID from your roster. This ' - 'will unsubscribe you from its presence, cancel ' - 'its subscription to yours, and remove the item ' - 'from your roster.', - shortdesc='Remove a user from your roster.', - completion=self.completion_remove) - self.register_command('export', self.command_export, - usage='[/path/to/file]', - desc='Export your contacts into /path/to/file if ' - 'specified, or $HOME/poezio_contacts if not.', - shortdesc='Export your roster to a file.', - completion=partial(self.completion_file, 1)) - self.register_command('import', self.command_import, - usage='[/path/to/file]', - desc='Import your contacts from /path/to/file if ' - 'specified, or $HOME/poezio_contacts if not.', - shortdesc='Import your roster from a file.', - completion=partial(self.completion_file, 1)) - self.register_command('password', self.command_password, - usage='', - shortdesc='Change your password') - - self.register_command('reconnect', self.command_reconnect, - desc='Disconnect from the remote server if you are ' - 'currently connected and then connect to it again.', - shortdesc='Disconnect and reconnect to the server.') - self.register_command('disconnect', self.command_disconnect, - desc='Disconnect from the remote server.', - shortdesc='Disconnect from the server.') - self.register_command('clear', self.command_clear, - shortdesc='Clear the info buffer.') - self.register_command('last_activity', self.command_last_activity, + self.register_command( + 'deny', + self.command_deny, + usage='[jid]', + desc='Deny your presence to the provided JID (or the ' + 'selected contact in your roster), who is asking' + 'you to be in his/here roster.', + shortdesc='Deny a user your presence.', + completion=self.completion_deny) + self.register_command( + 'accept', + self.command_accept, + usage='[jid]', + desc='Allow the provided JID (or the selected contact ' + 'in your roster), to see your presence.', + shortdesc='Allow a user your presence.', + completion=self.completion_deny) + self.register_command( + 'add', + self.command_add, usage='', - desc='Informs you of the last activity of a JID.', - shortdesc='Get the activity of someone.', - completion=self.core.completion.last_activity) + desc='Add the specified JID to your roster, ask them to' + ' allow you to see his presence, and allow them to' + ' see your presence.', + shortdesc='Add a user to your roster.') + self.register_command( + 'name', + self.command_name, + usage=' [name]', + shortdesc='Set the given JID\'s name.', + completion=self.completion_name) + self.register_command( + 'groupadd', + self.command_groupadd, + usage='[ ]|', + desc='Add the given JID or selected line to the given group.', + shortdesc='Add a user to a group', + completion=self.completion_groupadd) + self.register_command( + 'groupmove', + self.command_groupmove, + usage=' ', + desc='Move the given JID from the old group to the new group.', + shortdesc='Move a user to another group.', + completion=self.completion_groupmove) + self.register_command( + 'groupremove', + self.command_groupremove, + usage=' ', + desc='Remove the given JID from the given group.', + shortdesc='Remove a user from a group.', + completion=self.completion_groupremove) + self.register_command( + 'remove', + self.command_remove, + usage='[jid]', + desc='Remove the specified JID from your roster. This ' + 'will unsubscribe you from its presence, cancel ' + 'its subscription to yours, and remove the item ' + 'from your roster.', + shortdesc='Remove a user from your roster.', + completion=self.completion_remove) + self.register_command( + 'export', + self.command_export, + usage='[/path/to/file]', + desc='Export your contacts into /path/to/file if ' + 'specified, or $HOME/poezio_contacts if not.', + shortdesc='Export your roster to a file.', + completion=partial(self.completion_file, 1)) + self.register_command( + 'import', + self.command_import, + usage='[/path/to/file]', + desc='Import your contacts from /path/to/file if ' + 'specified, or $HOME/poezio_contacts if not.', + shortdesc='Import your roster from a file.', + completion=partial(self.completion_file, 1)) + self.register_command( + 'password', + self.command_password, + usage='', + shortdesc='Change your password') + + self.register_command( + 'reconnect', + self.command_reconnect, + desc='Disconnect from the remote server if you are ' + 'currently connected and then connect to it again.', + shortdesc='Disconnect and reconnect to the server.') + self.register_command( + 'disconnect', + self.command_disconnect, + desc='Disconnect from the remote server.', + shortdesc='Disconnect from the server.') + self.register_command( + 'clear', self.command_clear, shortdesc='Clear the info buffer.') + self.register_command( + 'last_activity', + self.command_last_activity, + usage='', + desc='Informs you of the last activity of a JID.', + shortdesc='Get the activity of someone.', + completion=self.core.completion.last_activity) self.resize() self.update_commands() @@ -151,54 +181,72 @@ class RosterInfoTab(Tab): def check_blocking(self, features): if 'urn:xmpp:blocking' in features and not self.core.xmpp.anon: - self.register_command('block', self.command_block, - usage='[jid]', - shortdesc='Prevent a JID from talking to you.', - completion=self.completion_block) - self.register_command('unblock', self.command_unblock, - usage='[jid]', - shortdesc='Allow a JID to talk to you.', - completion=self.completion_unblock) - self.register_command('list_blocks', self.command_list_blocks, - shortdesc='Show the blocked contacts.') - self.core.xmpp.del_event_handler('session_start', self.check_blocking) - self.core.xmpp.add_event_handler('blocked_message', self.on_blocked_message) + self.register_command( + 'block', + self.command_block, + usage='[jid]', + shortdesc='Prevent a JID from talking to you.', + completion=self.completion_block) + self.register_command( + 'unblock', + self.command_unblock, + usage='[jid]', + shortdesc='Allow a JID to talk to you.', + completion=self.completion_unblock) + self.register_command( + 'list_blocks', + self.command_list_blocks, + shortdesc='Show the blocked contacts.') + self.core.xmpp.del_event_handler('session_start', + self.check_blocking) + self.core.xmpp.add_event_handler('blocked_message', + self.on_blocked_message) def check_saslexternal(self, features): if 'urn:xmpp:saslcert:1' in features and not self.core.xmpp.anon: - self.register_command('certs', self.command_certs, - desc='List the fingerprints of certificates' - ' which can connect to your account.', - shortdesc='List allowed client certs.') - self.register_command('cert_add', self.command_cert_add, - desc='Add a client certificate to the authorized ones. ' - 'It must have an unique name and be contained in ' - 'a PEM file. [management] is a boolean indicating' - ' if a client connected using this certificate can' - ' manage the certificates itself.', - shortdesc='Add a client certificate.', - usage=' [management]', - completion=self.completion_cert_add) - self.register_command('cert_disable', self.command_cert_disable, - desc='Remove a certificate from the list ' - 'of allowed ones. Clients currently ' - 'using this certificate will not be ' - 'forcefully disconnected.', - shortdesc='Disable a certificate', - usage='') - self.register_command('cert_revoke', self.command_cert_revoke, - desc='Remove a certificate from the list ' - 'of allowed ones. Clients currently ' - 'using this certificate will be ' - 'forcefully disconnected.', - shortdesc='Revoke a certificate', - usage='') - self.register_command('cert_fetch', self.command_cert_fetch, - desc='Retrieve a certificate with its ' - 'name. It will be stored in .', - shortdesc='Fetch a certificate', - usage=' ', - completion=self.completion_cert_fetch) + self.register_command( + 'certs', + self.command_certs, + desc='List the fingerprints of certificates' + ' which can connect to your account.', + shortdesc='List allowed client certs.') + self.register_command( + 'cert_add', + self.command_cert_add, + desc='Add a client certificate to the authorized ones. ' + 'It must have an unique name and be contained in ' + 'a PEM file. [management] is a boolean indicating' + ' if a client connected using this certificate can' + ' manage the certificates itself.', + shortdesc='Add a client certificate.', + usage=' [management]', + completion=self.completion_cert_add) + self.register_command( + 'cert_disable', + self.command_cert_disable, + desc='Remove a certificate from the list ' + 'of allowed ones. Clients currently ' + 'using this certificate will not be ' + 'forcefully disconnected.', + shortdesc='Disable a certificate', + usage='') + self.register_command( + 'cert_revoke', + self.command_cert_revoke, + desc='Remove a certificate from the list ' + 'of allowed ones. Clients currently ' + 'using this certificate will be ' + 'forcefully disconnected.', + shortdesc='Revoke a certificate', + usage='') + self.register_command( + 'cert_fetch', + self.command_cert_fetch, + desc='Retrieve a certificate with its ' + 'name. It will be stored in .', + shortdesc='Fetch a certificate', + usage=' ', + completion=self.completion_cert_fetch) @property def selected_row(self): @@ -209,10 +257,11 @@ class RosterInfoTab(Tab): """ /certs """ + def cb(iq): if iq['type'] == 'error': - self.core.information('Unable to retrieve the certificate list.', - 'Error') + self.core.information( + 'Unable to retrieve the certificate list.', 'Error') return certs = [] for item in iq['sasl_certs']['items']: @@ -222,7 +271,8 @@ class RosterInfoTab(Tab): if not certs: return self.core.information('No certificates found', 'Info') msg = 'Certificates:\n' - msg += '\n'.join(((' %s%s' % (item[0] + (': ' if item[1] else ''), item[1])) for item in certs)) + msg += '\n'.join(((' %s%s' % (item[0] + (': ' if item[1] else ''), + item[1])) for item in certs)) self.core.information(msg, 'Info') self.core.xmpp.plugin['xep_0257'].get_certs(callback=cb, timeout=3) @@ -234,9 +284,11 @@ class RosterInfoTab(Tab): """ if not args or len(args) < 2: return self.core.command.help('cert_add') + def cb(iq): if iq['type'] == 'error': - self.core.information('Unable to add the certificate.', 'Error') + self.core.information('Unable to add the certificate.', + 'Error') else: self.core.information('Certificate added.', 'Info') @@ -245,9 +297,11 @@ class RosterInfoTab(Tab): try: with open(args[1]) as fd: crt = fd.read() - crt = crt.replace(ssl.PEM_FOOTER, '').replace(ssl.PEM_HEADER, '').replace(' ', '').replace('\n', '') + crt = crt.replace(ssl.PEM_FOOTER, '').replace( + ssl.PEM_HEADER, '').replace(' ', '').replace('\n', '') except Exception as e: - self.core.information('Unable to read the certificate: %s' % e, 'Error') + self.core.information('Unable to read the certificate: %s' % e, + 'Error') return if len(args) > 2: @@ -263,8 +317,8 @@ class RosterInfoTab(Tab): else: management = True - self.core.xmpp.plugin['xep_0257'].add_cert(name, crt, callback=cb, - allow_management=management) + self.core.xmpp.plugin['xep_0257'].add_cert( + name, crt, callback=cb, allow_management=management) def completion_cert_add(self, the_input): """ @@ -286,9 +340,11 @@ class RosterInfoTab(Tab): """ if not args: return self.core.command.help('cert_disable') + def cb(iq): if iq['type'] == 'error': - self.core.information('Unable to disable the certificate.', 'Error') + self.core.information('Unable to disable the certificate.', + 'Error') else: self.core.information('Certificate disabled.', 'Info') @@ -303,9 +359,11 @@ class RosterInfoTab(Tab): """ if not args: return self.core.command.help('cert_revoke') + def cb(iq): if iq['type'] == 'error': - self.core.information('Unable to revoke the certificate.', 'Error') + self.core.information('Unable to revoke the certificate.', + 'Error') else: self.core.information('Certificate revoked.', 'Info') @@ -313,7 +371,6 @@ class RosterInfoTab(Tab): self.core.xmpp.plugin['xep_0257'].revoke_cert(name, callback=cb) - @command_args_parser.quoted(2) def command_cert_fetch(self, args): """ @@ -321,6 +378,7 @@ class RosterInfoTab(Tab): """ if not args or len(args) < 2: return self.core.command.help('cert_fetch') + def cb(iq): if iq['type'] == 'error': self.core.information('Unable to fetch the certificate.', @@ -364,11 +422,12 @@ class RosterInfoTab(Tab): """ tab = self.core.get_conversation_by_jid(message['from'], False) if not tab: - log.debug('Received message from nonexistent tab: %s', message['from']) + log.debug('Received message from nonexistent tab: %s', + message['from']) message = '\x19%(info_col)s}Cannot send message to %(jid)s: contact blocked' % { - 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT), - 'jid': message['from'], - } + 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT), + 'jid': message['from'], + } tab.add_message(message) @command_args_parser.quoted(0, 1) @@ -386,7 +445,8 @@ class RosterInfoTab(Tab): def callback(iq): if iq['type'] == 'error': - return self.core.information('Could not block %s.' % jid, 'Error') + return self.core.information('Could not block %s.' % jid, + 'Error') elif iq['type'] == 'result': return self.core.information('Blocked %s.' % jid, 'Info') @@ -398,16 +458,19 @@ class RosterInfoTab(Tab): """ if the_input.get_argument_position() == 1: jids = roster.jids() - return Completion(the_input.new_completion, jids, 1, '', quotify=False) + return Completion( + the_input.new_completion, jids, 1, '', quotify=False) @command_args_parser.quoted(0, 1) def command_unblock(self, args): """ /unblock [jid] """ + def callback(iq): if iq['type'] == 'error': - return self.core.information('Could not unblock the contact.', 'Error') + return self.core.information('Could not unblock the contact.', + 'Error') elif iq['type'] == 'result': return self.core.information('Contact unblocked.', 'Info') @@ -424,6 +487,7 @@ class RosterInfoTab(Tab): """ Completion for /unblock """ + def on_result(iq): if iq['type'] == 'error': return @@ -439,9 +503,11 @@ class RosterInfoTab(Tab): """ /list_blocks """ + def callback(iq): if iq['type'] == 'error': - return self.core.information('Could not retrieve the blocklist.', 'Error') + return self.core.information( + 'Could not retrieve the blocklist.', 'Error') s = 'List of blocked JIDs:\n' items = (str(item) for item in iq['blocklist']['items']) jids = '\n'.join(items) @@ -508,25 +574,23 @@ class RosterInfoTab(Tab): info_width = self.width - roster_width - 1 if display_info: - self.v_separator.resize(self.height - 1 - tab_win_height, - 1, 0, roster_width) - self.information_win.resize(self.height - 1 - tab_win_height - - contact_win_h, - info_width, 0, roster_width + 1, - self.core.information_buffer) + self.v_separator.resize(self.height - 1 - tab_win_height, 1, 0, + roster_width) + self.information_win.resize( + self.height - 1 - tab_win_height - contact_win_h, info_width, + 0, roster_width + 1, self.core.information_buffer) if display_contact_win: y = self.height - tab_win_height - contact_win_h - 1 avatar_width = contact_win_h * 2 self.contact_info_win.resize(contact_win_h, - info_width - avatar_width, - y, roster_width + 1) - self.avatar_win.resize(contact_win_h, - avatar_width, - y, self.width - avatar_width) + info_width - avatar_width, y, + roster_width + 1) + self.avatar_win.resize(contact_win_h, avatar_width, y, + self.width - avatar_width) self.roster_win.resize(self.height - 1 - Tab.tab_win_height(), roster_width, 0, 0) - self.input.resize(1, self.width, self.height-1, 0) - self.default_help_message.resize(1, self.width, self.height-1, 0) + self.input.resize(1, self.width, self.height - 1, 0) + self.default_help_message.resize(1, self.width, self.height - 1, 0) def completion(self): # Check if we are entering a command (with the '/' key) @@ -544,9 +608,10 @@ class RosterInfoTab(Tab): args = shell_split(text) n = the_input.get_argument_position() if n == complete_number: - if args[n-1] == '' or len(args) < n+1: + if args[n - 1] == '' or len(args) < n + 1: home = os.getenv('HOME') or '/' - return Completion(the_input.new_completion, [home, '/tmp'], n, quotify=True) + return Completion( + the_input.new_completion, [home, '/tmp'], n, quotify=True) path_ = args[n] if path.isdir(path_): dir_ = path_ @@ -569,7 +634,8 @@ class RosterInfoTab(Tab): if not name.startswith('.'): end_list.append(value) - return Completion(the_input.new_completion, end_list, n, quotify=True) + return Completion( + the_input.new_completion, end_list, n, quotify=True) @command_args_parser.ignored def command_clear(self): @@ -578,7 +644,8 @@ class RosterInfoTab(Tab): """ self.core.information_buffer.messages = [] self.information_win.rebuild_everything(self.core.information_buffer) - self.core.information_win.rebuild_everything(self.core.information_buffer) + self.core.information_win.rebuild_everything( + self.core.information_buffer) self.refresh() @command_args_parser.quoted(1) @@ -586,14 +653,18 @@ class RosterInfoTab(Tab): """ /password """ + def callback(iq): if iq['type'] == 'result': self.core.information('Password updated', 'Account') if config.get('password'): config.silent_set('password', args[0]) else: - self.core.information('Unable to change the password', 'Account') - self.core.xmpp.plugin['xep_0077'].change_password(args[0], callback=callback) + self.core.information('Unable to change the password', + 'Account') + + self.core.xmpp.plugin['xep_0077'].change_password( + args[0], callback=callback) @command_args_parser.quoted(0, 1) def command_deny(self, args): @@ -631,7 +702,8 @@ class RosterInfoTab(Tab): return jid = safeJID(safeJID(args[0]).bare) if not str(jid): - self.core.information('The provided JID (%s) is not valid' % (args[0],), 'Error') + self.core.information('The provided JID (%s) is not valid' % + (args[0], ), 'Error') return if jid in roster and roster[jid].subscription in ('to', 'both'): return self.core.information('Already subscribed.', 'Roster') @@ -644,10 +716,12 @@ class RosterInfoTab(Tab): """ Set a name for the specified JID in your roster """ + def callback(iq): if not iq: self.core.information('The name could not be set.', 'Error') log.debug('Error in /name:\n%s', iq) + if args is None: return self.core.command.help('name') jid = safeJID(args[0]).bare @@ -662,8 +736,12 @@ class RosterInfoTab(Tab): if 'none' in groups: groups.remove('none') subscription = contact.subscription - self.core.xmpp.update_roster(jid, name=name, groups=groups, - subscription=subscription, callback=callback) + self.core.xmpp.update_roster( + jid, + name=name, + groups=groups, + subscription=subscription, + callback=callback) @command_args_parser.quoted(1, 1) def command_groupadd(self, args): @@ -712,8 +790,12 @@ class RosterInfoTab(Tab): self.core.information('The group could not be set.', 'Error') log.debug('Error in groupadd:\n%s', iq) - self.core.xmpp.update_roster(jid, name=name, groups=new_groups, - subscription=subscription, callback=callback) + self.core.xmpp.update_roster( + jid, + name=name, + groups=new_groups, + subscription=subscription, + callback=callback) @command_args_parser.quoted(3) def command_groupmove(self, args): @@ -767,8 +849,12 @@ class RosterInfoTab(Tab): self.core.information('The group could not be set', 'Error') log.debug('Error in groupmove:\n%s', iq) - self.core.xmpp.update_roster(jid, name=name, groups=new_groups, - subscription=subscription, callback=callback) + self.core.xmpp.update_roster( + jid, + name=name, + groups=new_groups, + subscription=subscription, + callback=callback) @command_args_parser.quoted(2) def command_groupremove(self, args): @@ -808,8 +894,12 @@ class RosterInfoTab(Tab): self.core.information('The group could not be set') log.debug('Error in groupremove:\n%s', iq) - self.core.xmpp.update_roster(jid, name=name, groups=new_groups, - subscription=subscription, callback=callback) + self.core.xmpp.update_roster( + jid, + name=name, + groups=new_groups, + subscription=subscription, + callback=callback) @command_args_parser.quoted(0, 1) def command_remove(self, args): @@ -842,7 +932,8 @@ class RosterInfoTab(Tab): else: filepath = path.join(getenv('HOME'), 'poezio_contacts') if not path.isfile(filepath): - self.core.information('The file %s does not exist' % filepath, 'Error') + self.core.information('The file %s does not exist' % filepath, + 'Error') return try: handle = open(filepath, 'r', encoding='utf-8') @@ -877,7 +968,8 @@ class RosterInfoTab(Tab): if roster.export(filepath): self.core.information('Contacts exported to %s' % filepath, 'Info') else: - self.core.information('Failed to export contacts to %s' % filepath, 'Info') + self.core.information('Failed to export contacts to %s' % filepath, + 'Info') def completion_remove(self, the_input): """ @@ -898,10 +990,13 @@ class RosterInfoTab(Tab): n = the_input.get_argument_position() if n == 1: jids = sorted(jid for jid in roster.jids()) - return Completion(the_input.new_completion, jids, n, '', quotify=True) + return Completion( + the_input.new_completion, jids, n, '', quotify=True) elif n == 2: - groups = sorted(group for group in roster.groups if group != 'none') - return Completion(the_input.new_completion, groups, n, '', quotify=True) + groups = sorted( + group for group in roster.groups if group != 'none') + return Completion( + the_input.new_completion, groups, n, '', quotify=True) return False def completion_groupmove(self, the_input): @@ -909,7 +1004,8 @@ class RosterInfoTab(Tab): n = the_input.get_argument_position() if n == 1: jids = sorted(jid for jid in roster.jids()) - return Completion(the_input.new_completion, jids, n, '', quotify=True) + return Completion( + the_input.new_completion, jids, n, '', quotify=True) elif n == 2: contact = roster[args[1]] if not contact: @@ -917,10 +1013,12 @@ class RosterInfoTab(Tab): groups = list(contact.groups) if 'none' in groups: groups.remove('none') - return Completion(the_input.new_completion, groups, n, '', quotify=True) + return Completion( + the_input.new_completion, groups, n, '', quotify=True) elif n == 3: groups = sorted(group for group in roster.groups) - return Completion(the_input.new_completion, groups, n, '', quotify=True) + return Completion( + the_input.new_completion, groups, n, '', quotify=True) return False def completion_groupremove(self, the_input): @@ -928,7 +1026,8 @@ class RosterInfoTab(Tab): n = the_input.get_argument_position() if n == 1: jids = sorted(jid for jid in roster.jids()) - return Completion(the_input.new_completion, jids, n, '', quotify=True) + return Completion( + the_input.new_completion, jids, n, '', quotify=True) elif n == 2: contact = roster[args[1]] if contact is None: @@ -938,7 +1037,8 @@ class RosterInfoTab(Tab): groups.remove('none') except ValueError: pass - return Completion(the_input.new_completion, groups, n, '', quotify=True) + return Completion( + the_input.new_completion, groups, n, '', quotify=True) return False def completion_deny(self, the_input): @@ -946,8 +1046,9 @@ class RosterInfoTab(Tab): Complete the first argument from the list of the contact with ask=='subscribe' """ - jids = sorted(str(contact.bare_jid) for contact in roster.contacts.values() - if contact.pending_in) + jids = sorted( + str(contact.bare_jid) for contact in roster.contacts.values() + if contact.pending_in) return Completion(the_input.new_completion, jids, 1, '', quotify=False) @command_args_parser.quoted(0, 1) @@ -976,8 +1077,10 @@ class RosterInfoTab(Tab): roster.modified() self.core.xmpp.send_presence(pto=jid, ptype='subscribed') self.core.xmpp.client_roster.send_last_presence() - if contact.subscription in ('from', 'none') and not contact.pending_out: - self.core.xmpp.send_presence(pto=jid, ptype='subscribe', pnick=self.core.own_nick) + if contact.subscription in ('from', + 'none') and not contact.pending_out: + self.core.xmpp.send_presence( + pto=jid, ptype='subscribe', pnick=self.core.own_nick) self.core.information('%s is now authorized' % jid, 'Roster') @@ -1027,7 +1130,8 @@ class RosterInfoTab(Tab): success = config.silent_set(option, str(not value)) roster.modified() if not success: - self.core.information('Unable to write in the config file', 'Error') + self.core.information('Unable to write in the config file', + 'Error') return True def on_slash(self): @@ -1035,9 +1139,10 @@ class RosterInfoTab(Tab): '/' is pressed, we enter "input mode" """ curses.curs_set(1) - self.input = windows.CommandInput("", self.reset_help_message, self.execute_slash_command) - self.input.resize(1, self.width, self.height-1, 0) - self.input.do_command("/") # we add the slash + self.input = windows.CommandInput("", self.reset_help_message, + self.execute_slash_command) + self.input.resize(1, self.width, self.height - 1, 0) + self.input.do_command("/") # we add the slash def reset_help_message(self, _=None): self.input = self.default_help_message @@ -1065,13 +1170,15 @@ class RosterInfoTab(Tab): @refresh_wrapper.conditional def move_cursor_down(self): - if isinstance(self.input, windows.Input) and not self.input.history_disabled: + if isinstance(self.input, + windows.Input) and not self.input.history_disabled: return return self.roster_win.move_cursor_down() @refresh_wrapper.conditional def move_cursor_up(self): - if isinstance(self.input, windows.Input) and not self.input.history_disabled: + if isinstance(self.input, + windows.Input) and not self.input.history_disabled: return return self.roster_win.move_cursor_up() @@ -1156,9 +1263,11 @@ class RosterInfoTab(Tab): cont = selected_row res = selected_row.get_highest_priority_resource() acc = [] - acc.append('Contact: %s (%s)' % (cont.bare_jid, res.presence if res else 'unavailable')) + acc.append('Contact: %s (%s)' % (cont.bare_jid, res.presence + if res else 'unavailable')) if res: - acc.append('%s connected resource%s' % (len(cont), '' if len(cont) == 1 else 's')) + acc.append('%s connected resource%s' % + (len(cont), '' if len(cont) == 1 else 's')) acc.append('Current status: %s' % res.status) if cont.tune: acc.append('Tune: %s' % common.format_tune_string(cont.tune)) @@ -1167,21 +1276,20 @@ class RosterInfoTab(Tab): if cont.activity: acc.append('Activity: %s' % cont.activity) if cont.gaming: - acc.append('Game: %s' % (common.format_gaming_string(cont.gaming))) + acc.append('Game: %s' % + (common.format_gaming_string(cont.gaming))) msg = '\n'.join(acc) elif isinstance(selected_row, Resource): res = selected_row msg = 'Resource: %s (%s)\nCurrent status: %s\nPriority: %s' % ( - res.jid, - res.presence, - res.status, - res.priority) + res.jid, res.presence, res.status, res.priority) elif isinstance(selected_row, RosterGroup): rg = selected_row msg = 'Group: %s [%s/%s] contacts online' % ( - rg.name, - rg.get_nb_connected_contacts(), - len(rg),) + rg.name, + rg.get_nb_connected_contacts(), + len(rg), + ) else: msg = None if msg: @@ -1210,8 +1318,10 @@ class RosterInfoTab(Tab): in it. """ curses.curs_set(1) - self.input = windows.CommandInput("[Search]", self.on_search_terminate, self.on_search_terminate, self.set_roster_filter) - self.input.resize(1, self.width, self.height-1, 0) + self.input = windows.CommandInput("[Search]", self.on_search_terminate, + self.on_search_terminate, + self.set_roster_filter) + self.input.resize(1, self.width, self.height - 1, 0) self.input.disable_history() roster.modified() self.refresh() @@ -1220,8 +1330,10 @@ class RosterInfoTab(Tab): @refresh_wrapper.always def start_search_slow(self): curses.curs_set(1) - self.input = windows.CommandInput("[Search]", self.on_search_terminate, self.on_search_terminate, self.set_roster_filter_slow) - self.input.resize(1, self.width, self.height-1, 0) + self.input = windows.CommandInput("[Search]", self.on_search_terminate, + self.on_search_terminate, + self.set_roster_filter_slow) + self.input.resize(1, self.width, self.height - 1, 0) self.input.disable_history() return True @@ -1248,6 +1360,7 @@ class RosterInfoTab(Tab): def on_close(self): return + def diffmatch(search, string): """ Use difflib and a loop to check if search_pattern can @@ -1259,10 +1372,12 @@ def diffmatch(search, string): l = len(search) ratio = 0.7 for i in range(len(string) - l + 1): - if difflib.SequenceMatcher(None, search, string[i:i+l]).ratio() >= ratio: + if difflib.SequenceMatcher(None, search, + string[i:i + l]).ratio() >= ratio: return True return False + def jid_and_name_match(contact, txt): """ Match jid with text precisely @@ -1276,13 +1391,14 @@ def jid_and_name_match(contact, txt): return True return False + def jid_and_name_match_slow(contact, txt): """ A function used to know if a contact in the roster should be shown in the roster """ if not txt: - return True # Everything matches when search is empty + return True # Everything matches when search is empty user = safeJID(contact.bare_jid).bare if diffmatch(txt, user): return True diff --git a/poezio/tabs/xmltab.py b/poezio/tabs/xmltab.py index 65491bf4..223b6667 100644 --- a/poezio/tabs/xmltab.py +++ b/poezio/tabs/xmltab.py @@ -25,7 +25,6 @@ from poezio.common import safeJID class MatchJID(object): - def __init__(self, jid, dest=''): self.jid = jid self.dest = dest @@ -46,13 +45,15 @@ class MatchJID(object): def __repr__(self): return '%s%s%s' % (self.dest, ': ' if self.dest else '', self.jid) + MATCHERS_MAPPINGS = { - MatchJID: ('JID', repr), - matcher.MatcherId: ('ID', lambda obj: obj._criteria), - matcher.MatchXMLMask: ('XMLMask', lambda obj: tostring(obj._criteria)), - matcher.MatchXPath: ('XPath', lambda obj: obj._criteria) + MatchJID: ('JID', repr), + matcher.MatcherId: ('ID', lambda obj: obj._criteria), + matcher.MatchXMLMask: ('XMLMask', lambda obj: tostring(obj._criteria)), + matcher.MatchXPath: ('XPath', lambda obj: obj._criteria) } + class XMLTab(Tab): def __init__(self, core): Tab.__init__(self, core) @@ -68,41 +69,57 @@ class XMLTab(Tab): self.core_buffer.add_window(self.text_win) self.default_help_message = windows.HelpText("/ to enter a command") - self.register_command('close', self.close, - shortdesc="Close this tab.") - self.register_command('clear', self.command_clear, - shortdesc='Clear the current buffer.') - self.register_command('filter_reset', self.command_filter_reset, - shortdesc='Reset the stanza filter.') - self.register_command('filter_id', self.command_filter_id, - usage='', - desc='Show only the stanzas with the id .', - shortdesc='Filter by id.') - self.register_command('filter_xpath', self.command_filter_xpath, - usage='', - desc='Show only the stanzas matching the xpath .' - ' Any occurrences of %n will be replaced by jabber:client.', - shortdesc='Filter by XPath.') - self.register_command('filter_jid', self.command_filter_jid, - usage='', - desc='Show only the stanzas matching the jid in from= or to=.', - shortdesc='Filter by JID.') - self.register_command('filter_from', self.command_filter_from, - usage='', - desc='Show only the stanzas matching the jid in from=.', - shortdesc='Filter by JID from.') - self.register_command('filter_to', self.command_filter_to, - usage='', - desc='Show only the stanzas matching the jid in to=.', - shortdesc='Filter by JID to.') - self.register_command('filter_xmlmask', self.command_filter_xmlmask, - usage='', - desc='Show only the stanzas matching the given xml mask.', - shortdesc='Filter by xml mask.') - self.register_command('dump', self.command_dump, - usage='', - desc='Writes the content of the XML buffer into a file.', - shortdesc='Write in a file.') + self.register_command('close', self.close, shortdesc="Close this tab.") + self.register_command( + 'clear', self.command_clear, shortdesc='Clear the current buffer.') + self.register_command( + 'filter_reset', + self.command_filter_reset, + shortdesc='Reset the stanza filter.') + self.register_command( + 'filter_id', + self.command_filter_id, + usage='', + desc='Show only the stanzas with the id .', + shortdesc='Filter by id.') + self.register_command( + 'filter_xpath', + self.command_filter_xpath, + usage='', + desc='Show only the stanzas matching the xpath .' + ' Any occurrences of %n will be replaced by jabber:client.', + shortdesc='Filter by XPath.') + self.register_command( + 'filter_jid', + self.command_filter_jid, + usage='', + desc= + 'Show only the stanzas matching the jid in from= or to=.', + shortdesc='Filter by JID.') + self.register_command( + 'filter_from', + self.command_filter_from, + usage='', + desc='Show only the stanzas matching the jid in from=.', + shortdesc='Filter by JID from.') + self.register_command( + 'filter_to', + self.command_filter_to, + usage='', + desc='Show only the stanzas matching the jid in to=.', + shortdesc='Filter by JID to.') + self.register_command( + 'filter_xmlmask', + self.command_filter_xmlmask, + usage='', + desc='Show only the stanzas matching the given xml mask.', + shortdesc='Filter by xml mask.') + self.register_command( + 'dump', + self.command_dump, + usage='', + desc='Writes the content of the XML buffer into a file.', + shortdesc='Write in a file.') self.input = self.default_help_message self.key_func['^T'] = self.close self.key_func['^I'] = self.completion @@ -120,8 +137,10 @@ class XMLTab(Tab): self.filter_type = '' self.filter = '' return - filter_types = map(lambda x: MATCHERS_MAPPINGS[type(x)][0], self.filters) - filter_strings = map(lambda x: MATCHERS_MAPPINGS[type(x)][1](x), self.filters) + filter_types = map(lambda x: MATCHERS_MAPPINGS[type(x)][0], + self.filters) + filter_strings = map(lambda x: MATCHERS_MAPPINGS[type(x)][1](x), + self.filters) self.filter_type = ','.join(filter_types) self.filter = ','.join(filter_strings) @@ -138,7 +157,8 @@ class XMLTab(Tab): new_messages = [] for msg in messages: try: - if msg.txt.strip() and self.match_stanza(ElementBase(ET.fromstring(clean_text(msg.txt)))): + if msg.txt.strip() and self.match_stanza( + ElementBase(ET.fromstring(clean_text(msg.txt)))): new_messages.append(msg) except ET.ParseError: log.debug('Malformed XML : %s', msg.txt, exc_info=True) @@ -212,7 +232,9 @@ class XMLTab(Tab): def command_filter_xpath(self, xpath): """/filter_xpath """ try: - self.update_filters(matcher.MatchXPath(xpath.replace('%n', self.core.xmpp.default_ns))) + self.update_filters( + matcher.MatchXPath( + xpath.replace('%n', self.core.xmpp.default_ns))) self.refresh() except: self.core.information('Invalid XML Path', 'Error') @@ -239,22 +261,25 @@ class XMLTab(Tab): xml = self.filtered_buffer.messages[:] else: xml = self.core_buffer.messages[:] - text = '\n'.join(('%s %s %s' % (msg.str_time, msg.nickname, clean_text(msg.txt)) for msg in xml)) + text = '\n'.join(('%s %s %s' % (msg.str_time, msg.nickname, + clean_text(msg.txt)) for msg in xml)) filename = os.path.expandvars(os.path.expanduser(args[0])) try: with open(filename, 'w') as fd: fd.write(text) except Exception as e: - self.core.information('Could not write the XML dump: %s' % e, 'Error') + self.core.information('Could not write the XML dump: %s' % e, + 'Error') def on_slash(self): """ '/' is pressed, activate the input """ curses.curs_set(1) - self.input = windows.CommandInput("", self.reset_help_message, self.execute_slash_command) - self.input.resize(1, self.width, self.height-1, 0) - self.input.do_command("/") # we add the slash + self.input = windows.CommandInput("", self.reset_help_message, + self.execute_slash_command) + self.input.resize(1, self.width, self.height - 1, 0) + self.input.do_command("/") # we add the slash @refresh_wrapper.always def reset_help_message(self, _=None): @@ -266,10 +291,10 @@ class XMLTab(Tab): return True def on_scroll_up(self): - return self.text_win.scroll_up(self.text_win.height-1) + return self.text_win.scroll_up(self.text_win.height - 1) def on_scroll_down(self): - return self.text_win.scroll_down(self.text_win.height-1) + return self.text_win.scroll_down(self.text_win.height - 1) @command_args_parser.ignored def command_clear(self): @@ -314,11 +339,9 @@ class XMLTab(Tab): self.text_win.resize(self.height - info_win_size - tab_win_height - 2, self.width, 0, 0) self.text_win.rebuild_everything(self.core_buffer) - self.info_header.resize(1, self.width, - self.height - 2 - info_win_size - - tab_win_height, - 0) - self.input.resize(1, self.width, self.height-1, 0) + self.info_header.resize( + 1, self.width, self.height - 2 - info_win_size - tab_win_height, 0) + self.input.resize(1, self.width, self.height - 1, 0) def refresh(self): if self.need_resize: @@ -350,9 +373,10 @@ class XMLTab(Tab): self.core.xml_tab = False def on_info_win_size_changed(self): - if self.core.information_win_size >= self.height-3: + if self.core.information_win_size >= self.height - 3: return - self.text_win.resize(self.height-2-self.core.information_win_size - Tab.tab_win_height(), self.width, 0, 0) - self.info_header.resize(1, self.width, self.height-2-self.core.information_win_size - Tab.tab_win_height(), 0) - - + self.text_win.resize(self.height - 2 - self.core.information_win_size - + Tab.tab_win_height(), self.width, 0, 0) + self.info_header.resize( + 1, self.width, self.height - 2 - self.core.information_win_size - + Tab.tab_win_height(), 0) -- cgit v1.2.3