diff options
author | mathieui <mathieui@mathieui.net> | 2017-11-12 15:03:09 +0100 |
---|---|---|
committer | mathieui <mathieui@mathieui.net> | 2017-11-12 15:03:09 +0100 |
commit | d55cc5872503567775f0d7a7731d6f489bf2299b (patch) | |
tree | 725f9e7b8144d36054447b3c82edfb45bda8df1d /poezio/core | |
parent | 92496db823db34f7f7fb1ab31eaef093a707c3e8 (diff) | |
download | poezio-d55cc5872503567775f0d7a7731d6f489bf2299b.tar.gz poezio-d55cc5872503567775f0d7a7731d6f489bf2299b.tar.bz2 poezio-d55cc5872503567775f0d7a7731d6f489bf2299b.tar.xz poezio-d55cc5872503567775f0d7a7731d6f489bf2299b.zip |
yapf -ir
Diffstat (limited to 'poezio/core')
-rw-r--r-- | poezio/core/__init__.py | 1 | ||||
-rw-r--r-- | poezio/core/commands.py | 202 | ||||
-rw-r--r-- | poezio/core/completions.py | 184 | ||||
-rw-r--r-- | poezio/core/core.py | 816 | ||||
-rw-r--r-- | poezio/core/handlers.py | 659 | ||||
-rw-r--r-- | poezio/core/structs.py | 14 |
6 files changed, 1116 insertions, 760 deletions
diff --git a/poezio/core/__init__.py b/poezio/core/__init__.py index 0c6d63d9..cfe4c179 100644 --- a/poezio/core/__init__.py +++ b/poezio/core/__init__.py @@ -5,4 +5,3 @@ __all__ = ['Core', 'Command', 'Status'] from poezio.core.core import Core from poezio.core.structs import Command, Status - diff --git a/poezio/core/commands.py b/poezio/core/commands.py index 5a28182b..ab0ced9a 100644 --- a/poezio/core/commands.py +++ b/poezio/core/commands.py @@ -46,10 +46,8 @@ class CommandCore: buff = ['Global commands:'] for name, command in self.core.commands.items(): if isinstance(command, Command): - acc.append(' \x19%s}%s\x19o - %s' % ( - color, - name, - command.short_desc)) + acc.append(' \x19%s}%s\x19o - %s' % (color, name, + command.short_desc)) else: acc.append(' \x19%s}%s\x19o' % (color, name)) acc = sorted(acc) @@ -59,10 +57,8 @@ class CommandCore: tab_commands = self.core.current_tab().commands for name, command in tab_commands.items(): if isinstance(command, Command): - acc.append(' \x19%s}%s\x19o - %s' % ( - color, - name, - command.short_desc)) + acc.append(' \x19%s}%s\x19o - %s' % (color, name, + command.short_desc)) else: acc.append(' \x19%s}%s\x19o' % (color, name)) acc = sorted(acc) @@ -93,11 +89,13 @@ class CommandCore: """ /runkey <key> """ + def replace_line_breaks(key): "replace ^J with \n" if key == '^J': return '\n' return key + if args is None: return self.help('runkey') char = args[0] @@ -135,7 +133,8 @@ class CommandCore: current.send_chat_state('inactive') for tab in self.core.tabs: if isinstance(tab, tabs.MucTab) and tab.joined: - muc.change_show(self.core.xmpp, tab.name, tab.own_nick, show, msg) + muc.change_show(self.core.xmpp, tab.name, tab.own_nick, show, + msg) if hasattr(tab, 'directed_presence'): del tab.directed_presence self.core.set_status(show, msg) @@ -156,12 +155,14 @@ class CommandCore: if ptype == 'available': ptype = None try: - pres = self.core.xmpp.make_presence(pto=jid, ptype=ptype, pstatus=status) + pres = self.core.xmpp.make_presence( + pto=jid, ptype=ptype, pstatus=status) self.core.events.trigger('send_normal_presence', pres) pres.send() except (XMPPError, NotConnectedError): self.core.information('Could not send directed presence', 'Error') - log.debug('Could not send directed presence to %s', jid, exc_info=True) + log.debug( + 'Could not send directed presence to %s', jid, exc_info=True) return tab = self.core.get_tab_by_name(jid) if tab: @@ -184,7 +185,7 @@ class CommandCore: """/theme <theme name>""" if args is None: return self.help('theme') - self.set('theme %s' % (args[0],)) + self.set('theme %s' % (args[0], )) @command_args_parser.quoted(1) def win(self, args): @@ -254,10 +255,12 @@ class CommandCore: if not old_tab and value == tab.name: old_tab = tab if not old_tab: - self.core.information("Tab %s does not exist" % args[0], "Error") + self.core.information("Tab %s does not exist" % args[0], + "Error") return None ref = old_tab.nb return ref + old = get_nb_from_value(args[0]) new = get_nb_from_value(args[1]) if new is None or old is None: @@ -281,19 +284,20 @@ class CommandCore: jid = safeJID(args[0]) else: if not isinstance(self.core.current_tab(), tabs.MucTab): - return self.core.information('Please provide a server', 'Error') + return self.core.information('Please provide a server', + 'Error') jid = safeJID(self.core.current_tab().name) list_tab = tabs.MucListTab(self.core, jid) self.core.add_tab(list_tab, True) cb = list_tab.on_muc_list_item_received - self.core.xmpp.plugin['xep_0030'].get_items(jid=jid, - callback=cb) + self.core.xmpp.plugin['xep_0030'].get_items(jid=jid, callback=cb) @command_args_parser.quoted(1) def version(self, args): """ /version <jid> """ + def callback(res): "Callback for /version" if not res: @@ -301,10 +305,9 @@ class CommandCore: ' 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: @@ -315,7 +318,8 @@ class CommandCore: fixes.get_version(self.core.xmpp, jid, callback=callback) elif jid in roster: for resource in roster[jid].resources: - fixes.get_version(self.core.xmpp, resource.jid, callback=callback) + fixes.get_version( + self.core.xmpp, resource.jid, callback=callback) def _empty_join(self): tab = self.core.current_tab() @@ -372,7 +376,7 @@ class CommandCore: else: room, nick = self._parse_join_jid(args[0]) if not room and not nick: - return # nothing was parsed + return # nothing was parsed room = room.lower() if nick == '': @@ -467,11 +471,13 @@ class CommandCore: bookmark.nick = nick if password: bookmark.password = password + def callback(iq): if iq["type"] != "error": self.core.information('Bookmark added.', 'Info') else: self.core.information("Could not add the bookmarks.", "Info") + self.core.bookmarks.save_local() self.core.bookmarks.save_remote(self.core.xmpp, callback) @@ -480,8 +486,7 @@ class CommandCore: for tab in self.core.get_tabs(tabs.MucTab): bookmark = self.core.bookmarks[tab.name] if not bookmark: - bookmark = Bookmark(tab.name, autojoin=True, - method=method) + bookmark = Bookmark(tab.name, autojoin=True, method=method) new_bookmarks.append(bookmark) else: bookmark.method = method @@ -489,11 +494,14 @@ class CommandCore: self.core.bookmarks.remove(bookmark) new_bookmarks.extend(self.core.bookmarks.bookmarks) self.core.bookmarks.set(new_bookmarks) + def _cb(iq): if iq["type"] != "error": self.core.information("Bookmarks saved.", "Info") else: - self.core.information("Could not save the remote bookmarks.", "Info") + self.core.information("Could not save the remote bookmarks.", + "Info") + self.core.bookmarks.save_local() self.core.bookmarks.save_remote(self.core.xmpp, _cb) @@ -520,7 +528,8 @@ class CommandCore: if success: self.core.information('Bookmark deleted', 'Info') else: - self.core.information('Error while deleting the bookmark', 'Error') + self.core.information('Error while deleting the bookmark', + 'Error') if not args: tab = self.core.current_tab() @@ -546,15 +555,17 @@ class CommandCore: lines = [] theme = get_theme() for section_name, section in config_dict.items(): - lines.append('\x19%(section_col)s}[%(section)s]\x19o' % - { - 'section': section_name, - 'section_col': dump_tuple(theme.COLOR_INFORMATION_TEXT), - }) + lines.append( + '\x19%(section_col)s}[%(section)s]\x19o' % { + 'section': section_name, + 'section_col': dump_tuple( + theme.COLOR_INFORMATION_TEXT), + }) for option_name, option_value in section.items(): - lines.append('%s\x19%s}=\x19o%s' % (option_name, - dump_tuple(theme.COLOR_REVISIONS_MESSAGE), - option_value)) + lines.append('%s\x19%s}=\x19o%s' % + (option_name, + dump_tuple(theme.COLOR_REVISIONS_MESSAGE), + option_value)) info = ('Current options:\n%s' % '\n'.join(lines), 'Info') elif len(args) == 1: option = args[0] @@ -573,7 +584,8 @@ class CommandCore: file_name = os.path.join(file_name, plugin_name + '.cfg') plugin_config = PluginConfig(file_name, plugin_name) else: - plugin_config = self.core.plugin_manager.plugins[plugin_name].config + plugin_config = self.core.plugin_manager.plugins[ + plugin_name].config value = plugin_config.get(option, default='', section=section) info = ('%s=%s' % (option, value), 'Info') else: @@ -600,14 +612,15 @@ class CommandCore: file_name = os.path.join(file_name, plugin_name + '.cfg') plugin_config = PluginConfig(file_name, plugin_name) else: - plugin_config = self.core.plugin_manager.plugins[plugin_name].config + plugin_config = self.core.plugin_manager.plugins[ + plugin_name].config info = plugin_config.set_and_save(option, value, section) else: if args[0] == '.': name = safeJID(self.core.current_tab().name).bare if not name: - self.core.information('Invalid tab to use the "." argument.', - 'Error') + self.core.information( + 'Invalid tab to use the "." argument.', 'Error') return section = name else: @@ -679,35 +692,35 @@ class CommandCore: """ /last_activity <jid> """ + def callback(iq): "Callback for the last activity" if iq['type'] != 'result': if iq['error']['type'] == 'auth': self.core.information('You are not allowed to see the ' - 'activity of this contact.', - 'Error') + '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'] from_ = iq['from'] if not safeJID(from_).user: msg = 'The uptime of %s is %s.' % ( - from_, - common.parse_secs_to_str(seconds)) + from_, common.parse_secs_to_str(seconds)) else: msg = 'The last activity of %s was %s ago%s' % ( - from_, - common.parse_secs_to_str(seconds), - (' and his/her last status was %s' % status) if status else '') + from_, common.parse_secs_to_str(seconds), + (' and his/her last status was %s' % status) + if status else '') self.core.information(msg, 'Info') if args is None: return self.help('last_activity') jid = safeJID(args[0]) - self.core.xmpp.plugin['xep_0012'].get_last_activity(jid, - callback=callback) + self.core.xmpp.plugin['xep_0012'].get_last_activity( + jid, callback=callback) @command_args_parser.quoted(0, 2) def mood(self, args): @@ -719,15 +732,14 @@ class CommandCore: mood = args[0] if mood not in pep.MOODS: - return self.core.information('%s is not a correct value for a mood.' - % mood, - 'Error') + return self.core.information( + '%s is not a correct value for a mood.' % mood, 'Error') if len(args) == 2: text = args[1] else: text = None - self.core.xmpp.plugin['xep_0107'].publish_mood(mood, text, - callback=dumb_callback) + self.core.xmpp.plugin['xep_0107'].publish_mood( + mood, text, callback=dumb_callback) @command_args_parser.quoted(0, 3) def activity(self, args): @@ -740,9 +752,8 @@ class CommandCore: general = args[0] if general not in pep.ACTIVITIES: - return self.core.information('%s is not a correct value for an activity' - % general, - 'Error') + return self.core.information( + '%s is not a correct value for an activity' % general, 'Error') specific = None text = None if length == 2: @@ -755,10 +766,9 @@ class CommandCore: text = args[2] if specific and specific not in pep.ACTIVITIES[general]: return self.core.information('%s is not a correct value ' - 'for an activity' % specific, - 'Error') - self.core.xmpp.plugin['xep_0108'].publish_activity(general, specific, text, - callback=dumb_callback) + 'for an activity' % specific, 'Error') + self.core.xmpp.plugin['xep_0108'].publish_activity( + general, specific, text, callback=dumb_callback) @command_args_parser.quoted(0, 2) def gaming(self, args): @@ -773,9 +783,8 @@ class CommandCore: address = args[1] else: address = None - return self.core.xmpp.plugin['xep_0196'].publish_gaming(name=name, - server_address=address, - callback=dumb_callback) + return self.core.xmpp.plugin['xep_0196'].publish_gaming( + name=name, server_address=address, callback=dumb_callback) @command_args_parser.quoted(2, 1, [None]) def invite(self, args): @@ -800,9 +809,9 @@ class CommandCore: return reason = args[1] del self.core.pending_invites[jid.bare] - self.core.xmpp.plugin['xep_0045'].decline_invite(jid.bare, - self.core.pending_invites[jid.bare], - reason) + self.core.xmpp.plugin['xep_0045'].decline_invite( + jid.bare, self.core.pending_invites[jid.bare], reason) + ### Commands without a completion in this class ### @@ -811,8 +820,8 @@ class CommandCore: """/invitations""" build = "" for invite in self.core.pending_invites: - build += "%s by %s" % (invite, - safeJID(self.core.pending_invites[invite]).bare) + build += "%s by %s" % ( + invite, safeJID(self.core.pending_invites[invite]).bare) if self.core.pending_invites: build = "You are invited to the following rooms:\n" + build else: @@ -838,7 +847,8 @@ class CommandCore: self.core.save_config() self.core.plugin_manager.disable_plugins() self.core.disconnect(msg) - self.core.xmpp.add_event_handler("disconnected", self.core.exit, disposable=True) + self.core.xmpp.add_event_handler( + "disconnected", self.core.exit, disposable=True) @command_args_parser.quoted(0, 1, ['']) def destroy_room(self, args): @@ -849,7 +859,8 @@ class CommandCore: if room: muc.destroy_room(self.core.xmpp, room) elif isinstance(self.core.current_tab(), tabs.MucTab) and not args[0]: - muc.destroy_room(self.core.xmpp, self.core.current_tab().general_jid) + muc.destroy_room(self.core.xmpp, + self.core.current_tab().general_jid) else: self.core.information('Invalid JID: "%s"' % args[0], 'Error') @@ -862,12 +873,15 @@ class CommandCore: return self.help('bind') if not config.silent_set(args[0], args[1], section='bindings'): - self.core.information('Unable to write in the config file', 'Error') + self.core.information('Unable to write in the config file', + 'Error') if args[1]: - self.core.information('%s is now bound to %s' % (args[0], args[1]), 'Info') + self.core.information('%s is now bound to %s' % (args[0], args[1]), + 'Info') else: - self.core.information('%s is now reset to the default binding' % args[0], 'Info') + self.core.information( + '%s is now reset to the default binding' % args[0], 'Info') @command_args_parser.raw def rawxml(self, args): @@ -881,7 +895,8 @@ class CommandCore: stanza = args try: stanza = StanzaBase(self.core.xmpp, xml=ET.fromstring(stanza)) - if stanza.xml.tag == 'iq' and stanza.xml.attrib.get('type') in ('get', 'set'): + if stanza.xml.tag == 'iq' and stanza.xml.attrib.get('type') in ( + 'get', 'set'): iq_id = stanza.xml.attrib.get('id') if not iq_id: iq_id = self.core.xmpp.new_id() @@ -893,18 +908,15 @@ class CommandCore: self.core.xmpp.remove_handler('Iq %s' % iq_id) self.core.xmpp.register_handler( - Callback('Iq %s' % iq_id, - StanzaPath('iq@id=%s' % iq_id), - iqfunc - ) - ) + Callback('Iq %s' % iq_id, + StanzaPath('iq@id=%s' % iq_id), iqfunc)) stanza.send() except: self.core.information('Could not send custom stanza', 'Error') - log.debug('/rawxml: Could not send custom stanza (%s)', - repr(stanza), - exc_info=True) - + log.debug( + '/rawxml: Could not send custom stanza (%s)', + repr(stanza), + exc_info=True) @command_args_parser.quoted(1, 256) def load(self, args): @@ -928,9 +940,8 @@ class CommandCore: """ /plugins """ - self.core.information("Plugins currently in use: %s" % - repr(list(self.core.plugin_manager.plugins.keys())), - 'Info') + self.core.information("Plugins currently in use: %s" % repr( + list(self.core.plugin_manager.plugins.keys())), 'Info') @command_args_parser.quoted(1, 1) def message(self, args): @@ -942,7 +953,8 @@ class CommandCore: jid = safeJID(args[0]) if not jid.user and not jid.domain and not jid.resource: return self.core.information('Invalid JID.', 'Error') - tab = self.core.get_conversation_by_jid(jid.full, False, fallback_barejid=False) + tab = self.core.get_conversation_by_jid( + jid.full, False, fallback_barejid=False) muc = self.core.get_tab_by_name(jid.bare, typ=tabs.MucTab) if not tab and not muc: tab = self.core.open_conversation_window(jid.full, focus=True) @@ -977,8 +989,8 @@ class CommandCore: list_tab = tabs.AdhocCommandsListTab(self.core, jid) self.core.add_tab(list_tab, True) cb = list_tab.on_list_received - self.core.xmpp.plugin['xep_0050'].get_commands(jid=jid, local=False, - callback=cb) + self.core.xmpp.plugin['xep_0050'].get_commands( + jid=jid, local=False, callback=cb) @command_args_parser.ignored def self_(self): @@ -990,15 +1002,11 @@ class CommandCore: nick = self.core.own_nick jid = self.core.xmpp.boundjid.full info = ('Your JID is %s\nYour current status is "%s" (%s)' - '\nYour default nickname is %s\nYou are running poezio %s' % ( - jid, - message if message else '', - show if show else 'available', - nick, - config_opts.version)) + '\nYour default nickname is %s\nYou are running poezio %s' % + (jid, message if message else '', show + if show else 'available', nick, config_opts.version)) self.core.information(info, 'Info') - @command_args_parser.ignored def reload(self): """ @@ -1006,6 +1014,6 @@ class CommandCore: """ self.core.reload_config() + def dumb_callback(*args, **kwargs): "mock callback" - diff --git a/poezio/core/completions.py b/poezio/core/completions.py index 634ab6b3..5e7e510f 100644 --- a/poezio/core/completions.py +++ b/poezio/core/completions.py @@ -17,13 +17,15 @@ from poezio.roster import roster from poezio.core.structs import POSSIBLE_SHOW, Completion + class CompletionCore: def __init__(self, core): self.core = core def help(self, the_input): """Completion for /help.""" - commands = sorted(self.core.commands.keys()) + sorted(self.core.current_tab().commands.keys()) + commands = sorted(self.core.commands.keys()) + sorted( + self.core.current_tab().commands.keys()) return Completion(the_input.new_completion, commands, 1, quotify=False) def status(self, the_input): @@ -31,8 +33,11 @@ class CompletionCore: Completion of /status """ if the_input.get_argument_position() == 1: - return Completion(the_input.new_completion, [status for status in POSSIBLE_SHOW], 1, ' ', quotify=False) - + return Completion( + the_input.new_completion, [status for status in POSSIBLE_SHOW], + 1, + ' ', + quotify=False) def presence(self, the_input): """ @@ -40,29 +45,38 @@ class CompletionCore: """ arg = the_input.get_argument_position() if arg == 1: - return Completion(the_input.auto_completion, [jid for jid in roster.jids()], '', quotify=True) + return Completion( + the_input.auto_completion, [jid for jid in roster.jids()], + '', + quotify=True) elif arg == 2: - return Completion(the_input.auto_completion, [status for status in POSSIBLE_SHOW], '', quotify=True) - + return Completion( + the_input.auto_completion, + [status for status in POSSIBLE_SHOW], + '', + quotify=True) def theme(self, the_input): """ Completion for /theme""" themes_dir = config.get('themes_dir') - themes_dir = (themes_dir or - os.path.join(os.environ.get('XDG_DATA_HOME') or - os.path.join(os.environ.get('HOME'), '.local', 'share'), - 'poezio', 'themes')) + themes_dir = (themes_dir or os.path.join( + os.environ.get('XDG_DATA_HOME') + or os.path.join(os.environ.get('HOME'), '.local', 'share'), + 'poezio', 'themes')) themes_dir = os.path.expanduser(themes_dir) try: names = os.listdir(themes_dir) except OSError: log.error('Completion for /theme failed', exc_info=True) return False - theme_files = [name[:-3] for name in names if name.endswith('.py') and name != '__init__.py'] + theme_files = [ + name[:-3] for name in names + if name.endswith('.py') and name != '__init__.py' + ] if 'default' not in theme_files: theme_files.append('default') - return Completion(the_input.new_completion, theme_files, 1, '', quotify=False) - + return Completion( + the_input.new_completion, theme_files, 1, '', quotify=False) def win(self, the_input): """Completion for /win""" @@ -72,7 +86,6 @@ class CompletionCore: l = [i[1] for i in l] return Completion(the_input.new_completion, l, 1, '', quotify=False) - def join(self, the_input): """ Completion for /join @@ -97,7 +110,9 @@ class CompletionCore: relevant_rooms = [] relevant_rooms.extend(sorted(self.core.pending_invites.keys())) - bookmarks = [(str(elem.jid) if not elem.nick else '%s/%s' % (elem.jid, elem.nick)) for elem in self.core.bookmarks] + bookmarks = [(str(elem.jid) + if not elem.nick else '%s/%s' % (elem.jid, elem.nick)) + for elem in self.core.bookmarks] to_suggest = [] for bookmark in bookmarks: tab = self.core.get_tab_by_name(bookmark, tabs.MucTab) @@ -113,31 +128,39 @@ class CompletionCore: serv_list = [] for tab in self.core.get_tabs(tabs.MucTab): if tab.joined: - serv_list.append('%s@%s' % (jid.user, safeJID(tab.name).host)) + serv_list.append('%s@%s' % (jid.user, + safeJID(tab.name).host)) serv_list.extend(relevant_rooms) - return Completion(the_input.new_completion, serv_list, 1, quotify=True) + return Completion( + the_input.new_completion, serv_list, 1, quotify=True) elif args[1].startswith('/'): # we completing only a resource - return Completion(the_input.new_completion, ['/%s' % self.core.own_nick], 1, quotify=True) + return Completion( + the_input.new_completion, ['/%s' % self.core.own_nick], + 1, + quotify=True) else: - return Completion(the_input.new_completion, relevant_rooms, 1, quotify=True) - + return Completion( + the_input.new_completion, relevant_rooms, 1, quotify=True) def version(self, the_input): """Completion for /version""" - comp = reduce(lambda x, y: x + [i.jid for i in y], (roster[jid].resources for jid in roster.jids() if len(roster[jid])), []) - return Completion(the_input.new_completion, sorted(comp), 1, quotify=False) - + comp = reduce(lambda x, y: x + [i.jid for i in y], + (roster[jid].resources for jid in roster.jids() + if len(roster[jid])), []) + return Completion( + the_input.new_completion, sorted(comp), 1, quotify=False) def list(self, the_input): """Completion for /list""" muc_serv_list = [] - for tab in self.core.get_tabs(tabs.MucTab): # TODO, also from an history + for tab in self.core.get_tabs( + tabs.MucTab): # TODO, also from an history if tab.name not in muc_serv_list: muc_serv_list.append(safeJID(tab.name).server) if muc_serv_list: - return Completion(the_input.new_completion, muc_serv_list, 1, quotify=False) - + return Completion( + the_input.new_completion, muc_serv_list, 1, quotify=False) def move_tab(self, the_input): """Completion for /move_tab""" @@ -145,8 +168,8 @@ class CompletionCore: if n == 1: nodes = [tab.name for tab in self.core.tabs if tab] nodes.remove('Roster') - return Completion(the_input.new_completion, nodes, 1, ' ', quotify=True) - + return Completion( + the_input.new_completion, nodes, 1, ' ', quotify=True) def runkey(self, the_input): """ @@ -157,14 +180,14 @@ class CompletionCore: list_.extend(self.core.current_tab().key_func.keys()) return Completion(the_input.new_completion, list_, 1, quotify=False) - def bookmark(self, the_input): """Completion for /bookmark""" args = common.shell_split(the_input.text) n = the_input.get_argument_position(quoted=True) if n == 2: - return Completion(the_input.new_completion, ['true', 'false'], 2, quotify=True) + return Completion( + the_input.new_completion, ['true', 'false'], 2, quotify=True) if n >= 3: return False @@ -175,7 +198,8 @@ class CompletionCore: if jid.server and (jid.resource or jid.full.endswith('/')): tab = self.core.get_tab_by_name(jid.bare, tabs.MucTab) nicks = [tab.own_nick] if tab else [] - default = os.environ.get('USER') if os.environ.get('USER') else 'poezio' + default = os.environ.get('USER') if os.environ.get( + 'USER') else 'poezio' nick = config.get('default_nick') if not nick: if default not in nicks: @@ -184,29 +208,37 @@ class CompletionCore: if nick not in nicks: nicks.append(nick) jids_list = ['%s/%s' % (jid.bare, nick) for nick in nicks] - return Completion(the_input.new_completion, jids_list, 1, quotify=True) + return Completion( + the_input.new_completion, jids_list, 1, quotify=True) muc_list = [tab.name for tab in self.core.get_tabs(tabs.MucTab)] muc_list.sort() muc_list.append('*') return Completion(the_input.new_completion, muc_list, 1, quotify=True) - def remove_bookmark(self, the_input): """Completion for /remove_bookmark""" - return Completion(the_input.new_completion, [bm.jid for bm in self.core.bookmarks], 1, quotify=False) - + return Completion( + the_input.new_completion, [bm.jid for bm in self.core.bookmarks], + 1, + quotify=False) def decline(self, the_input): """Completion for /decline""" n = the_input.get_argument_position(quoted=True) if n == 1: - return Completion(the_input.auto_completion, sorted(self.core.pending_invites.keys()), 1, '', quotify=True) - + return Completion( + the_input.auto_completion, + sorted(self.core.pending_invites.keys()), + 1, + '', + quotify=True) def bind(self, the_input): n = the_input.get_argument_position() if n == 1: - args = [key for key in self.core.key_func if not key.startswith('_')] + args = [ + key for key in self.core.key_func if not key.startswith('_') + ] elif n == 2: args = [key for key in self.core.key_func] else: @@ -214,7 +246,6 @@ class CompletionCore: return Completion(the_input.new_completion, args, n, '', quotify=False) - def message(self, the_input): """Completion for /message""" n = the_input.get_argument_position(quoted=True) @@ -231,14 +262,17 @@ class CompletionCore: l.append(jid) return Completion(the_input.new_completion, l, 1, '', quotify=True) - def invite(self, the_input): """Completion for /invite""" n = the_input.get_argument_position(quoted=True) if n == 1: - comp = reduce(lambda x, y: x + [i.jid for i in y], (roster[jid].resources for jid in roster.jids() if len(roster[jid])), []) + comp = reduce(lambda x, y: x + [i.jid for i in y], + (roster[jid].resources for jid in roster.jids() + if len(roster[jid])), []) comp = sorted(comp) - bares = sorted(roster[contact].bare_jid for contact in roster.jids() if len(roster[contact])) + bares = sorted( + roster[contact].bare_jid for contact in roster.jids() + if len(roster[contact])) off = sorted(jid for jid in roster.jids() if jid not in bares) comp = comp + bares + off return Completion(the_input.new_completion, comp, n, quotify=True) @@ -248,15 +282,19 @@ class CompletionCore: if tab.joined: rooms.append(tab.name) rooms.sort() - return Completion(the_input.new_completion, rooms, n, '', quotify=True) - + return Completion( + the_input.new_completion, rooms, n, '', quotify=True) def activity(self, the_input): """Completion for /activity""" n = the_input.get_argument_position(quoted=True) args = common.shell_split(the_input.text) if n == 1: - return Completion(the_input.new_completion, sorted(pep.ACTIVITIES.keys()), n, quotify=True) + return Completion( + the_input.new_completion, + sorted(pep.ACTIVITIES.keys()), + n, + quotify=True) elif n == 2: if args[1] in pep.ACTIVITIES: l = list(pep.ACTIVITIES[args[1]]) @@ -264,13 +302,15 @@ class CompletionCore: l.sort() return Completion(the_input.new_completion, l, n, quotify=True) - def mood(self, the_input): """Completion for /mood""" n = the_input.get_argument_position(quoted=True) if n == 1: - return Completion(the_input.new_completion, sorted(pep.MOODS.keys()), 1, quotify=True) - + return Completion( + the_input.new_completion, + sorted(pep.MOODS.keys()), + 1, + quotify=True) def last_activity(self, the_input): """ @@ -279,9 +319,11 @@ class CompletionCore: n = the_input.get_argument_position(quoted=False) if n >= 2: return False - comp = reduce(lambda x, y: x + [i.jid for i in y], (roster[jid].resources for jid in roster.jids() if len(roster[jid])), []) - return Completion(the_input.new_completion, sorted(comp), 1, '', quotify=False) - + comp = reduce(lambda x, y: x + [i.jid for i in y], + (roster[jid].resources for jid in roster.jids() + if len(roster[jid])), []) + return Completion( + the_input.new_completion, sorted(comp), 1, '', quotify=False) def server_cycle(self, the_input): """Completion for /server_cycle""" @@ -291,7 +333,6 @@ class CompletionCore: serv_list.add(serv) return Completion(the_input.new_completion, sorted(serv_list), 1, ' ') - def set(self, the_input): """Completion for /set""" args = common.shell_split(the_input.text) @@ -302,9 +343,13 @@ class CompletionCore: if '|' in args[1]: plugin_name, section = args[1].split('|')[:2] if plugin_name not in self.core.plugin_manager.plugins: - return Completion(the_input.new_completion, [], n, quotify=True) + return Completion( + the_input.new_completion, [], n, quotify=True) plugin = self.core.plugin_manager.plugins[plugin_name] - end_list = ['%s|%s' % (plugin_name, section) for section in plugin.config.sections()] + end_list = [ + '%s|%s' % (plugin_name, section) + for section in plugin.config.sections() + ] else: end_list = set(config.options('Poezio')) end_list.update(config.default.get('Poezio', {})) @@ -314,11 +359,13 @@ class CompletionCore: if '|' in args[1]: plugin_name, section = args[1].split('|')[:2] if plugin_name not in self.core.plugin_manager.plugins: - return Completion(the_input.new_completion, [''], n, quotify=True) + return Completion( + the_input.new_completion, [''], n, quotify=True) plugin = self.core.plugin_manager.plugins[plugin_name] end_list = set(plugin.config.options(section or plugin_name)) if plugin.config.default: - end_list.update(plugin.config.default.get(section or plugin_name, {})) + end_list.update( + plugin.config.default.get(section or plugin_name, {})) end_list = list(end_list) end_list.sort() elif not config.has_option('Poezio', args[1]): @@ -333,9 +380,14 @@ class CompletionCore: if '|' in args[1]: plugin_name, section = args[1].split('|')[:2] if plugin_name not in self.core.plugin_manager.plugins: - return Completion(the_input.new_completion, [''], n, quotify=True) + return Completion( + the_input.new_completion, [''], n, quotify=True) plugin = self.core.plugin_manager.plugins[plugin_name] - end_list = [str(plugin.config.get(args[2], '', section or plugin_name)), ''] + end_list = [ + str( + plugin.config.get(args[2], '', section + or plugin_name)), '' + ] else: if not config.has_section(args[1]): end_list = [''] @@ -345,7 +397,6 @@ class CompletionCore: return False return Completion(the_input.new_completion, end_list, n, quotify=True) - def set_default(self, the_input): """ Completion for /set_default """ @@ -357,11 +408,13 @@ class CompletionCore: return Completion(self.set, the_input) return False - def toggle(self, the_input): "Completion for /toggle" - return Completion(the_input.new_completion, config.options('Poezio'), 1, quotify=False) - + return Completion( + the_input.new_completion, + config.options('Poezio'), + 1, + quotify=False) def bookmark_local(self, the_input): """Completion for /bookmark_local""" @@ -377,7 +430,8 @@ class CompletionCore: if jid.server and (jid.resource or jid.full.endswith('/')): tab = self.core.get_tab_by_name(jid.bare, tabs.MucTab) nicks = [tab.own_nick] if tab else [] - default = os.environ.get('USER') if os.environ.get('USER') else 'poezio' + default = os.environ.get('USER') if os.environ.get( + 'USER') else 'poezio' nick = config.get('default_nick') if not nick: if default not in nicks: @@ -386,8 +440,8 @@ class CompletionCore: if nick not in nicks: nicks.append(nick) jids_list = ['%s/%s' % (jid.bare, nick) for nick in nicks] - return Completion(the_input.new_completion, jids_list, 1, quotify=True) + return Completion( + the_input.new_completion, jids_list, 1, quotify=True) muc_list = [tab.name for tab in self.core.get_tabs(tabs.MucTab)] muc_list.append('*') return Completion(the_input.new_completion, muc_list, 1, quotify=True) - diff --git a/poezio/core/core.py b/poezio/core/core.py index 33bbbc54..7678c747 100644 --- a/poezio/core/core.py +++ b/poezio/core/core.py @@ -66,8 +66,7 @@ class Core(object): self.stdscr = None status = config.get('status') status = POSSIBLE_SHOW.get(status, None) - self.status = Status(show=status, - message=config.get('status_message')) + self.status = Status(show=status, message=config.get('status_message')) self.running = True self.xmpp = connection.Connection() self.xmpp.core = self @@ -81,7 +80,8 @@ class Core(object): # that are displayed in almost all tabs, in an # information window. self.information_buffer = TextBuffer() - self.information_win_size = config.get('info_win_height', section='var') + self.information_win_size = config.get( + 'info_win_height', section='var') self.information_win = windows.TextWin(300) self.information_buffer.add_window(self.information_win) self.left_tab_win = None @@ -164,7 +164,7 @@ class Core(object): 'M-D': self.scroll_info_up, 'M-C': self.scroll_info_down, 'M-k': self.escape_next_key, - ######## actions mappings ########## + ######## actions mappings ########## '_noop': lambda *args, **kwargs: None, '_bookmark': self.command.bookmark, '_bookmark_local': self.command.bookmark_local, @@ -188,25 +188,30 @@ class Core(object): '_show_plugins': self.command.plugins, '_show_xmltab': self.command.xml_tab, '_toggle_pane': self.toggle_left_pane, - ###### status actions ###### + ###### status actions ###### '_available': lambda: self.command.status('available'), '_away': lambda: self.command.status('away'), '_chat': lambda: self.command.status('chat'), '_dnd': lambda: self.command.status('dnd'), '_xa': lambda: self.command.status('xa'), - ##### Custom actions ######## + ##### Custom actions ######## '_exc_': self.try_execute, } self.key_func.update(key_func) # Add handlers self.xmpp.add_event_handler('connected', self.handler.on_connected) - self.xmpp.add_event_handler('connection_failed', self.handler.on_failed_connection) - self.xmpp.add_event_handler('disconnected', self.handler.on_disconnected) - self.xmpp.add_event_handler('stream_error', self.handler.on_stream_error) - self.xmpp.add_event_handler('failed_all_auth', self.handler.on_failed_all_auth) + self.xmpp.add_event_handler('connection_failed', + self.handler.on_failed_connection) + self.xmpp.add_event_handler('disconnected', + self.handler.on_disconnected) + self.xmpp.add_event_handler('stream_error', + self.handler.on_stream_error) + self.xmpp.add_event_handler('failed_all_auth', + self.handler.on_failed_all_auth) self.xmpp.add_event_handler('no_auth', self.handler.on_no_auth) - self.xmpp.add_event_handler("session_start", self.handler.on_session_start) + self.xmpp.add_event_handler("session_start", + self.handler.on_session_start) self.xmpp.add_event_handler("session_start", self.handler.on_session_start_features) self.xmpp.add_event_handler("groupchat_presence", @@ -215,8 +220,9 @@ class Core(object): self.handler.on_groupchat_message) self.xmpp.add_event_handler("groupchat_invite", self.handler.on_groupchat_invitation) - self.xmpp.add_event_handler("groupchat_direct_invite", - self.handler.on_groupchat_direct_invitation) + self.xmpp.add_event_handler( + "groupchat_direct_invite", + self.handler.on_groupchat_direct_invitation) self.xmpp.add_event_handler("groupchat_decline", self.handler.on_groupchat_decline) self.xmpp.add_event_handler("groupchat_config_status", @@ -224,13 +230,17 @@ class Core(object): self.xmpp.add_event_handler("groupchat_subject", self.handler.on_groupchat_subject) self.xmpp.add_event_handler("message", self.handler.on_message) - self.xmpp.add_event_handler("message_error", self.handler.on_error_message) - self.xmpp.add_event_handler("receipt_received", self.handler.on_receipt) + self.xmpp.add_event_handler("message_error", + self.handler.on_error_message) + self.xmpp.add_event_handler("receipt_received", + self.handler.on_receipt) self.xmpp.add_event_handler("got_online", self.handler.on_got_online) self.xmpp.add_event_handler("got_offline", self.handler.on_got_offline) - self.xmpp.add_event_handler("roster_update", self.handler.on_roster_update) + self.xmpp.add_event_handler("roster_update", + self.handler.on_roster_update) self.xmpp.add_event_handler("changed_status", self.handler.on_presence) - self.xmpp.add_event_handler("presence_error", self.handler.on_presence_error) + self.xmpp.add_event_handler("presence_error", + self.handler.on_presence_error) self.xmpp.add_event_handler("roster_subscription_request", self.handler.on_subscription_request) self.xmpp.add_event_handler("roster_subscription_authorized", @@ -252,8 +262,10 @@ class Core(object): self.handler.on_chatstate_inactive) self.xmpp.add_event_handler("attention", self.handler.on_attention) self.xmpp.add_event_handler("ssl_cert", self.handler.validate_ssl) - self.xmpp.add_event_handler("ssl_invalid_chain", self.handler.ssl_invalid_chain) - self.xmpp.add_event_handler('carbon_received', self.handler.on_carbon_received) + self.xmpp.add_event_handler("ssl_invalid_chain", + self.handler.ssl_invalid_chain) + self.xmpp.add_event_handler('carbon_received', + self.handler.on_carbon_received) self.xmpp.add_event_handler('carbon_sent', self.handler.on_carbon_sent) self.xmpp.add_event_handler('http_confirm', self.handler.http_confirm) @@ -315,14 +327,11 @@ class Core(object): self.xmpp.set_keepalive_values) self.add_configuration_handler("connection_check_interval", self.xmpp.set_keepalive_values) - self.add_configuration_handler("themes_dir", - theming.update_themes_dir) - self.add_configuration_handler("theme", - self.on_theme_config_change) + self.add_configuration_handler("themes_dir", theming.update_themes_dir) + self.add_configuration_handler("theme", self.on_theme_config_change) self.add_configuration_handler("use_bookmarks_method", self.on_bookmarks_method_config_change) - self.add_configuration_handler("password", - self.on_password_change) + self.add_configuration_handler("password", self.on_password_change) self.add_configuration_handler("enable_vertical_tab_list", self.on_vertical_tab_list_config_change) self.add_configuration_handler("vertical_tab_list_size", @@ -394,14 +403,15 @@ class Core(object): """ Called when the request_message_receipts option changes """ - self.xmpp.plugin['xep_0184'].auto_request = config.get(option, - default=True) + self.xmpp.plugin['xep_0184'].auto_request = config.get( + option, default=True) def on_ack_receipts_config_change(self, option, value): """ Called when the ack_message_receipts option changes """ - self.xmpp.plugin['xep_0184'].auto_ack = config.get(option, default=True) + self.xmpp.plugin['xep_0184'].auto_ack = config.get( + option, default=True) def on_plugins_dir_config_change(self, option, value): """ @@ -438,7 +448,6 @@ class Core(object): """ self.xmpp.password = value - def on_nick_determinism_changed(self, option, value): """If we change the value to true, we call /recolor on all the MucTabs, to make the current nick colors reflect their deterministic value. @@ -501,10 +510,10 @@ class Core(object): """ sig = args[0] signals = { - 1: 'SIGHUP', - 13: 'SIGPIPE', - 15: 'SIGTERM', - } + 1: 'SIGHUP', + 13: 'SIGPIPE', + 15: 'SIGTERM', + } log.error("%s received. Exiting…", signals[sig]) if config.get('enable_user_mood'): @@ -547,13 +556,12 @@ class Core(object): 'The online help is here http://doc.poez.io/\n' 'No room is joined by default, but you can join poezio’s' ' room (with /join poezio@muc.poez.io), where you can' - ' ask for help or tell us how great it is.', - 'Help') + ' ask for help or tell us how great it is.', 'Help') self.refresh_window() self.xmpp.plugin['xep_0012'].begin_idle(jid=self.xmpp.boundjid) def exit(self, event=None): - log.debug("exit(%s)", event) + log.debug("exit(%s)", event) asyncio.get_event_loop().stop() def on_exception(self, typ, value, trace): @@ -592,11 +600,13 @@ class Core(object): """ main loop waiting for the user to press a key """ + def replace_line_breaks(key): "replace ^J with \n" if key == '^J': return '\n' return key + def separate_chars_from_bindings(char_list): """ returns a list of lists. For example if you give @@ -638,7 +648,7 @@ class Core(object): log.debug("Input is readable.") big_char_list = [replace_key_with_bound(key)\ for key in self.read_keyboard()] - log.debug("Got from keyboard: %s", (big_char_list,)) + log.debug("Got from keyboard: %s", (big_char_list, )) # whether to refresh after ALL keys have been handled for char_list in separate_chars_from_bindings(big_char_list): @@ -651,7 +661,8 @@ class Core(object): except ValueError: pass else: - if self.current_tab().nb == nb and config.get('go_to_previous_tab_on_alt_number'): + if self.current_tab().nb == nb and config.get( + 'go_to_previous_tab_on_alt_number'): self.go_to_previous_tab() else: self.command.win('%d' % nb) @@ -673,12 +684,10 @@ class Core(object): """ ok = roster.save_to_config_file() ok = ok and config.silent_set('info_win_height', - self.information_win_size, - 'var') + self.information_win_size, 'var') if not ok: self.information('Unable to save runtime preferences' - ' in the config file', - 'Error') + ' in the config file', 'Error') def on_roster_enter_key(self, roster_row): """ @@ -690,9 +699,8 @@ class Core(object): else: self.focus_tab_named(roster_row.bare_jid) if isinstance(roster_row, Resource): - if not self.get_conversation_by_jid(roster_row.jid, - False, - fallback_barejid=False): + if not self.get_conversation_by_jid( + roster_row.jid, False, fallback_barejid=False): self.open_conversation_window(roster_row.jid) else: self.focus_tab_named(roster_row.jid) @@ -716,7 +724,6 @@ class Core(object): """ self.do_command(text, True) - ##################### Anything related to command execution ################### def execute(self, line): @@ -727,15 +734,14 @@ class Core(object): return if line.startswith('/'): command = line.strip().split()[0][1:] - arg = line[2+len(command):] # jump the '/' and the ' ' + arg = line[2 + len(command):] # jump the '/' and the ' ' # example. on "/link 0 open", command = "link" and arg = "0 open" if command in self.commands: func = self.commands[command].func func(arg) return else: - self.information("Unknown command (%s)" % (command), - 'Error') + self.information("Unknown command (%s)" % (command), 'Error') def exec_command(self, command): """ @@ -770,16 +776,15 @@ class Core(object): fifo_path = config.get('remote_fifo_path') if not self.remote_fifo: try: - self.remote_fifo = Fifo(os.path.join(fifo_path, - 'poezio.fifo'), - 'w') + self.remote_fifo = Fifo( + os.path.join(fifo_path, 'poezio.fifo'), 'w') except (OSError, IOError) as exc: - log.error('Could not open the fifo for writing (%s)', - os.path.join(fifo_path, './', 'poezio.fifo'), - exc_info=True) + log.error( + 'Could not open the fifo for writing (%s)', + os.path.join(fifo_path, './', 'poezio.fifo'), + exc_info=True) self.information('Could not open the fifo ' - 'file for writing: %s' % exc, - 'Error') + 'file for writing: %s' % exc, 'Error') return args = (pipes.quote(arg.replace('\n', ' ')) for arg in command) @@ -787,10 +792,11 @@ class Core(object): try: self.remote_fifo.write(command_str) except (IOError) as exc: - log.error('Could not write in the fifo (%s): %s', - os.path.join(fifo_path, './', 'poezio.fifo'), - repr(command), - exc_info=True) + log.error( + 'Could not write in the fifo (%s): %s', + os.path.join(fifo_path, './', 'poezio.fifo'), + repr(command), + exc_info=True) self.information('Could not execute %s: %s' % (command, exc), 'Error') self.remote_fifo = None @@ -799,12 +805,12 @@ class Core(object): try: executor.start() except ValueError as exc: - log.error('Could not execute command (%s)', - repr(command), - exc_info=True) + log.error( + 'Could not execute command (%s)', + repr(command), + exc_info=True) self.information('%s' % exc, 'Error') - def do_command(self, key, raw): """ Execute the action associated with a key @@ -824,7 +830,6 @@ class Core(object): else: self.current_tab().on_input(key, raw) - def try_execute(self, line): """ Try to execute a command in the current tab @@ -835,7 +840,6 @@ class Core(object): except: log.error('Execute failed (%s)', line, exc_info=True) - ########################## TImed Events ####################################### def remove_timed_event(self, event): @@ -844,9 +848,8 @@ class Core(object): def add_timed_event(self, event): """Add a new timed event""" - event.handler = asyncio.get_event_loop().call_later(event.delay, - event.callback, - *event.args) + event.handler = asyncio.get_event_loop().call_later( + event.delay, event.callback, *event.args) ####################### XMPP-related actions ################################## @@ -894,7 +897,10 @@ class Core(object): if reconnect: # Add a one-time event to reconnect as soon as we are # effectively disconnected - self.xmpp.add_event_handler('disconnected', lambda event: self.xmpp.connect(), disposable=True) + self.xmpp.add_event_handler( + 'disconnected', + lambda event: self.xmpp.connect(), + disposable=True) def send_message(self, msg): """ @@ -913,20 +919,19 @@ class Core(object): or a mediated one if it does not. TODO: allow passwords """ + def callback(iq): if not iq: return if 'jabber:x:conference' in iq['disco_info'].get_features(): self.xmpp.plugin['xep_0249'].send_invitation( - jid, - room, - reason=reason) - else: # fallback - self.xmpp.plugin['xep_0045'].invite(room, jid, - reason=reason or '') + jid, room, reason=reason) + else: # fallback + self.xmpp.plugin['xep_0045'].invite( + room, jid, reason=reason or '') - self.xmpp.plugin['xep_0030'].get_info(jid=jid, timeout=5, - callback=callback) + self.xmpp.plugin['xep_0030'].get_info( + jid=jid, timeout=5, callback=callback) def get_error_message(self, stanza, deprecated=False): """ @@ -951,16 +956,22 @@ class Core(object): body = condition or 'Unknown error' if code: message = '%(from)s: %(code)s - %(msg)s: %(body)s' % { - 'from': sender, 'msg': msg, 'body': body, 'code': code} + 'from': sender, + 'msg': msg, + 'body': body, + 'code': code + } else: message = '%(from)s: %(msg)s: %(body)s' % { - 'from': sender, 'msg': msg, 'body': body} + 'from': sender, + 'msg': msg, + 'body': body + } return message - ####################### Tab logic-related things ############################## - ### Tab getters ### +### Tab getters ### def get_tabs(self, cls=None): "Get all the tabs of a type" @@ -1002,8 +1013,8 @@ class Core(object): # We create a dynamic conversation with the bare Jid if # nothing was found (and we lock it to the resource # later) - conversation = self.open_conversation_window(jid.bare, - False) + conversation = self.open_conversation_window( + jid.bare, False) else: conversation = None return conversation @@ -1062,7 +1073,8 @@ class Core(object): if not target: if new_pos < len(self.tabs): old_tab = self.tabs[old_pos] - self.tabs[new_pos], self.tabs[old_pos] = old_tab, tabs.GapTab(self) + self.tabs[new_pos], self.tabs[old_pos] = old_tab, tabs.GapTab( + self) else: self.tabs.append(self.tabs[old_pos]) self.tabs[old_pos] = tabs.GapTab(self) @@ -1139,6 +1151,7 @@ class Core(object): Read 2 more chars and go to the tab with the given number """ + def read_next_digit(digit): try: int(digit) @@ -1156,6 +1169,7 @@ class Core(object): else: # We need to read more digits keyboard.continuation_keys_callback = read_next_digit + keyboard.continuation_keys_callback = read_next_digit def go_to_roster(self): @@ -1164,7 +1178,7 @@ class Core(object): def go_to_previous_tab(self): "Go to the previous tab" - self.command.win('%s' % (self.previous_tab_nb,)) + self.command.win('%s' % (self.previous_tab_nb, )) def go_to_important_room(self): """ @@ -1183,15 +1197,14 @@ class Core(object): else: tab_refs[tab.state].append(tab) # sort the state by priority and remove those with negative priority - states = sorted(tab_refs.keys(), - key=(lambda x: priority.get(x, 0)), - reverse=True) + states = sorted( + tab_refs.keys(), key=(lambda x: priority.get(x, 0)), reverse=True) states = [state for state in states if priority.get(state, -1) >= 0] for state in states: for tab in tab_refs[state]: - if (tab.nb < self.current_tab_nb and - tab_refs[state][-1].nb > self.current_tab_nb): + if (tab.nb < self.current_tab_nb + and tab_refs[state][-1].nb > self.current_tab_nb): continue self.command.win('%s' % tab.nb) return @@ -1202,7 +1215,7 @@ class Core(object): for tab in self.tabs: if tab.name == tab_name: if (type_ and (isinstance(tab, type_))) or not type_: - self.command.win('%s' % (tab.nb,)) + self.command.win('%s' % (tab.nb, )) return True return False @@ -1249,7 +1262,7 @@ class Core(object): """ Open a Private conversation in a MUC and focus if needed. """ - complete_jid = room_name+'/'+user_nick + complete_jid = room_name + '/' + user_nick # if the room exists, focus it and return for tab in self.get_tabs(tabs.PrivateTab): if tab.name == complete_jid: @@ -1301,12 +1314,14 @@ class Core(object): if tab: tab.rename_user(old_nick, user) - def on_user_left_private_conversation(self, room_name, user, status_message): + def on_user_left_private_conversation(self, room_name, user, + status_message): """ The user left the MUC: add a message in the associated private conversation """ - tab = self.get_tab_by_name('%s/%s' % (room_name, user.nick), tabs.PrivateTab) + tab = self.get_tab_by_name('%s/%s' % (room_name, user.nick), + tabs.PrivateTab) if tab: tab.user_left(status_message, user) @@ -1315,7 +1330,8 @@ class Core(object): The user joined a MUC: add a message in the associated private conversation """ - tab = self.get_tab_by_name('%s/%s' % (room_name, nick), tabs.PrivateTab) + tab = self.get_tab_by_name('%s/%s' % (room_name, nick), + tabs.PrivateTab) if tab: tab.user_rejoined(nick) @@ -1352,10 +1368,10 @@ class Core(object): if tab is None: tab = self.current_tab() if isinstance(tab, tabs.RosterInfoTab): - return # The tab 0 should NEVER be closed + return # The tab 0 should NEVER be closed tab.on_close() - del tab.key_func # Remove self references - del tab.commands # and make the object collectable + del tab.key_func # Remove self references + del tab.commands # and make the object collectable nb = tab.nb if was_current: if self.previous_tab_nb != nb: @@ -1365,7 +1381,7 @@ class Core(object): if nb >= len(self.tabs) - 1: self.tabs.remove(tab) nb -= 1 - while not self.tabs[nb]: # remove the trailing gaps + while not self.tabs[nb]: # remove the trailing gaps self.tabs.pop() nb -= 1 else: @@ -1397,7 +1413,6 @@ class Core(object): if self.current_tab() is tab: self.refresh_window() - ####################### Curses and ui-related stuff ########################### def doupdate(self): @@ -1412,18 +1427,21 @@ class Core(object): """ filter_types = config.get('information_buffer_type_filter').split(':') if typ.lower() in filter_types: - log.debug('Did not show the message:\n\t%s> %s \n\tdue to information_popup_type_filter configuration', typ, msg) + log.debug( + 'Did not show the message:\n\t%s> %s \n\tdue to information_popup_type_filter configuration', + typ, msg) return False filter_messages = config.get('filter_info_messages').split(':') for words in filter_messages: if words and words in msg: - log.debug('Did not show the message:\n\t%s> %s \n\tdue to filter_info_messages configuration', typ, msg) + log.debug( + 'Did not show the message:\n\t%s> %s \n\tdue to filter_info_messages configuration', + typ, msg) return False colors = get_theme().INFO_COLORS color = colors.get(typ.lower(), colors.get('default', None)) - nb_lines = self.information_buffer.add_message(msg, - nickname=typ, - nick_color=color) + nb_lines = self.information_buffer.add_message( + msg, nickname=typ, nick_color=color) popup_on = config.get('information_buffer_popup_on').split() if isinstance(self.current_tab(), tabs.RosterInfoTab): self.refresh_window() @@ -1449,8 +1467,8 @@ class Core(object): curses.start_color() curses.use_default_colors() theming.reload_theme() - curses.ungetch(" ") # H4X: without this, the screen is - stdscr.getkey() # erased on the first "getkey()" + curses.ungetch(" ") # H4X: without this, the screen is + stdscr.getkey() # erased on the first "getkey()" def reset_curses(self): """ @@ -1604,9 +1622,8 @@ class Core(object): if time <= 0 or size <= 0: return result = self.grow_information_win(size) - timed_event = timed_events.DelayedEvent(time, - self.shrink_information_win, - result) + timed_event = timed_events.DelayedEvent( + time, self.shrink_information_win, result) self.add_timed_event(timed_event) self.refresh_window() @@ -1627,12 +1644,10 @@ class Core(object): self.information_win_size = tabs.Tab.height - 6 if tabs.Tab.height < 6: self.information_win_size = 0 - height = (tabs.Tab.height - 1 - self.information_win_size - - tabs.Tab.tab_win_height()) - self.information_win.resize(self.information_win_size, - tabs.Tab.width, - height, - 0) + height = (tabs.Tab.height - 1 - self.information_win_size - + tabs.Tab.tab_win_height()) + self.information_win.resize(self.information_win_size, tabs.Tab.width, + height, 0) def resize_global_info_bar(self): """ @@ -1645,16 +1660,15 @@ class Core(object): return try: height, _ = self.stdscr.getmaxyx() - truncated_win = self.stdscr.subwin(height, - config.get('vertical_tab_list_size'), - 0, 0) + truncated_win = self.stdscr.subwin( + height, config.get('vertical_tab_list_size'), 0, 0) except: log.error('Curses error on infobar resize', exc_info=True) return - self.left_tab_win = windows.VerticalGlobalInfoBar(self, truncated_win) + self.left_tab_win = windows.VerticalGlobalInfoBar( + self, truncated_win) elif not self.size.core_degrade_y: - self.tab_win.resize(1, tabs.Tab.width, - tabs.Tab.height - 2, 0) + self.tab_win.resize(1, tabs.Tab.width, tabs.Tab.height - 2, 0) self.left_tab_win = None def add_message_to_text_buffer(self, buff, txt, nickname=None): @@ -1663,7 +1677,8 @@ class Core(object): (in the Info tab of the info window in the RosterTab) """ if not buff: - self.information('Trying to add a message in no room: %s' % txt, 'Error') + self.information('Trying to add a message in no room: %s' % txt, + 'Error') return buff.add_message(txt, nickname=nickname) @@ -1683,11 +1698,11 @@ class Core(object): # the screen that they can occupy, and we draw the tab list on the # remaining space, on the left height, width = self.stdscr.getmaxyx() - if (config.get('enable_vertical_tab_list') and - not self.size.core_degrade_x): + if (config.get('enable_vertical_tab_list') + and not self.size.core_degrade_x): try: scr = self.stdscr.subwin(0, - config.get('vertical_tab_list_size')) + config.get('vertical_tab_list_size')) except: log.error('Curses error on resize', exc_info=True) return @@ -1739,227 +1754,300 @@ class Core(object): """ Register the commands when poezio starts """ - self.register_command('help', self.command.help, - usage='[command]', - shortdesc='\\_o< KOIN KOIN KOIN', - completion=self.completion.help) - self.register_command('join', self.command.join, - usage="[room_name][@server][/nick] [password]", - desc="Join the specified room. You can specify a nickname " - "after a slash (/). If no nickname is specified, you will" - " use the default_nick in the configuration file. You can" - " omit the room name: you will then join the room you\'re" - " looking at (useful if you were kicked). You can also " - "provide a room_name without specifying a server, the " - "server of the room you're currently in will be used. You" - " can also provide a password to join the room.\nExamples" - ":\n/join room@server.tld\n/join room@server.tld/John\n" - "/join room2\n/join /me_again\n/join\n/join room@server" - ".tld/my_nick password\n/join / password", - shortdesc='Join a room', - completion=self.completion.join) - self.register_command('exit', self.command.quit, - desc='Just disconnect from the server and exit poezio.', - shortdesc='Exit poezio.') - self.register_command('quit', self.command.quit, - desc='Just disconnect from the server and exit poezio.', - shortdesc='Exit poezio.') - self.register_command('next', self.rotate_rooms_right, - shortdesc='Go to the next room.') - self.register_command('prev', self.rotate_rooms_left, - shortdesc='Go to the previous room.') - self.register_command('win', self.command.win, - usage='<number or name>', - shortdesc='Go to the specified room', - completion=self.completion.win) + self.register_command( + 'help', + self.command.help, + usage='[command]', + shortdesc='\\_o< KOIN KOIN KOIN', + completion=self.completion.help) + self.register_command( + 'join', + self.command.join, + usage="[room_name][@server][/nick] [password]", + desc="Join the specified room. You can specify a nickname " + "after a slash (/). If no nickname is specified, you will" + " use the default_nick in the configuration file. You can" + " omit the room name: you will then join the room you\'re" + " looking at (useful if you were kicked). You can also " + "provide a room_name without specifying a server, the " + "server of the room you're currently in will be used. You" + " can also provide a password to join the room.\nExamples" + ":\n/join room@server.tld\n/join room@server.tld/John\n" + "/join room2\n/join /me_again\n/join\n/join room@server" + ".tld/my_nick password\n/join / password", + shortdesc='Join a room', + completion=self.completion.join) + self.register_command( + 'exit', + self.command.quit, + desc='Just disconnect from the server and exit poezio.', + shortdesc='Exit poezio.') + self.register_command( + 'quit', + self.command.quit, + desc='Just disconnect from the server and exit poezio.', + shortdesc='Exit poezio.') + self.register_command( + 'next', self.rotate_rooms_right, shortdesc='Go to the next room.') + self.register_command( + 'prev', + self.rotate_rooms_left, + shortdesc='Go to the previous room.') + self.register_command( + 'win', + self.command.win, + usage='<number or name>', + shortdesc='Go to the specified room', + completion=self.completion.win) self.commands['w'] = self.commands['win'] - self.register_command('move_tab', self.command.move_tab, - usage='<source> <destination>', - desc="Insert the <source> tab at the position of " - "<destination>. This will make the following tabs shift in" - " some cases (refer to the documentation). A tab can be " - "designated by its number or by the beginning of its " - "address. You can use \".\" as a shortcut for the current " - "tab.", - shortdesc='Move a tab.', - completion=self.completion.move_tab) - self.register_command('destroy_room', self.command.destroy_room, - usage='[room JID]', - desc='Try to destroy the room [room JID], or the current' - ' tab if it is a multi-user chat and [room JID] is ' - 'not given.', - shortdesc='Destroy a room.', - completion=None) - self.register_command('show', self.command.status, - usage='<availability> [status message]', - desc="Sets your availability and (optionally) your status " - "message. The <availability> argument is one of \"available" - ", chat, away, afk, dnd, busy, xa\" and the optional " - "[status message] argument will be your status message.", - shortdesc='Change your availability.', - completion=self.completion.status) + self.register_command( + 'move_tab', + self.command.move_tab, + usage='<source> <destination>', + desc="Insert the <source> tab at the position of " + "<destination>. This will make the following tabs shift in" + " some cases (refer to the documentation). A tab can be " + "designated by its number or by the beginning of its " + "address. You can use \".\" as a shortcut for the current " + "tab.", + shortdesc='Move a tab.', + completion=self.completion.move_tab) + self.register_command( + 'destroy_room', + self.command.destroy_room, + usage='[room JID]', + desc='Try to destroy the room [room JID], or the current' + ' tab if it is a multi-user chat and [room JID] is ' + 'not given.', + shortdesc='Destroy a room.', + completion=None) + self.register_command( + 'show', + self.command.status, + usage='<availability> [status message]', + desc="Sets your availability and (optionally) your status " + "message. The <availability> argument is one of \"available" + ", chat, away, afk, dnd, busy, xa\" and the optional " + "[status message] argument will be your status message.", + shortdesc='Change your availability.', + completion=self.completion.status) self.commands['status'] = self.commands['show'] - self.register_command('bookmark_local', self.command.bookmark_local, - usage="[roomname][/nick] [password]", - desc="Bookmark Local: Bookmark locally the specified room " - "(you will then auto-join it on each poezio start). This" - " commands uses almost the same syntaxe as /join. Type " - "/help join for syntax examples. Note that when typing " - "\"/bookmark\" on its own, the room will be bookmarked " - "with the nickname you\'re currently using in this room " - "(instead of default_nick)", - shortdesc='Bookmark a room locally.', - completion=self.completion.bookmark_local) - self.register_command('bookmark', self.command.bookmark, - usage="[roomname][/nick] [autojoin] [password]", - desc="Bookmark: Bookmark online the specified room (you " - "will then auto-join it on each poezio start if autojoin" - " is specified and is 'true'). This commands uses almost" - " the same syntax as /join. Type /help join for syntax " - "examples. Note that when typing \"/bookmark\" alone, the" - " room will be bookmarked with the nickname you\'re " - "currently using in this room (instead of default_nick).", - shortdesc="Bookmark a room online.", - completion=self.completion.bookmark) - self.register_command('set', self.command.set, - usage="[plugin|][section] <option> [value]", - desc="Set the value of an option in your configuration file." - " You can, for example, change your default nickname by " - "doing `/set default_nick toto` or your resource with `/set" - " resource blabla`. You can also set options in specific " - "sections with `/set bindings M-i ^i` or in specific plugin" - " with `/set mpd_client| host 127.0.0.1`. `toggle` can be " - "used as a special value to toggle a boolean option.", - shortdesc="Set the value of an option", - completion=self.completion.set) - self.register_command('set_default', self.command.set_default, - usage="[section] <option>", - desc="Set the default value of an option. For example, " - "`/set_default resource` will reset the resource " - "option. You can also reset options in specific " - "sections by doing `/set_default section option`.", - shortdesc="Set the default value of an option", - completion=self.completion.set_default) - self.register_command('toggle', self.command.toggle, - usage='<option>', - desc='Shortcut for /set <option> toggle', - shortdesc='Toggle an option', - completion=self.completion.toggle) - self.register_command('theme', self.command.theme, - usage='[theme name]', - desc="Reload the theme defined in the config file. If theme" - "_name is provided, set that theme before reloading it.", - shortdesc='Load a theme', - completion=self.completion.theme) - self.register_command('list', self.command.list, - usage='[server]', - desc="Get the list of public rooms" - " on the specified server.", - shortdesc='List the rooms.', - completion=self.completion.list) - self.register_command('message', self.command.message, - usage='<jid> [optional message]', - desc="Open a conversation with the specified JID (even if it" - " is not in our roster), and send a message to it, if the " - "message is specified.", - shortdesc='Send a message', - completion=self.completion.message) - self.register_command('version', self.command.version, - usage='<jid>', - desc="Get the software version of the given JID (usually its" - " XMPP client and Operating System).", - shortdesc='Get the software version of a JID.', - completion=self.completion.version) - self.register_command('server_cycle', self.command.server_cycle, - usage='[domain] [message]', - desc='Disconnect and reconnect in all the rooms in domain.', - shortdesc='Cycle a range of rooms', - completion=self.completion.server_cycle) - self.register_command('bind', self.command.bind, - usage='<key> <equ>', - desc="Bind a key to another key or to a “command”. For " - "example \"/bind ^H KEY_UP\" makes Control + h do the" - " same same as the Up key.", - completion=self.completion.bind, - shortdesc='Bind a key to another key.') - self.register_command('load', self.command.load, - usage='<plugin> [<otherplugin> …]', - shortdesc='Load the specified plugin(s)', - completion=self.plugin_manager.completion_load) - self.register_command('unload', self.command.unload, - usage='<plugin> [<otherplugin> …]', - shortdesc='Unload the specified plugin(s)', - completion=self.plugin_manager.completion_unload) - self.register_command('plugins', self.command.plugins, - shortdesc='Show the plugins in use.') - self.register_command('presence', self.command.presence, - usage='<JID> [type] [status]', - desc="Send a directed presence to <JID> and using" - " [type] and [status] if provided.", - shortdesc='Send a directed presence.', - completion=self.completion.presence) - self.register_command('rawxml', self.command.rawxml, - usage='<xml>', - shortdesc='Send a custom xml stanza.') - self.register_command('invite', self.command.invite, - usage='<jid> <room> [reason]', - desc='Invite jid in room with reason.', - shortdesc='Invite someone in a room.', - completion=self.completion.invite) - self.register_command('invitations', self.command.invitations, - shortdesc='Show the pending invitations.') - self.register_command('bookmarks', self.command.bookmarks, - shortdesc='Show the current bookmarks.') - self.register_command('remove_bookmark', self.command.remove_bookmark, - usage='[jid]', - desc="Remove the specified bookmark, or the " - "bookmark on the current tab, if any.", - shortdesc='Remove a bookmark', - completion=self.completion.remove_bookmark) - self.register_command('xml_tab', self.command.xml_tab, - shortdesc='Open an XML tab.') - self.register_command('runkey', self.command.runkey, - usage='<key>', - shortdesc='Execute the action defined for <key>.', - completion=self.completion.runkey) - self.register_command('self', self.command.self_, - shortdesc='Remind you of who you are.') - self.register_command('last_activity', self.command.last_activity, - usage='<jid>', - desc='Informs you of the last activity of a JID.', - shortdesc='Get the activity of someone.', - completion=self.completion.last_activity) - self.register_command('ad-hoc', self.command.adhoc, - usage='<jid>', - shortdesc='List available ad-hoc commands on the given jid') - self.register_command('reload', self.command.reload, - shortdesc='Reload the config. You can achieve the same by ' - 'sending SIGUSR1 to poezio.') + self.register_command( + 'bookmark_local', + self.command.bookmark_local, + usage="[roomname][/nick] [password]", + desc="Bookmark Local: Bookmark locally the specified room " + "(you will then auto-join it on each poezio start). This" + " commands uses almost the same syntaxe as /join. Type " + "/help join for syntax examples. Note that when typing " + "\"/bookmark\" on its own, the room will be bookmarked " + "with the nickname you\'re currently using in this room " + "(instead of default_nick)", + shortdesc='Bookmark a room locally.', + completion=self.completion.bookmark_local) + self.register_command( + 'bookmark', + self.command.bookmark, + usage="[roomname][/nick] [autojoin] [password]", + desc="Bookmark: Bookmark online the specified room (you " + "will then auto-join it on each poezio start if autojoin" + " is specified and is 'true'). This commands uses almost" + " the same syntax as /join. Type /help join for syntax " + "examples. Note that when typing \"/bookmark\" alone, the" + " room will be bookmarked with the nickname you\'re " + "currently using in this room (instead of default_nick).", + shortdesc="Bookmark a room online.", + completion=self.completion.bookmark) + self.register_command( + 'set', + self.command.set, + usage="[plugin|][section] <option> [value]", + desc="Set the value of an option in your configuration file." + " You can, for example, change your default nickname by " + "doing `/set default_nick toto` or your resource with `/set" + " resource blabla`. You can also set options in specific " + "sections with `/set bindings M-i ^i` or in specific plugin" + " with `/set mpd_client| host 127.0.0.1`. `toggle` can be " + "used as a special value to toggle a boolean option.", + shortdesc="Set the value of an option", + completion=self.completion.set) + self.register_command( + 'set_default', + self.command.set_default, + usage="[section] <option>", + desc="Set the default value of an option. For example, " + "`/set_default resource` will reset the resource " + "option. You can also reset options in specific " + "sections by doing `/set_default section option`.", + shortdesc="Set the default value of an option", + completion=self.completion.set_default) + self.register_command( + 'toggle', + self.command.toggle, + usage='<option>', + desc='Shortcut for /set <option> toggle', + shortdesc='Toggle an option', + completion=self.completion.toggle) + self.register_command( + 'theme', + self.command.theme, + usage='[theme name]', + desc="Reload the theme defined in the config file. If theme" + "_name is provided, set that theme before reloading it.", + shortdesc='Load a theme', + completion=self.completion.theme) + self.register_command( + 'list', + self.command.list, + usage='[server]', + desc="Get the list of public rooms" + " on the specified server.", + shortdesc='List the rooms.', + completion=self.completion.list) + self.register_command( + 'message', + self.command.message, + usage='<jid> [optional message]', + desc="Open a conversation with the specified JID (even if it" + " is not in our roster), and send a message to it, if the " + "message is specified.", + shortdesc='Send a message', + completion=self.completion.message) + self.register_command( + 'version', + self.command.version, + usage='<jid>', + desc="Get the software version of the given JID (usually its" + " XMPP client and Operating System).", + shortdesc='Get the software version of a JID.', + completion=self.completion.version) + self.register_command( + 'server_cycle', + self.command.server_cycle, + usage='[domain] [message]', + desc='Disconnect and reconnect in all the rooms in domain.', + shortdesc='Cycle a range of rooms', + completion=self.completion.server_cycle) + self.register_command( + 'bind', + self.command.bind, + usage='<key> <equ>', + desc="Bind a key to another key or to a “command”. For " + "example \"/bind ^H KEY_UP\" makes Control + h do the" + " same same as the Up key.", + completion=self.completion.bind, + shortdesc='Bind a key to another key.') + self.register_command( + 'load', + self.command.load, + usage='<plugin> [<otherplugin> …]', + shortdesc='Load the specified plugin(s)', + completion=self.plugin_manager.completion_load) + self.register_command( + 'unload', + self.command.unload, + usage='<plugin> [<otherplugin> …]', + shortdesc='Unload the specified plugin(s)', + completion=self.plugin_manager.completion_unload) + self.register_command( + 'plugins', + self.command.plugins, + shortdesc='Show the plugins in use.') + self.register_command( + 'presence', + self.command.presence, + usage='<JID> [type] [status]', + desc="Send a directed presence to <JID> and using" + " [type] and [status] if provided.", + shortdesc='Send a directed presence.', + completion=self.completion.presence) + self.register_command( + 'rawxml', + self.command.rawxml, + usage='<xml>', + shortdesc='Send a custom xml stanza.') + self.register_command( + 'invite', + self.command.invite, + usage='<jid> <room> [reason]', + desc='Invite jid in room with reason.', + shortdesc='Invite someone in a room.', + completion=self.completion.invite) + self.register_command( + 'invitations', + self.command.invitations, + shortdesc='Show the pending invitations.') + self.register_command( + 'bookmarks', + self.command.bookmarks, + shortdesc='Show the current bookmarks.') + self.register_command( + 'remove_bookmark', + self.command.remove_bookmark, + usage='[jid]', + desc="Remove the specified bookmark, or the " + "bookmark on the current tab, if any.", + shortdesc='Remove a bookmark', + completion=self.completion.remove_bookmark) + self.register_command( + 'xml_tab', self.command.xml_tab, shortdesc='Open an XML tab.') + self.register_command( + 'runkey', + self.command.runkey, + usage='<key>', + shortdesc='Execute the action defined for <key>.', + completion=self.completion.runkey) + self.register_command( + 'self', self.command.self_, shortdesc='Remind you of who you are.') + self.register_command( + 'last_activity', + self.command.last_activity, + usage='<jid>', + desc='Informs you of the last activity of a JID.', + shortdesc='Get the activity of someone.', + completion=self.completion.last_activity) + self.register_command( + 'ad-hoc', + self.command.adhoc, + usage='<jid>', + shortdesc='List available ad-hoc commands on the given jid') + self.register_command( + 'reload', + self.command.reload, + shortdesc='Reload the config. You can achieve the same by ' + 'sending SIGUSR1 to poezio.') if config.get('enable_user_activity'): - self.register_command('activity', self.command.activity, - usage='[<general> [specific] [text]]', - desc='Send your current activity to your contacts ' - '(use the completion). Nothing means ' - '"stop broadcasting an activity".', - shortdesc='Send your activity.', - completion=self.completion.activity) + self.register_command( + 'activity', + self.command.activity, + usage='[<general> [specific] [text]]', + desc='Send your current activity to your contacts ' + '(use the completion). Nothing means ' + '"stop broadcasting an activity".', + shortdesc='Send your activity.', + completion=self.completion.activity) if config.get('enable_user_mood'): - self.register_command('mood', self.command.mood, - usage='[<mood> [text]]', - desc='Send your current mood to your contacts ' - '(use the completion). Nothing means ' - '"stop broadcasting a mood".', - shortdesc='Send your mood.', - completion=self.completion.mood) + self.register_command( + 'mood', + self.command.mood, + usage='[<mood> [text]]', + desc='Send your current mood to your contacts ' + '(use the completion). Nothing means ' + '"stop broadcasting a mood".', + shortdesc='Send your mood.', + completion=self.completion.mood) if config.get('enable_user_gaming'): - self.register_command('gaming', self.command.gaming, - usage='[<game name> [server address]]', - desc='Send your current gaming activity to ' - 'your contacts. Nothing means "stop ' - 'broadcasting a gaming activity".', - shortdesc='Send your gaming activity.', - completion=None) + self.register_command( + 'gaming', + self.command.gaming, + usage='[<game name> [server address]]', + desc='Send your current gaming activity to ' + 'your contacts. Nothing means "stop ' + 'broadcasting a gaming activity".', + shortdesc='Send your gaming activity.', + completion=None) + ####################### Random things to move ################################# @@ -1971,22 +2059,26 @@ class Core(object): tab = self.get_tab_by_name(bm.jid, tabs.MucTab) nick = bm.nick if bm.nick else self.own_nick if not tab: - self.open_new_room(bm.jid, nick, focus=False, - password=bm.password) + self.open_new_room( + bm.jid, nick, focus=False, password=bm.password) self.initial_joins.append(bm.jid) # do not join rooms that do not have autojoin # but display them anyway if bm.autojoin: - muc.join_groupchat(self, bm.jid, nick, - passwd=bm.password, - status=self.status.message, - show=self.status.show) + muc.join_groupchat( + self, + bm.jid, + nick, + passwd=bm.password, + status=self.status.message, + show=self.status.show) def check_bookmark_storage(self, features): private = 'jabber:iq:private' in features pep_ = 'http://jabber.org/protocol/pubsub#publish' in features self.bookmarks.available_storage['private'] = private self.bookmarks.available_storage['pep'] = pep_ + def _join_remote_only(iq): if iq['type'] == 'error': type_ = iq['error']['type'] @@ -1998,8 +2090,10 @@ class Core(object): return remote_bookmarks = self.bookmarks.remote() self.join_initial_rooms(remote_bookmarks) + if not self.xmpp.anon and config.get('use_remote_bookmarks'): - self.bookmarks.get_remote(self.xmpp, self.information, _join_remote_only) + self.bookmarks.get_remote(self.xmpp, self.information, + _join_remote_only) def room_error(self, error, room_name): """ @@ -2009,32 +2103,42 @@ class Core(object): if not tab: return error_message = self.get_error_message(error) - tab.add_message(error_message, highlight=True, nickname='Error', - nick_color=get_theme().COLOR_ERROR_MSG, typ=2) + tab.add_message( + error_message, + highlight=True, + nickname='Error', + nick_color=get_theme().COLOR_ERROR_MSG, + typ=2) code = error['error']['code'] if code == '401': msg = 'To provide a password in order to join the room, type "/join / password" (replace "password" by the real password)' tab.add_message(msg, typ=2) if code == '409': if config.get('alternative_nickname') != '': - self.command.join('%s/%s'% (tab.name, tab.own_nick+config.get('alternative_nickname'))) + self.command.join( + '%s/%s' % + (tab.name, + tab.own_nick + config.get('alternative_nickname'))) else: if not tab.joined: - tab.add_message('You can join the room with an other nick, by typing "/join /other_nick"', typ=2) + tab.add_message( + 'You can join the room with an other nick, by typing "/join /other_nick"', + typ=2) self.refresh_window() - class KeyDict(dict): """ A dict, with a wrapper for get() that will return a custom value if the key starts with _exc_ """ + def get(self, k, d=None): if isinstance(k, str) and k.startswith('_exc_') and len(k) > 5: return lambda: dict.get(self, '_exc_')(k[5:]) return dict.get(self, k, d) + def replace_key_with_bound(key): """ Replace an inputted key with the one defined as its replacement @@ -2044,5 +2148,3 @@ def replace_key_with_bound(key): if not bind: bind = key return bind - - diff --git a/poezio/core/handlers.py b/poezio/core/handlers.py index e6d54558..ac6ae573 100644 --- a/poezio/core/handlers.py +++ b/poezio/core/handlers.py @@ -80,6 +80,7 @@ class HandlerCore: """ Enable carbons & blocking on session start if wanted and possible """ + def callback(iq): if not iq: return @@ -87,21 +88,23 @@ class HandlerCore: rostertab = self.core.get_tab_by_name('Roster', tabs.RosterInfoTab) rostertab.check_blocking(features) rostertab.check_saslexternal(features) - if (config.get('enable_carbons') and - 'urn:xmpp:carbons:2' in features): + if (config.get('enable_carbons') + and 'urn:xmpp:carbons:2' in features): self.core.xmpp.plugin['xep_0280'].enable() self.core.check_bookmark_storage(features) - self.core.xmpp.plugin['xep_0030'].get_info(jid=self.core.xmpp.boundjid.domain, - callback=callback) + self.core.xmpp.plugin['xep_0030'].get_info( + jid=self.core.xmpp.boundjid.domain, callback=callback) def on_carbon_received(self, message): """ Carbon <received/> received """ + def ignore_message(recv): log.debug('%s has category conference, ignoring carbon', recv['from'].server) + def receive_message(recv): recv['to'] = self.core.xmpp.boundjid.full if recv['receipt']: @@ -109,12 +112,14 @@ class HandlerCore: self.on_normal_message(recv) recv = message['carbon_received'] - if (recv['from'].bare not in roster or - roster[recv['from'].bare].subscription == 'none'): - fixes.has_identity(self.core.xmpp, recv['from'].server, - identity='conference', - on_true=functools.partial(ignore_message, recv), - on_false=functools.partial(receive_message, recv)) + if (recv['from'].bare not in roster + or roster[recv['from'].bare].subscription == 'none'): + fixes.has_identity( + self.core.xmpp, + recv['from'].server, + identity='conference', + on_true=functools.partial(ignore_message, recv), + on_false=functools.partial(receive_message, recv)) return else: receive_message(recv) @@ -123,20 +128,24 @@ class HandlerCore: """ Carbon <sent/> received """ + def ignore_message(sent): log.debug('%s has category conference, ignoring carbon', sent['to'].server) + def send_message(sent): sent['from'] = self.core.xmpp.boundjid.full self.on_normal_message(sent) sent = message['carbon_sent'] - if (sent['to'].bare not in roster or - roster[sent['to'].bare].subscription == 'none'): - fixes.has_identity(self.core.xmpp, sent['to'].server, - identity='conference', - on_true=functools.partial(ignore_message, sent), - on_false=functools.partial(send_message, sent)) + if (sent['to'].bare not in roster + or roster[sent['to'].bare].subscription == 'none'): + fixes.has_identity( + self.core.xmpp, + sent['to'].server, + identity='conference', + on_true=functools.partial(ignore_message, sent), + on_false=functools.partial(send_message, sent)) else: send_message(sent) @@ -150,7 +159,11 @@ class HandlerCore: if jid.bare in self.core.pending_invites: return # there are 2 'x' tags in the messages, making message['x'] useless - invite = StanzaBase(self.core.xmpp, xml=message.xml.find('{http://jabber.org/protocol/muc#user}x/{http://jabber.org/protocol/muc#user}invite')) + invite = StanzaBase( + self.core.xmpp, + xml=message.xml.find( + '{http://jabber.org/protocol/muc#user}x/{http://jabber.org/protocol/muc#user}invite' + )) inviter = invite['from'] reason = invite['reason'] password = invite['password'] @@ -204,7 +217,9 @@ class HandlerCore: When receiving private message from a muc OR a normal message (from one of our contacts) """ - if message.xml.find('{http://jabber.org/protocol/muc#user}x/{http://jabber.org/protocol/muc#user}invite') is not None: + if message.xml.find( + '{http://jabber.org/protocol/muc#user}x/{http://jabber.org/protocol/muc#user}invite' + ) is not None: return if message['type'] == 'groupchat': return @@ -228,7 +243,8 @@ class HandlerCore: self.core.room_error(message, jid_from.bare) else: text = self.core.get_error_message(message) - p_tab = self.core.get_tab_by_name(jid_from.full, tabs.PrivateTab) + p_tab = self.core.get_tab_by_name(jid_from.full, + tabs.PrivateTab) if p_tab: p_tab.add_error(text) else: @@ -240,12 +256,11 @@ class HandlerCore: self.core.information(error_msg, 'Error') return error = '\x19%s}%s\x19o' % (dump_tuple(get_theme().COLOR_CHAR_NACK), - error_msg) + error_msg) if not tab.nack_message('\n' + error, message['id'], message['to']): tab.add_message(error, typ=0) self.core.refresh_window() - def on_normal_message(self, message): """ When receiving "normal" messages (not a private message from a @@ -254,14 +269,18 @@ class HandlerCore: if message['type'] == 'error': return elif message['type'] == 'headline' and message['body']: - return self.core.information('%s says: %s' % (message['from'], message['body']), 'Headline') + return self.core.information( + '%s says: %s' % (message['from'], message['body']), 'Headline') - use_xhtml = config.get_by_tabname('enable_xhtml_im', message['from'].bare) + use_xhtml = config.get_by_tabname('enable_xhtml_im', + message['from'].bare) tmp_dir = config.get('tmp_image_dir') or path.join(CACHE_DIR, 'images') extract_images = config.get('extract_inline_images') - body = xhtml.get_body_from_message_stanza(message, use_xhtml=use_xhtml, - tmp_dir=tmp_dir, - extract_images=extract_images) + body = xhtml.get_body_from_message_stanza( + message, + use_xhtml=use_xhtml, + tmp_dir=tmp_dir, + extract_images=extract_images) if not body: if not self.core.xmpp.plugin['xep_0380'].has_eme(message): return @@ -279,7 +298,8 @@ class HandlerCore: remote_nick = roster[conv_jid.bare].name # check for a received nick if not remote_nick and config.get('enable_user_nick'): - if message.xml.find('{http://jabber.org/protocol/nick}nick') is not None: + if message.xml.find( + '{http://jabber.org/protocol/nick}nick') is not None: remote_nick = message['nick']['nick'] if not remote_nick: remote_nick = conv_jid.user @@ -298,7 +318,8 @@ class HandlerCore: return conversation = self.core.get_conversation_by_jid(conv_jid, create=True) - if isinstance(conversation, tabs.DynamicConversationTab) and conv_jid.resource: + if isinstance(conversation, + tabs.DynamicConversationTab) and conv_jid.resource: conversation.lock(conv_jid.resource) if not own and not conversation.nick: @@ -312,9 +333,11 @@ class HandlerCore: self.core.events.trigger('conversation_msg', message, conversation) if not message['body']: return - body = xhtml.get_body_from_message_stanza(message, use_xhtml=use_xhtml, - tmp_dir=tmp_dir, - extract_images=extract_images) + body = xhtml.get_body_from_message_stanza( + message, + use_xhtml=use_xhtml, + tmp_dir=tmp_dir, + extract_images=extract_images) delayed, date = common.find_delayed_tag(message) def try_modify(): @@ -324,21 +347,27 @@ class HandlerCore: if replaced_id and config.get_by_tabname('group_corrections', conv_jid.bare): try: - conversation.modify_message(body, replaced_id, message['id'], jid=jid, - nickname=remote_nick) + conversation.modify_message( + body, + replaced_id, + message['id'], + jid=jid, + nickname=remote_nick) return True except CorrectionError: log.debug('Unable to correct a message', exc_info=True) return False if not try_modify(): - conversation.add_message(body, date, - nickname=remote_nick, - nick_color=color, - history=delayed, - identifier=message['id'], - jid=jid, - typ=1) + conversation.add_message( + body, + date, + nickname=remote_nick, + nick_color=color, + history=delayed, + identifier=message['id'], + jid=jid, + typ=1) if conversation.remote_wants_chatstates is None and not delayed: if message['chat_state']: @@ -366,7 +395,8 @@ class HandlerCore: return log.debug('Received 0084 avatar update from %s', jid) try: - metadata = msg['pubsub_event']['items']['item']['avatar_metadata']['items'] + metadata = msg['pubsub_event']['items']['item']['avatar_metadata'][ + 'items'] except Exception: log.debug('Failed getting metadata from 0084:', exc_info=True) return @@ -387,12 +417,16 @@ class HandlerCore: # If we didn’t have any, query the data instead. if not info['url']: try: - result = yield from self.core.xmpp['xep_0084'].retrieve_avatar(jid, - avatar_hash, - timeout=60) - contact.avatar = result['pubsub']['items']['item']['avatar_data']['value'] + result = yield from self.core.xmpp[ + 'xep_0084'].retrieve_avatar( + jid, avatar_hash, timeout=60) + contact.avatar = result['pubsub']['items']['item'][ + 'avatar_data']['value'] except Exception: - log.debug('Failed retrieving 0084 data from %s:', jid, exc_info=True) + log.debug( + 'Failed retrieving 0084 data from %s:', + jid, + exc_info=True) continue log.debug('Received %s avatar: %s', jid, info['type']) @@ -427,9 +461,8 @@ class HandlerCore: # If we didn’t have any, query the vCard instead. try: - result = yield from self.core.xmpp['xep_0054'].get_vcard(jid, - cached=True, - timeout=60) + result = yield from self.core.xmpp['xep_0054'].get_vcard( + jid, cached=True, timeout=60) avatar = result['vcard_temp']['PHOTO'] contact.avatar = avatar['BINVAL'] except Exception: @@ -473,25 +506,32 @@ class HandlerCore: item = item['gaming'] # only name and server_address are used for now contact.gaming = { - 'character_name': item['character_name'], - 'character_profile': item['character_profile'], - 'name': item['name'], - 'level': item['level'], - 'uri': item['uri'], - 'server_name': item['server_name'], - 'server_address': item['server_address'], - } + 'character_name': item['character_name'], + 'character_profile': item['character_profile'], + 'name': item['name'], + 'level': item['level'], + 'uri': item['uri'], + 'server_name': item['server_name'], + 'server_address': item['server_address'], + } else: contact.gaming = {} if contact.gaming: - logger.log_roster_change(contact.bare_jid, 'is playing %s' % (common.format_gaming_string(contact.gaming))) + logger.log_roster_change( + contact.bare_jid, 'is playing %s' % + (common.format_gaming_string(contact.gaming))) - if old_gaming != contact.gaming and config.get_by_tabname('display_gaming_notifications', contact.bare_jid): + if old_gaming != contact.gaming and config.get_by_tabname( + 'display_gaming_notifications', contact.bare_jid): if contact.gaming: - self.core.information('%s is playing %s' % (contact.bare_jid, common.format_gaming_string(contact.gaming)), 'Gaming') + self.core.information( + '%s is playing %s' % + (contact.bare_jid, + common.format_gaming_string(contact.gaming)), 'Gaming') else: - self.core.information(contact.bare_jid + ' stopped playing.', 'Gaming') + self.core.information(contact.bare_jid + ' stopped playing.', + 'Gaming') def on_mood_event(self, message): """ @@ -518,13 +558,18 @@ class HandlerCore: contact.mood = '' if contact.mood: - logger.log_roster_change(contact.bare_jid, 'has now the mood: %s' % contact.mood) + logger.log_roster_change(contact.bare_jid, + 'has now the mood: %s' % contact.mood) - if old_mood != contact.mood and config.get_by_tabname('display_mood_notifications', contact.bare_jid): + if old_mood != contact.mood and config.get_by_tabname( + 'display_mood_notifications', contact.bare_jid): if contact.mood: - self.core.information('Mood from '+ contact.bare_jid + ': ' + contact.mood, 'Mood') + self.core.information( + 'Mood from ' + contact.bare_jid + ': ' + contact.mood, + 'Mood') else: - self.core.information(contact.bare_jid + ' stopped having his/her mood.', 'Mood') + self.core.information( + contact.bare_jid + ' stopped having his/her mood.', 'Mood') def on_activity_event(self, message): """ @@ -537,7 +582,8 @@ class HandlerCore: roster.modified() item = message['pubsub_event']['items']['item'] old_activity = contact.activity - if item.xml.find('{http://jabber.org/protocol/activity}activity') is not None: + if item.xml.find( + '{http://jabber.org/protocol/activity}activity') is not None: try: activity = item['activity']['value'] except ValueError: @@ -557,13 +603,18 @@ class HandlerCore: contact.activity = '' if contact.activity: - logger.log_roster_change(contact.bare_jid, 'has now the activity %s' % contact.activity) + logger.log_roster_change( + contact.bare_jid, 'has now the activity %s' % contact.activity) - if old_activity != contact.activity and config.get_by_tabname('display_activity_notifications', contact.bare_jid): + if old_activity != contact.activity and config.get_by_tabname( + 'display_activity_notifications', contact.bare_jid): if contact.activity: - self.core.information('Activity from '+ contact.bare_jid + ': ' + contact.activity, 'Activity') + self.core.information('Activity from ' + contact.bare_jid + + ': ' + contact.activity, 'Activity') else: - self.core.information(contact.bare_jid + ' stopped doing his/her activity.', 'Activity') + self.core.information( + contact.bare_jid + ' stopped doing his/her activity.', + 'Activity') def on_tune_event(self, message): """ @@ -579,27 +630,31 @@ class HandlerCore: if item.xml.find('{http://jabber.org/protocol/tune}tune') is not None: item = item['tune'] contact.tune = { - 'artist': item['artist'], - 'length': item['length'], - 'rating': item['rating'], - 'source': item['source'], - 'title': item['title'], - 'track': item['track'], - 'uri': item['uri'] - } + 'artist': item['artist'], + 'length': item['length'], + 'rating': item['rating'], + 'source': item['source'], + 'title': item['title'], + 'track': item['track'], + 'uri': item['uri'] + } else: contact.tune = {} if contact.tune: - logger.log_roster_change(message['from'].bare, 'is now listening to %s' % common.format_tune_string(contact.tune)) + logger.log_roster_change(message['from'].bare, + 'is now listening to %s' % + common.format_tune_string(contact.tune)) - if old_tune != contact.tune and config.get_by_tabname('display_tune_notifications', contact.bare_jid): + if old_tune != contact.tune and config.get_by_tabname( + 'display_tune_notifications', contact.bare_jid): if contact.tune: self.core.information( - 'Tune from '+ message['from'].bare + ': ' + common.format_tune_string(contact.tune), - 'Tune') + 'Tune from ' + message['from'].bare + ': ' + + common.format_tune_string(contact.tune), 'Tune') else: - self.core.information(contact.bare_jid + ' stopped listening to music.', 'Tune') + self.core.information( + contact.bare_jid + ' stopped listening to music.', 'Tune') def on_groupchat_message(self, message): """ @@ -609,14 +664,16 @@ class HandlerCore: return room_from = message['from'].bare - if message['type'] == 'error': # Check if it's an error + if message['type'] == 'error': # Check if it's an error self.core.room_error(message, room_from) return tab = self.core.get_tab_by_name(room_from, tabs.MucTab) if not tab: - self.core.information("message received for a non-existing room: %s" % (room_from)) - muc.leave_groupchat(self.core.xmpp, room_from, self.core.own_nick, msg='') + self.core.information( + "message received for a non-existing room: %s" % (room_from)) + muc.leave_groupchat( + self.core.xmpp, room_from, self.core.own_nick, msg='') return nick_from = message['mucnick'] @@ -628,9 +685,11 @@ class HandlerCore: use_xhtml = config.get_by_tabname('enable_xhtml_im', room_from) tmp_dir = config.get('tmp_image_dir') or path.join(CACHE_DIR, 'images') extract_images = config.get('extract_inline_images') - body = xhtml.get_body_from_message_stanza(message, use_xhtml=use_xhtml, - tmp_dir=tmp_dir, - extract_images=extract_images) + body = xhtml.get_body_from_message_stanza( + message, + use_xhtml=use_xhtml, + tmp_dir=tmp_dir, + extract_images=extract_images) if not body: return @@ -639,18 +698,29 @@ class HandlerCore: replaced = False if message.xml.find('{urn:xmpp:message-correct:0}replace') is not None: replaced_id = message['replace']['id'] - if replaced_id is not '' and config.get_by_tabname('group_corrections', - message['from'].bare): + if replaced_id is not '' and config.get_by_tabname( + 'group_corrections', message['from'].bare): try: delayed_date = date or datetime.now() - if tab.modify_message(body, replaced_id, message['id'], + if tab.modify_message( + body, + replaced_id, + message['id'], time=delayed_date, - nickname=nick_from, user=user): + nickname=nick_from, + user=user): self.core.events.trigger('highlight', message, tab) replaced = True except CorrectionError: log.debug('Unable to correct a message', exc_info=True) - if not replaced and tab.add_message(body, date, nick_from, history=delayed, identifier=message['id'], jid=message['from'], typ=1): + if not replaced and tab.add_message( + body, + date, + nick_from, + history=delayed, + identifier=message['id'], + jid=message['from'], + typ=1): self.core.events.trigger('highlight', message, tab) if message['from'].resource == tab.own_nick: @@ -693,45 +763,61 @@ class HandlerCore: use_xhtml = config.get_by_tabname('enable_xhtml_im', jid.bare) tmp_dir = config.get('tmp_image_dir') or path.join(CACHE_DIR, 'images') extract_images = config.get('extract_inline_images') - body = xhtml.get_body_from_message_stanza(message, use_xhtml=use_xhtml, - tmp_dir=tmp_dir, - extract_images=extract_images) - tab = self.core.get_tab_by_name(jid.full, tabs.PrivateTab) # get the tab with the private conversation + body = xhtml.get_body_from_message_stanza( + message, + use_xhtml=use_xhtml, + tmp_dir=tmp_dir, + extract_images=extract_images) + tab = self.core.get_tab_by_name( + jid.full, + tabs.PrivateTab) # get the tab with the private conversation ignore = config.get_by_tabname('ignore_private', room_from) - if not tab: # It's the first message we receive: create the tab + if not tab: # It's the first message we receive: create the tab if body and not ignore: - tab = self.core.open_private_window(room_from, nick_from, False) + tab = self.core.open_private_window(room_from, nick_from, + False) if ignore: self.core.events.trigger('ignored_private', message, tab) msg = config.get_by_tabname('private_auto_response', room_from) if msg and body: - self.core.xmpp.send_message(mto=jid.full, mbody=msg, mtype='chat') + self.core.xmpp.send_message( + mto=jid.full, mbody=msg, mtype='chat') return tab.last_remote_message = datetime.now() self.core.events.trigger('private_msg', message, tab) - body = xhtml.get_body_from_message_stanza(message, use_xhtml=use_xhtml, - tmp_dir=tmp_dir, - extract_images=extract_images) + body = xhtml.get_body_from_message_stanza( + message, + use_xhtml=use_xhtml, + tmp_dir=tmp_dir, + extract_images=extract_images) if not body or not tab: return replaced = False user = tab.parent_muc.get_user_by_name(nick_from) if message.xml.find('{urn:xmpp:message-correct:0}replace') is not None: replaced_id = message['replace']['id'] - if replaced_id is not '' and config.get_by_tabname('group_corrections', - room_from): + if replaced_id is not '' and config.get_by_tabname( + 'group_corrections', room_from): try: - tab.modify_message(body, replaced_id, message['id'], user=user, jid=message['from'], - nickname=nick_from) + tab.modify_message( + body, + replaced_id, + message['id'], + user=user, + jid=message['from'], + nickname=nick_from) replaced = True except CorrectionError: log.debug('Unable to correct a message', exc_info=True) if not replaced: - tab.add_message(body, time=None, nickname=nick_from, - forced_user=user, - identifier=message['id'], - jid=message['from'], - typ=1) + tab.add_message( + body, + time=None, + nickname=nick_from, + forced_user=user, + identifier=message['id'], + jid=message['from'], + typ=1) if tab.remote_wants_chatstates is None: if message['chat_state']: @@ -767,7 +853,8 @@ class HandlerCore: def _on_chatstate(self, message, state): if message['type'] == 'chat': if not self._on_chatstate_normal_conversation(message, state): - tab = self.core.get_tab_by_name(message['from'].full, tabs.PrivateTab) + tab = self.core.get_tab_by_name(message['from'].full, + tabs.PrivateTab) if not tab: return self._on_chatstate_private_conversation(message, state) @@ -862,10 +949,10 @@ class HandlerCore: contact = roster.get_and_set(jid) roster.update_contact_groups(contact) contact.pending_in = True - self.core.information('%s wants to subscribe to your presence, use ' - '/accept <jid> or /deny <jid> in the roster ' - 'tab to accept or reject the query.' % jid, - 'Roster') + self.core.information( + '%s wants to subscribe to your presence, use ' + '/accept <jid> or /deny <jid> in the roster ' + 'tab to accept or reject the query.' % jid, 'Roster') self.core.get_tab_by_number(0).state = 'highlight' roster.modified() if isinstance(self.core.current_tab(), tabs.RosterInfoTab): @@ -876,7 +963,8 @@ class HandlerCore: jid = presence['from'].bare contact = roster[jid] if contact.subscription not in ('both', 'from'): - self.core.information('%s accepted your contact proposal' % jid, 'Roster') + self.core.information('%s accepted your contact proposal' % jid, + 'Roster') if contact.pending_out: contact.pending_out = False @@ -892,7 +980,8 @@ class HandlerCore: if not contact: return roster.modified() - self.core.information('%s does not want to receive your status anymore.' % jid, 'Roster') + self.core.information( + '%s does not want to receive your status anymore.' % jid, 'Roster') self.core.get_tab_by_number(0).state = 'highlight' if isinstance(self.core.current_tab(), tabs.RosterInfoTab): self.core.refresh_window() @@ -905,10 +994,13 @@ class HandlerCore: return roster.modified() if contact.pending_out: - self.core.information('%s rejected your contact proposal' % jid, 'Roster') + self.core.information('%s rejected your contact proposal' % jid, + 'Roster') contact.pending_out = False else: - self.core.information('%s does not want you to receive his/her/its status anymore.'%jid, 'Roster') + self.core.information( + '%s does not want you to receive his/her/its status anymore.' % + jid, 'Roster') self.core.get_tab_by_number(0).state = 'highlight' if isinstance(self.core.current_tab(), tabs.RosterInfoTab): self.core.refresh_window() @@ -916,7 +1008,8 @@ class HandlerCore: ### Presence-related handlers ### def on_presence(self, presence): - if presence.match('presence/muc') or presence.xml.find('{http://jabber.org/protocol/muc#user}x') is not None: + if presence.match('presence/muc') or presence.xml.find( + '{http://jabber.org/protocol/muc#user}x') is not None: return jid = presence['from'] contact = roster[jid.bare] @@ -930,10 +1023,12 @@ class HandlerCore: return roster.modified() contact.error = None - self.core.events.trigger('normal_presence', presence, contact[jid.full]) + self.core.events.trigger('normal_presence', presence, + contact[jid.full]) tab = self.core.get_conversation_by_jid(jid, create=False) if tab: - tab.update_status(Status(show=presence['show'], message=presence['status'])) + tab.update_status( + Status(show=presence['show'], message=presence['status'])) if isinstance(self.core.current_tab(), tabs.RosterInfoTab): self.core.refresh_window() elif self.core.current_tab() == tab: @@ -956,7 +1051,8 @@ class HandlerCore: """ A JID got offline """ - if presence.match('presence/muc') or presence.xml.find('{http://jabber.org/protocol/muc#user}x') is not None: + if presence.match('presence/muc') or presence.xml.find( + '{http://jabber.org/protocol/muc#user}x') is not None: return jid = presence['from'] if not logger.log_roster_change(jid.bare, 'got offline'): @@ -970,9 +1066,12 @@ class HandlerCore: if contact.name: name = contact.name if jid.resource: - self.core.add_information_message_to_conversation_tab(jid.full, '\x195}%s is \x191}offline' % name) - self.core.add_information_message_to_conversation_tab(jid.bare, '\x195}%s is \x191}offline' % name) - self.core.information('\x193}%s \x195}is \x191}offline' % name, 'Roster') + self.core.add_information_message_to_conversation_tab( + jid.full, '\x195}%s is \x191}offline' % name) + self.core.add_information_message_to_conversation_tab( + jid.bare, '\x195}%s is \x191}offline' % name) + self.core.information('\x193}%s \x195}is \x191}offline' % name, + 'Roster') roster.modified() if isinstance(self.core.current_tab(), tabs.RosterInfoTab): self.core.refresh_window() @@ -981,7 +1080,8 @@ class HandlerCore: """ A JID got online """ - if presence.match('presence/muc') or presence.xml.find('{http://jabber.org/protocol/muc#user}x') is not None: + if presence.match('presence/muc') or presence.xml.find( + '{http://jabber.org/protocol/muc#user}x') is not None: return jid = presence['from'] contact = roster[jid.bare] @@ -996,17 +1096,22 @@ class HandlerCore: 'priority': presence.get_priority() or 0, 'status': presence['status'], 'show': presence['show'], - }) + }) self.core.events.trigger('normal_presence', presence, resource) name = contact.name if contact.name else jid.bare - self.core.add_information_message_to_conversation_tab(jid.full, '\x195}%s is \x194}online' % name) + self.core.add_information_message_to_conversation_tab( + jid.full, '\x195}%s is \x194}online' % name) if time.time() - self.core.connection_time > 10: # We do not display messages if we recently logged in if presence['status']: - self.core.information("\x193}%s \x195}is \x194}online\x195} (\x19o%s\x195})" % (name, presence['status']), "Roster") + self.core.information( + "\x193}%s \x195}is \x194}online\x195} (\x19o%s\x195})" % + (name, presence['status']), "Roster") else: - self.core.information("\x193}%s \x195}is \x194}online\x195}" % name, "Roster") - self.core.add_information_message_to_conversation_tab(jid.bare, '\x195}%s is \x194}online' % name) + self.core.information( + "\x193}%s \x195}is \x194}online\x195}" % name, "Roster") + self.core.add_information_message_to_conversation_tab( + jid.bare, '\x195}%s is \x194}online' % name) if isinstance(self.core.current_tab(), tabs.RosterInfoTab): self.core.refresh_window() @@ -1022,14 +1127,14 @@ class HandlerCore: self.core.events.trigger('muc_presence', presence, tab) tab.handle_presence(presence) - ### Connection-related handlers ### def on_failed_connection(self, error): """ We cannot contact the remote server """ - self.core.information("Connection to remote server failed: %s" % (error,), 'Error') + self.core.information("Connection to remote server failed: %s" % + (error, ), 'Error') @asyncio.coroutine def on_disconnected(self, event): @@ -1046,10 +1151,12 @@ class HandlerCore: tab.disconnect() msg_typ = 'Error' if not self.core.legitimate_disconnect else 'Info' self.core.information("Disconnected from server.", msg_typ) - if self.core.legitimate_disconnect or not config.get('auto_reconnect', True): + if self.core.legitimate_disconnect or not config.get( + 'auto_reconnect', True): return - if (self.core.last_stream_error and - self.core.last_stream_error[1]['condition'] in ('conflict', 'host-unknown')): + if (self.core.last_stream_error + and self.core.last_stream_error[1]['condition'] in ( + 'conflict', 'host-unknown')): return yield from asyncio.sleep(1) self.core.information("Auto-reconnecting.", 'Info') @@ -1076,8 +1183,8 @@ class HandlerCore: """ Authentication failed (no mech) """ - self.core.information("Authentication failed, no login method available.", - 'Error') + self.core.information( + "Authentication failed, no login method available.", 'Error') self.core.legitimate_disconnect = True def on_connected(self, event): @@ -1092,10 +1199,11 @@ class HandlerCore: Called when we are connected and authenticated """ self.core.connection_time = time.time() - if not self.core.plugins_autoloaded: # Do not reload plugins on reconnection + if not self.core.plugins_autoloaded: # Do not reload plugins on reconnection self.core.autoload_plugins() self.core.information("Authentication success.", 'Info') - self.core.information("Your JID is %s" % self.core.xmpp.boundjid.full, 'Info') + self.core.information("Your JID is %s" % self.core.xmpp.boundjid.full, + 'Info') if not self.core.xmpp.anon: # request the roster self.core.xmpp.get_roster() @@ -1112,7 +1220,8 @@ class HandlerCore: self.core.join_initial_rooms(self.core.bookmarks) if config.get('enable_user_nick'): - self.core.xmpp.plugin['xep_0172'].publish_nick(nick=self.core.own_nick, callback=dumb_callback) + self.core.xmpp.plugin['xep_0172'].publish_nick( + nick=self.core.own_nick, callback=dumb_callback) asyncio.ensure_future(self.core.xmpp.plugin['xep_0115'].update_caps()) # Start the ping's plugin regular event self.core.xmpp.set_keepalive_values() @@ -1126,9 +1235,14 @@ class HandlerCore: """ room_from = message['from'] tab = self.core.get_tab_by_name(room_from, tabs.MucTab) - status_codes = {s.attrib['code'] for s in message.xml.findall('{%s}x/{%s}status' % (tabs.NS_MUC_USER, tabs.NS_MUC_USER))} + status_codes = { + s.attrib['code'] + for s in message.xml.findall('{%s}x/{%s}status' % ( + tabs.NS_MUC_USER, tabs.NS_MUC_USER)) + } if '101' in status_codes: - self.core.information('Your affiliation in the room %s changed' % room_from, 'Info') + self.core.information( + 'Your affiliation in the room %s changed' % room_from, 'Info') elif tab and status_codes: show_unavailable = '102' in status_codes hide_unavailable = '103' in status_codes @@ -1141,38 +1255,70 @@ class HandlerCore: modif = False if show_unavailable or hide_unavailable or non_priv or logging_off\ or non_anon or semi_anon or full_anon: - tab.add_message('\x19%(info_col)s}Info: A configuration change not privacy-related occured.' % - {'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)}, - typ=2) + tab.add_message( + '\x19%(info_col)s}Info: A configuration change not privacy-related occured.' + % { + 'info_col': dump_tuple( + get_theme().COLOR_INFORMATION_TEXT) + }, + typ=2) modif = True if show_unavailable: - tab.add_message('\x19%(info_col)s}Info: The unavailable members are now shown.' % - {'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)}, - typ=2) + tab.add_message( + '\x19%(info_col)s}Info: The unavailable members are now shown.' + % { + 'info_col': dump_tuple( + get_theme().COLOR_INFORMATION_TEXT) + }, + typ=2) elif hide_unavailable: - tab.add_message('\x19%(info_col)s}Info: The unavailable members are now hidden.' % - {'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)}, - typ=2) + tab.add_message( + '\x19%(info_col)s}Info: The unavailable members are now hidden.' + % { + 'info_col': dump_tuple( + get_theme().COLOR_INFORMATION_TEXT) + }, + typ=2) if non_anon: - tab.add_message('\x191}Warning:\x19%(info_col)s} The room is now not anonymous. (public JID)' % - {'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)}, - typ=2) + tab.add_message( + '\x191}Warning:\x19%(info_col)s} The room is now not anonymous. (public JID)' + % { + 'info_col': dump_tuple( + get_theme().COLOR_INFORMATION_TEXT) + }, + typ=2) elif semi_anon: - tab.add_message('\x19%(info_col)s}Info: The room is now semi-anonymous. (moderators-only JID)' % - {'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)}, - typ=2) + tab.add_message( + '\x19%(info_col)s}Info: The room is now semi-anonymous. (moderators-only JID)' + % { + 'info_col': dump_tuple( + get_theme().COLOR_INFORMATION_TEXT) + }, + typ=2) elif full_anon: - tab.add_message('\x19%(info_col)s}Info: The room is now fully anonymous.' % - {'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)}, - typ=2) + tab.add_message( + '\x19%(info_col)s}Info: The room is now fully anonymous.' % + { + 'info_col': dump_tuple( + get_theme().COLOR_INFORMATION_TEXT) + }, + typ=2) if logging_on: - tab.add_message('\x191}Warning: \x19%(info_col)s}This room is publicly logged' % - {'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)}, - typ=2) + tab.add_message( + '\x191}Warning: \x19%(info_col)s}This room is publicly logged' + % { + 'info_col': dump_tuple( + get_theme().COLOR_INFORMATION_TEXT) + }, + typ=2) elif logging_off: - tab.add_message('\x19%(info_col)s}Info: This room is not logged anymore.' % - {'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)}, - typ=2) + tab.add_message( + '\x19%(info_col)s}Info: This room is not logged anymore.' % + { + 'info_col': dump_tuple( + get_theme().COLOR_INFORMATION_TEXT) + }, + typ=2) if modif: self.core.refresh_window() @@ -1203,22 +1349,30 @@ class HandlerCore: after = '' if user: user_col = dump_tuple(user.color) - user_string = '\x19%s}%s\x19%s}%s' % (user_col, nick_from, fmt['info_col'], after) + user_string = '\x19%s}%s\x19%s}%s' % (user_col, nick_from, + fmt['info_col'], + after) else: - user_string = '\x19%s}%s%s' % (fmt['info_col'], nick_from, after) + user_string = '\x19%s}%s%s' % (fmt['info_col'], nick_from, + after) fmt['user'] = user_string if nick_from: - tab.add_message("%(user)s set the subject to: \x19%(text_col)s}%(subject)s" % fmt, - time=None, - typ=2) + tab.add_message( + "%(user)s set the subject to: \x19%(text_col)s}%(subject)s" + % fmt, + time=None, + typ=2) else: - tab.add_message("\x19%(info_col)s}The subject is: \x19%(text_col)s}%(subject)s" % fmt, - time=None, - typ=2) + tab.add_message( + "\x19%(info_col)s}The subject is: \x19%(text_col)s}%(subject)s" + % fmt, + time=None, + typ=2) tab.topic = subject tab.topic_from = nick_from - if self.core.get_tab_by_name(room_from, tabs.MucTab) is self.core.current_tab(): + if self.core.get_tab_by_name(room_from, + tabs.MucTab) is self.core.current_tab(): self.core.refresh_window() def on_receipt(self, message): @@ -1231,7 +1385,8 @@ class HandlerCore: return conversation = self.core.get_tab_by_name(jid.full, tabs.OneToOneTab) - conversation = conversation or self.core.get_tab_by_name(jid.bare, tabs.OneToOneTab) + conversation = conversation or self.core.get_tab_by_name( + jid.bare, tabs.OneToOneTab) if not conversation: log.error("Received ack from non-existing chat tab: %s", jid) return @@ -1272,15 +1427,21 @@ class HandlerCore: if self.core.xml_tab: if PYGMENTS: xhtml_text = highlight('%s' % stanza, LEXER, FORMATTER) - poezio_colored = xhtml.xhtml_to_poezio_colors(xhtml_text, force=True).rstrip('\x19o').strip() + poezio_colored = xhtml.xhtml_to_poezio_colors( + xhtml_text, force=True).rstrip('\x19o').strip() else: poezio_colored = '%s' % stanza - self.core.add_message_to_text_buffer(self.core.xml_buffer, poezio_colored, - nickname=get_theme().CHAR_XML_OUT) + self.core.add_message_to_text_buffer( + self.core.xml_buffer, + poezio_colored, + nickname=get_theme().CHAR_XML_OUT) try: - if self.core.xml_tab.match_stanza(ElementBase(ET.fromstring(stanza))): - self.core.add_message_to_text_buffer(self.core.xml_tab.filtered_buffer, poezio_colored, - nickname=get_theme().CHAR_XML_OUT) + if self.core.xml_tab.match_stanza( + ElementBase(ET.fromstring(stanza))): + self.core.add_message_to_text_buffer( + self.core.xml_tab.filtered_buffer, + poezio_colored, + nickname=get_theme().CHAR_XML_OUT) except: log.debug('', exc_info=True) @@ -1295,15 +1456,20 @@ class HandlerCore: if self.core.xml_tab: if PYGMENTS: xhtml_text = highlight('%s' % stanza, LEXER, FORMATTER) - poezio_colored = xhtml.xhtml_to_poezio_colors(xhtml_text, force=True).rstrip('\x19o').strip() + poezio_colored = xhtml.xhtml_to_poezio_colors( + xhtml_text, force=True).rstrip('\x19o').strip() else: poezio_colored = '%s' % stanza - self.core.add_message_to_text_buffer(self.core.xml_buffer, poezio_colored, - nickname=get_theme().CHAR_XML_IN) + self.core.add_message_to_text_buffer( + self.core.xml_buffer, + poezio_colored, + nickname=get_theme().CHAR_XML_IN) try: if self.core.xml_tab.match_stanza(stanza): - self.core.add_message_to_text_buffer(self.core.xml_tab.filtered_buffer, poezio_colored, - nickname=get_theme().CHAR_XML_IN) + self.core.add_message_to_text_buffer( + self.core.xml_tab.filtered_buffer, + poezio_colored, + nickname=get_theme().CHAR_XML_IN) except: log.debug('', exc_info=True) if isinstance(self.core.current_tab(), tabs.XMLTab): @@ -1311,33 +1477,34 @@ class HandlerCore: self.core.doupdate() def ssl_invalid_chain(self, tb): - self.core.information('The certificate sent by the server is invalid.', 'Error') + self.core.information('The certificate sent by the server is invalid.', + 'Error') self.core.disconnect() def _ssl_pop_tab(self, old_cert, new_cert): def cb(result): if result: self.core.information( - 'New certificate accepted:\nnew: %s\nold: %s' % ( - old_cert, new_cert), - 'Info') + 'New certificate accepted:\nnew: %s\nold: %s' % + (old_cert, new_cert), 'Info') log.debug('Setting certificate to %s', new_cert) if not config.silent_set('certificate', new_cert): - self.core.information( - 'Unable to write in the config file', - 'Error') + self.core.information('Unable to write in the config file', + 'Error') else: - self.core.information('You refused to validate the certificate.' - ' You are now disconnected.', 'Info') + self.core.information( + 'You refused to validate the certificate.' + ' You are now disconnected.', 'Info') self.core.disconnect() - confirm_tab = tabs.ConfirmTab(self.core, - 'Certificate check required', - CERT_WARNING_TEXT % (self.core.xmpp.boundjid.domain, old_cert, new_cert), - 'You need to accept or reject the certificate', - cb, - critical=True) - + confirm_tab = tabs.ConfirmTab( + self.core, + 'Certificate check required', + CERT_WARNING_TEXT % (self.core.xmpp.boundjid.domain, old_cert, + new_cert), + 'You need to accept or reject the certificate', + cb, + critical=True) self.core.add_tab(confirm_tab, True) self.core.doupdate() @@ -1356,21 +1523,28 @@ class HandlerCore: cert = config.get('certificate') # update the cert representation when it uses the old one if cert and ':' not in cert: - cert = ':'.join(i + j for i, j in zip(cert[::2], cert[1::2])).upper() + cert = ':'.join( + i + j for i, j in zip(cert[::2], cert[1::2])).upper() config.set_and_save('certificate', cert) der = ssl.PEM_cert_to_DER_cert(pem) - asn1 = pyasn1.codec.der.decoder.decode(der, asn1Spec=pyasn1_modules.rfc2459.Certificate())[0] - spki = asn1.getComponentByName("tbsCertificate").getComponentByName("subjectPublicKeyInfo") - spki_digest = sha256(pyasn1.codec.der.encoder.encode(spki)).hexdigest().upper() - spki_found_cert = ':'.join(i + j for i, j in zip(spki_digest[::2], spki_digest[1::2])) + asn1 = pyasn1.codec.der.decoder.decode( + der, asn1Spec=pyasn1_modules.rfc2459.Certificate())[0] + spki = asn1.getComponentByName("tbsCertificate").getComponentByName( + "subjectPublicKeyInfo") + spki_digest = sha256( + pyasn1.codec.der.encoder.encode(spki)).hexdigest().upper() + spki_found_cert = ':'.join( + i + j for i, j in zip(spki_digest[::2], spki_digest[1::2])) sha2_digest = sha512(der).hexdigest().upper() - sha2_found_cert = ':'.join(i + j for i, j in zip(sha2_digest[::2], sha2_digest[1::2])) + sha2_found_cert = ':'.join( + i + j for i, j in zip(sha2_digest[::2], sha2_digest[1::2])) if cert: if sha2_found_cert == cert: - log.debug('Current hash is cert hash, moving to SPKI hash (%s)', - spki_found_cert) + log.debug( + 'Current hash is cert hash, moving to SPKI hash (%s)', + spki_found_cert) config.set_and_save('certificate', spki_found_cert) return elif spki_found_cert == cert: @@ -1380,10 +1554,12 @@ class HandlerCore: else: log.debug('First time. Setting certificate to %s', spki_found_cert) if not config.silent_set('certificate', spki_found_cert): - self.core.information('Unable to write in the config file', 'Error') + self.core.information('Unable to write in the config file', + 'Error') def http_confirm(self, stanza): confirm = stanza['confirm'] + def cb(result): if result: reply = stanza.reply() @@ -1396,13 +1572,15 @@ class HandlerCore: reply.append(stanza['confirm']) reply.send() - c_id, c_url, c_method = confirm['id'], confirm['url'], confirm['method'] - confirm_tab = tabs.ConfirmTab(self.core, - 'HTTP Verification', - HTTP_VERIF_TEXT % (c_method, c_url, c_id, stanza['from'].full), - 'An HTTP verification was requested', - cb, - critical=False) + c_id, c_url, c_method = confirm['id'], confirm['url'], confirm[ + 'method'] + confirm_tab = tabs.ConfirmTab( + self.core, + 'HTTP Verification', + HTTP_VERIF_TEXT % (c_method, c_url, c_id, stanza['from'].full), + 'An HTTP verification was requested', + cb, + critical=False) self.core.add_tab(confirm_tab, False) self.core.refresh_window() self.core.doupdate() @@ -1411,19 +1589,24 @@ class HandlerCore: def next_adhoc_step(self, iq, adhoc_session): status = iq['command']['status'] - xform = iq.xml.find('{http://jabber.org/protocol/commands}command/{jabber:x:data}x') + xform = iq.xml.find( + '{http://jabber.org/protocol/commands}command/{jabber:x:data}x') if xform is not None: form = self.core.xmpp.plugin['xep_0004'].build_form(xform) else: form = None if status == 'error': - return self.core.information("An error occured while executing the command") + return self.core.information( + "An error occured while executing the command") if status == 'executing': if not form: - self.core.information("Adhoc command step does not contain a data-form. Aborting the execution.", "Error") - return self.core.xmpp.plugin['xep_0050'].cancel_command(adhoc_session) + self.core.information( + "Adhoc command step does not contain a data-form. Aborting the execution.", + "Error") + return self.core.xmpp.plugin['xep_0050'].cancel_command( + adhoc_session) on_validate = self._validate_adhoc_step on_cancel = self._cancel_adhoc_command if status == 'completed': @@ -1435,18 +1618,20 @@ class HandlerCore: if form: for note in iq['command']['notes']: form.add_field(type='fixed', label=note[1]) - self.core.open_new_form(form, on_cancel, on_validate, - session=adhoc_session) - else: # otherwise, just display an information - # message + self.core.open_new_form( + form, on_cancel, on_validate, session=adhoc_session) + else: # otherwise, just display an information + # message notes = '\n'.join([note[1] for note in iq['command']['notes']]) - self.core.information("Adhoc command %s: %s" % (status, notes), "Info") + self.core.information("Adhoc command %s: %s" % (status, notes), + "Info") def adhoc_error(self, iq, adhoc_session): self.core.xmpp.plugin['xep_0050'].terminate_command(adhoc_session) error_message = self.core.get_error_message(iq) - self.core.information("An error occured while executing the command: %s" % (error_message), - 'Error') + self.core.information( + "An error occured while executing the command: %s" % + (error_message), 'Error') def _cancel_adhoc_command(self, form, session): self.core.xmpp.plugin['xep_0050'].cancel_command(session) @@ -1461,6 +1646,7 @@ class HandlerCore: self.core.xmpp.plugin['xep_0050'].terminate_command(session) self.core.close_tab() + def _composing_tab_state(tab, state): """ Set a tab state to or from the "composing" state @@ -1473,7 +1659,7 @@ def _composing_tab_state(tab, state): elif isinstance(tab, tabs.ConversationTab): values = ('true', 'direct', 'conversation') else: - return # should not happen + return # should not happen show = config.get('show_composing_tabs') show = show in values @@ -1486,4 +1672,3 @@ def _composing_tab_state(tab, state): tab.state = 'composing' elif tab.state == 'composing' and state != 'composing': tab.restore_state() - diff --git a/poezio/core/structs.py b/poezio/core/structs.py index 7d568f04..72c9628a 100644 --- a/poezio/core/structs.py +++ b/poezio/core/structs.py @@ -2,8 +2,10 @@ Module defining structures useful to the core class and related methods """ -__all__ = ['ERROR_AND_STATUS_CODES', 'DEPRECATED_ERRORS', 'POSSIBLE_SHOW', - 'Status', 'Command', 'Completion'] +__all__ = [ + 'ERROR_AND_STATUS_CODES', 'DEPRECATED_ERRORS', 'POSSIBLE_SHOW', 'Status', + 'Command', 'Completion' +] # http://xmpp.org/extensions/xep-0045.html#errorstatus ERROR_AND_STATUS_CODES = { @@ -15,7 +17,7 @@ ERROR_AND_STATUS_CODES = { '407': 'You are not in the member list', '409': 'This nickname is already in use or has been reserved', '503': 'The maximum number of users has been reached', - } +} # http://xmpp.org/extensions/xep-0086.html DEPRECATED_ERRORS = { @@ -48,14 +50,18 @@ POSSIBLE_SHOW = { 'xa': 'xa' } + class Status: __slots__ = ('show', 'message') + def __init__(self, show, message): self.show = show self.message = message + class Command: __slots__ = ('func', 'desc', 'comp', 'short_desc', 'usage') + def __init__(self, func, desc, comp, short_desc, usage): self.func = func self.desc = desc @@ -63,11 +69,13 @@ class Command: self.short_desc = short_desc self.usage = usage + class Completion: """ A completion result essentially currying the input completion call. """ __slots__ = ['func', 'args', 'kwargs', 'comp_list'] + def __init__(self, func, comp_list, *args, **kwargs): self.func = func self.comp_list = comp_list |