diff options
author | Florent Le Coz <louiz@louiz.org> | 2014-11-25 16:58:26 +0100 |
---|---|---|
committer | Florent Le Coz <louiz@louiz.org> | 2014-11-25 17:07:45 +0100 |
commit | e1808a8455aadc9fac300c68e397b712a030ae29 (patch) | |
tree | df5ad7cf84a8e9faa4cd018189bf585ee9d7bdeb /src | |
parent | 61b5c6a91e58b81811bcbf8e789b0bf112d3a416 (diff) | |
download | poezio-e1808a8455aadc9fac300c68e397b712a030ae29.tar.gz poezio-e1808a8455aadc9fac300c68e397b712a030ae29.tar.bz2 poezio-e1808a8455aadc9fac300c68e397b712a030ae29.tar.xz poezio-e1808a8455aadc9fac300c68e397b712a030ae29.zip |
Parse command arguments using a decorator and make things more consistent
Avoid surprises with some commands accepting quoted arguments and some other
not.
fix #2555
Diffstat (limited to 'src')
-rw-r--r-- | src/core/commands.py | 292 | ||||
-rwxr-xr-x | src/daemon.py | 3 | ||||
-rw-r--r-- | src/decorators.py | 92 | ||||
-rw-r--r-- | src/tabs/basetabs.py | 17 | ||||
-rw-r--r-- | src/tabs/conversationtab.py | 29 | ||||
-rw-r--r-- | src/tabs/muctab.py | 208 | ||||
-rw-r--r-- | src/tabs/privatetab.py | 17 | ||||
-rw-r--r-- | src/tabs/rostertab.py | 100 | ||||
-rw-r--r-- | src/tabs/xmltab.py | 36 |
9 files changed, 488 insertions, 306 deletions
diff --git a/src/core/commands.py b/src/core/commands.py index 4a8f7f19..712840d2 100644 --- a/src/core/commands.py +++ b/src/core/commands.py @@ -28,15 +28,16 @@ import multiuserchat as muc from plugin import PluginConfig from roster import roster from theming import dump_tuple, get_theme +from decorators import command_args_parser from . structs import Command, possible_show -def command_help(self, arg): +@command_args_parser.quoted(0, 1) +def command_help(self, args): """ - /help <command_name> + /help [command_name] """ - args = arg.split() if not args: color = dump_tuple(get_theme().COLOR_HELP_COMMANDS) acc = [] @@ -67,7 +68,7 @@ def command_help(self, arg): msg = '\n'.join(buff) msg += _("\nType /help <command_name> to know what each command does") - if args: + else: command = args[0].lstrip('/').strip() if command in self.current_tab().commands: @@ -84,7 +85,8 @@ def command_help(self, arg): msg = tup[1] self.information(msg, 'Help') -def command_runkey(self, arg): +@command_args_parser.quoted(1) +def command_runkey(self, args): """ /runkey <key> """ @@ -93,7 +95,9 @@ def command_runkey(self, arg): if key == '^J': return '\n' return key - char = arg.strip() + if args is None: + return self.command_help('runkey') + char = args[0] func = self.key_func.get(char, None) if func: func() @@ -102,21 +106,20 @@ def command_runkey(self, arg): if res: self.refresh_window() -def command_status(self, arg): +@command_args_parser.quoted(1, 1, [None]) +def command_status(self, args): """ /status <status> [msg] """ - args = common.shell_split(arg) - if len(args) < 1: - return + if args is None: + return self.command_help('status') + if not args[0] in possible_show.keys(): - self.command_help('status') - return + return self.command_help('status') + show = possible_show[args[0]] - if len(args) == 2: - msg = args[1] - else: - msg = None + msg = args[1] + pres = self.xmpp.make_presence() if msg: pres['status'] = msg @@ -136,19 +139,15 @@ def command_status(self, arg): if is_muctab and current.joined and show not in ('away', 'xa'): current.send_chat_state('active') -def command_presence(self, arg): +@command_args_parser.quoted(1, 2, [None, None]) +def command_presence(self, args): """ /presence <JID> [type] [status] """ - args = common.shell_split(arg) - if len(args) == 1: - jid, type, status = args[0], None, None - elif len(args) == 2: - jid, type, status = args[0], args[1], None - elif len(args) == 3: - jid, type, status = args[0], args[1], args[2] - else: - return + if args is None: + return self.command_help('presence') + + jid, type, status = args[0], args[1], args[2] if jid == '.' and isinstance(self.current_tab(), tabs.ChatTab): jid = self.current_tab().name if type == 'available': @@ -177,24 +176,26 @@ def command_presence(self, arg): if self.current_tab() in tab.privates: self.current_tab().send_chat_state(chatstate, True) -def command_theme(self, arg=''): +@command_args_parser.quoted(1) +def command_theme(self, args=None): """/theme <theme name>""" - args = arg.split() - if args: - self.command_set('theme %s' % (args[0],)) + if args is None: + return self.command_help('theme') + self.command_set('theme %s' % (args[0],)) -def command_win(self, arg): +@command_args_parser.quoted(1) +def command_win(self, args): """ /win <number> """ - arg = arg.strip() - if not arg: - self.command_help('win') - return + if args is None: + return self.command_help('win') + + nb = args[0] try: - nb = int(arg.split()[0]) + nb = int(nb) except ValueError: - nb = arg + pass if self.current_tab_nb == nb: return self.previous_tab_nb = self.current_tab_nb @@ -219,15 +220,15 @@ def command_win(self, arg): self.current_tab().on_gain_focus() self.refresh_window() -def command_move_tab(self, arg): +@command_args_parser.quoted(2) +def command_move_tab(self, args): """ /move_tab old_pos new_pos """ - args = common.shell_split(arg) - current_tab = self.current_tab() - if len(args) != 2: + if args is None: return self.command_help('move_tab') + current_tab = self.current_tab() if args[0] == '.': args[0] = current_tab.nb if args[1] == '.': @@ -259,16 +260,16 @@ def command_move_tab(self, arg): self.current_tab_nb = self.tabs.index(current_tab) self.refresh_window() -def command_list(self, arg): +@command_args_parser.quoted(0, 1) +def command_list(self, args): """ - /list <server> + /list [server] Opens a MucListTab containing the list of the room in the specified server """ - arg = arg.split() - if len(arg) > 1: + if args is None: return self.command_help('list') - elif arg: - server = safeJID(arg[0]).server + elif args: + server = safeJID(args[0]).server else: if not isinstance(self.current_tab(), tabs.MucTab): return self.information('Please provide a server', 'Error') @@ -279,7 +280,8 @@ def command_list(self, arg): self.xmpp.plugin['xep_0030'].get_items(jid=server, callback=cb) -def command_version(self, arg): +@command_args_parser.quoted(1) +def command_version(self, args): """ /version <jid> """ @@ -296,9 +298,9 @@ def command_version(self, arg): res.get('os') or _('an unknown platform')) self.information(version, 'Info') - args = common.shell_split(arg) - if len(args) < 1: + if args is None: return self.command_help('version') + jid = safeJID(args[0]) if jid.resource or jid not in roster: fixes.get_version(self.xmpp, jid, callback=callback) @@ -308,11 +310,11 @@ def command_version(self, arg): else: fixes.get_version(self.xmpp, jid, callback=callback) -def command_join(self, arg, histo_length=None): +@command_args_parser.quoted(0, 2) +def command_join(self, args, histo_length=None): """ /join [room][/nick] [password] """ - args = common.shell_split(arg) password = None if len(args) == 0: tab = self.current_tab() @@ -409,11 +411,11 @@ def command_join(self, arg, histo_length=None): tab.refresh() self.doupdate() -def command_bookmark_local(self, arg=''): +@command_args_parser.quoted(0, 2) +def command_bookmark_local(self, args): """ /bookmark_local [room][/nick] [password] """ - args = common.shell_split(arg) nick = None password = None if not args and not isinstance(self.current_tab(), tabs.MucTab): @@ -470,15 +472,14 @@ def command_bookmark_local(self, arg=''): self.information(_('Your local bookmarks are now: %s') % [b for b in bookmark.bookmarks if b.method == 'local'], 'Info') -def command_bookmark(self, arg=''): +@command_args_parser.quoted(0, 3) +def command_bookmark(self, args): """ /bookmark [room][/nick] [autojoin] [password] """ - if not config.get('use_remote_bookmarks'): - self.command_bookmark_local(arg) - return - args = common.shell_split(arg) + return self.command_bookmark_local(" ".join(args)) + nick = None if not args and not isinstance(self.current_tab(), tabs.MucTab): return @@ -553,7 +554,8 @@ def command_bookmark(self, arg=''): if each.method in ('pep', 'privatexml'): remote.append(each) -def command_bookmarks(self, arg=''): +@command_args_parser.ignored +def command_bookmarks(self): """/bookmarks""" local = [] remote = [] @@ -568,9 +570,10 @@ def command_bookmarks(self, arg=''): self.information(_('Your local bookmarks are: %s') % local, _('Info')) -def command_remove_bookmark(self, arg=''): +@command_args_parser.quoted(0, 1) +def command_remove_bookmark(self, args): """/remove_bookmark [jid]""" - args = common.shell_split(arg) + if not args: tab = self.current_tab() if isinstance(tab, tabs.MucTab) and bookmark.get_by_jid(tab.name): @@ -589,11 +592,11 @@ def command_remove_bookmark(self, arg=''): else: self.information('No bookmark to remove', 'Info') -def command_set(self, arg): +@command_args_parser.quoted(1, 2) +def command_set(self, args): """ /set [module|][section] <option> [value] """ - args = common.shell_split(arg) if len(args) == 1: option = args[0] value = config.get(option) @@ -650,33 +653,35 @@ def command_set(self, arg): self.call_for_resize() self.information(*info) -def command_toggle(self, arg): +@command_args_parser.quoted(1) +def command_toggle(self, args): """ /toggle <option> shortcut for /set <option> toggle """ - arg = arg.split() - if arg and arg[0]: + if args is None: + return self.command_help('toggle') + + if args[0]: self.command_set('%s toggle' % arg[0]) -def command_server_cycle(self, arg=''): +@command_args_parser.quoted(1, 1) +def command_server_cycle(self, args): """ Do a /cycle on each room of the given server. If none, do it on the current tab """ - args = common.shell_split(arg) tab = self.current_tab() message = "" - if len(args): + if args: domain = args[0] - if len(args) > 1: + if len(args) == 2: message = args[1] else: if isinstance(tab, tabs.MucTab): domain = safeJID(tab.name).domain else: - self.information(_("No server specified"), "Error") - return + return self.information(_("No server specified"), "Error") for tab in self.get_tabs(tabs.MucTab): if tab.name.endswith(domain): if tab.joined: @@ -690,7 +695,8 @@ def command_server_cycle(self, arg=''): else: self.command_join('"%s/%s"' %(tab.name, tab.own_nick)) -def command_last_activity(self, arg): +@command_args_parser.quoted(1) +def command_last_activity(self, args): """ /last_activity <jid> """ @@ -717,41 +723,42 @@ def command_last_activity(self, arg): common.parse_secs_to_str(seconds), (' and his/her last status was %s' % status) if status else '') self.information(msg, 'Info') - jid = safeJID(arg) - if jid == '': + + if args is None: return self.command_help('last_activity') + jid = safeJID(args[0]) self.xmpp.plugin['xep_0012'].get_last_activity(jid, callback=callback) -def command_mood(self, arg): +@command_args_parser.quoted(0, 2) +def command_mood(self, args): """ /mood [<mood> [text]] """ - args = common.shell_split(arg) if not args: - self.xmpp.plugin['xep_0107'].stop() - return + return self.xmpp.plugin['xep_0107'].stop() + mood = args[0] if mood not in pep.MOODS: return self.information(_('%s is not a correct value for a mood.') - % mood, + % mood, _('Error')) - if len(args) > 1: + if len(args) == 2: text = args[1] else: text = None self.xmpp.plugin['xep_0107'].publish_mood(mood, text, callback=dumb_callback) -def command_activity(self, arg): +@command_args_parser.quoted(0, 3) +def command_activity(self, args): """ /activity [<general> [specific] [text]] """ - args = common.shell_split(arg) length = len(args) if not length: - self.xmpp.plugin['xep_0108'].stop() - return + return self.xmpp.plugin['xep_0108'].stop() + general = args[0] if general not in pep.ACTIVITIES: return self.information(_('%s is not a correct value for an activity') @@ -774,14 +781,14 @@ def command_activity(self, arg): self.xmpp.plugin['xep_0108'].publish_activity(general, specific, text, callback=dumb_callback) -def command_gaming(self, arg): +@command_args_parser.quoted(0, 2) +def command_gaming(self, args): """ /gaming [<game name> [server address]] """ - args = common.shell_split(arg) if not args: - self.xmpp.plugin['xep_0196'].stop() - return + return self.xmpp.plugin['xep_0196'].stop() + name = args[0] if len(args) > 1: address = args[1] @@ -791,25 +798,27 @@ def command_gaming(self, arg): server_address=address, callback=dumb_callback) -def command_invite(self, arg): +@command_args_parser.quoted(2, 1, [None]) +def command_invite(self, args): """/invite <to> <room> [reason]""" - args = common.shell_split(arg) - if len(args) < 2: - return - reason = args[2] if len(args) > 2 else None + + if args is None: + return self.command_help('invite') + + reason = args[2] to = safeJID(args[0]) room = safeJID(args[1]).bare self.invite(to.full, room, reason=reason) -def command_decline(self, arg): +@command_args_parser.quoted(1, 1, ['']) +def command_decline(self, args): """/decline <room@server.tld> [reason]""" - args = common.shell_split(arg) - if not len(args): - return + if args is None: + return self.command_help('decline') jid = safeJID(args[0]) if jid.bare not in self.pending_invites: return - reason = args[1] if len(args) > 1 else '' + reason = args[1] del self.pending_invites[jid.bare] self.xmpp.plugin['xep_0045'].decline_invite(jid.bare, self.pending_invites[jid.bare], @@ -817,7 +826,8 @@ def command_decline(self, arg): ### Commands without a completion in this class ### -def command_invitations(self, arg=''): +@command_args_parser.ignored +def command_invitations(self): """/invitations""" build = "" for invite in self.pending_invites: @@ -829,17 +839,16 @@ def command_invitations(self, arg=''): build = "You do not have any pending invitations." self.information(build, 'Info') -def command_quit(self, arg=''): +@command_args_parser.quoted(0, 1, [None]) +def command_quit(self, args): """ - /quit + /quit [message] """ if not self.xmpp.is_connected(): self.exit() return - if len(arg.strip()) != 0: - msg = arg - else: - msg = None + + msg = args[0] if config.get('enable_user_mood'): self.xmpp.plugin['xep_0107'].stop() if config.get('enable_user_activity'): @@ -851,44 +860,47 @@ def command_quit(self, arg=''): self.disconnect(msg) self.xmpp.add_event_handler("disconnected", self.exit, disposable=True) -def command_destroy_room(self, arg=''): +@command_args_parser.quoted(0, 1, ['']) +def command_destroy_room(self, args): """ /destroy_room [JID] """ - room = safeJID(arg).bare + room = safeJID(args[0]).bare if room: muc.destroy_room(self.xmpp, room) elif isinstance(self.current_tab(), tabs.MucTab) and not arg: muc.destroy_room(self.xmpp, self.current_tab().general_jid) else: - self.information(_('Invalid JID: "%s"') % arg, _('Error')) + self.information(_('Invalid JID: "%s"') % args[0], _('Error')) -def command_bind(self, arg): +@command_args_parser.quoted(1, 1, ['']) +def command_bind(self, args): """ Bind a key. """ - args = common.shell_split(arg) - if len(args) < 1: + if args is None: return self.command_help('bind') - elif len(args) < 2: - args.append("") + if not config.silent_set(args[0], args[1], section='bindings'): self.information(_('Unable to write in the config file'), 'Error') + if args[1]: self.information('%s is now bound to %s' % (args[0], args[1]), 'Info') else: self.information('%s is now unbound' % args[0], 'Info') -def command_rawxml(self, arg): +@command_args_parser.raw +def command_rawxml(self, args): """ /rawxml <xml stanza> """ - if not arg: - return + if not args: + return + stanza = args[0] try: - stanza = StanzaBase(self.xmpp, xml=ET.fromstring(arg)) + stanza = StanzaBase(self.xmpp, xml=ET.fromstring(stanza)) if stanza.xml.tag == 'iq' and \ stanza.xml.attrib.get('type') in ('get', 'set') and \ stanza.xml.attrib.get('id'): @@ -912,27 +924,29 @@ def command_rawxml(self, arg): except: self.information(_('Could not send custom stanza'), 'Error') log.debug('/rawxml: Could not send custom stanza (%s)', - repr(arg), + repr(stanza), exc_info=True) -def command_load(self, arg): +@command_args_parser.quoted(1, 256) +def command_load(self, args): """ /load <plugin> [<otherplugin> …] + # TODO: being able to load more than 256 plugins at once, hihi. """ - args = arg.split() for plugin in args: self.plugin_manager.load(plugin) -def command_unload(self, arg): +@command_args_parser.quoted(1, 256) +def command_unload(self, args): """ /unload <plugin> [<otherplugin> …] """ - args = arg.split() for plugin in args: self.plugin_manager.unload(plugin) -def command_plugins(self, arg=''): +@command_args_parser.ignored +def command_plugins(self): """ /plugins """ @@ -940,14 +954,13 @@ def command_plugins(self, arg=''): repr(list(self.plugin_manager.plugins.keys())), _('Info')) -def command_message(self, arg): +@command_args_parser.quoted(1, 1) +def command_message(self, args): """ /message <jid> [message] """ - args = common.shell_split(arg) - if len(args) < 1: - self.command_help('message') - return + if args is None: + return self.command_help('message') jid = safeJID(args[0]) if not jid.user and not jid.domain and not jid.resource: return self.information('Invalid JID.', 'Error') @@ -956,10 +969,11 @@ def command_message(self, arg): tab = self.open_conversation_window(jid.full, focus=True) else: self.focus_tab_named(tab.name) - if len(args) > 1: + if len(args) == 2: tab.command_say(args[1]) -def command_xml_tab(self, arg=''): +@command_args_parser.ignored +def command_xml_tab(self): """/xml_tab""" self.xml_tab = True xml_tab = self.focus_tab_named('XMLTab', tabs.XMLTab) @@ -967,21 +981,19 @@ def command_xml_tab(self, arg=''): tab = tabs.XMLTab() self.add_tab(tab, True) -def command_adhoc(self, arg): - arg = arg.split() - if len(arg) > 1: +@command_args_parser.quoted(1) +def command_adhoc(self, args): + if not args: return self.command_help('ad-hoc') - elif arg: - jid = safeJID(arg[0]) - else: - return self.information('Please provide a jid', 'Error') + jid = safeJID(args[0]) list_tab = tabs.AdhocCommandsListTab(jid) self.add_tab(list_tab, True) cb = list_tab.on_list_received self.xmpp.plugin['xep_0050'].get_commands(jid=jid, local=False, callback=cb) -def command_self(self, arg=None): +@command_args_parser.ignored +def command_self(self): """ /self """ diff --git a/src/daemon.py b/src/daemon.py index 395054a7..7f66558c 100755 --- a/src/daemon.py +++ b/src/daemon.py @@ -56,6 +56,7 @@ class Executor(threading.Thread): command.pop(-1) def run(self): + print("executing %s" % (self.command)) log.debug('executing %s', self.command) stdout = DEVNULL if self.filename: @@ -65,7 +66,7 @@ class Executor(threading.Thread): log.error('Could not open redirection file: %s', self.filename, exc_info=True) return try: - subprocess.call(self.command, stdout=stdout, stderr=DEVNULL) + subprocess.call(self.command) except: if self.remote: import traceback diff --git a/src/decorators.py b/src/decorators.py index 251d8749..627d8f59 100644 --- a/src/decorators.py +++ b/src/decorators.py @@ -2,6 +2,8 @@ Module containing various decorators """ +import common + class RefreshWrapper(object): def __init__(self): self.core = None @@ -41,3 +43,93 @@ class RefreshWrapper(object): return wrap refresh_wrapper = RefreshWrapper() + +class CommandArgParser(object): + """Modify the string argument of the function into a list of strings + containing the right number of extracted arguments, or None if we don’t + have enough. + """ + @staticmethod + def raw(func): + """Just call the function with a single string, which is the original string + untouched + """ + def wrap(self, args, *a, **kw): + return func(self, args, *a, **kw) + return wrap + + @staticmethod + def ignored(func): + """ + Call the function without any argument + """ + def wrap(self, args, *a, **kw): + return func(self, *a, **kw) + return wrap + + @staticmethod + def quoted(mandatory, optional=0, defaults=[], + ignore_trailing_arguments=False): + + """The function receives a list with a number of arguments that is between + the numbers `mandatory` and `optional`. + + If the string doesn’t contain at least `mandatory` arguments, we return + None because the given arguments are invalid. + + If there are any remaining arguments after `mandatory` and `optional` + arguments have been found (and “ignore_trailing_arguments" is not True), + we happen them to the last argument of the list. + + An argument is a string (with or without whitespaces) between to quotes + ("), or a whitespace separated word (if not inside quotes). + + The argument `defaults` is a list of strings that are used when an + optional argument is missing. For example if we accept one optional + argument, zero is available but we have one value in the `defaults` + list, we use that string inplace. The `defaults` list can only + replace missing optional arguments, not mandatory ones. And it + should not contain more than `mandatory` values. Also you cannot + + Example: + This method needs at least one argument, and accepts up to 3 + arguments + + >> @command_args_parser.quoted(1, 2, ['default for first arg'], False) + >> def f(args): + >> print(args) + + >> f('coucou les amis') # We have one mandatory and two optional + ['coucou', 'les', 'amis'] + >> f('"coucou les amis" "PROUT PROUT"') # One mandator and only one optional, + # no default for the second + ['coucou les amis', 'PROUT PROUT'] + >> f('') # Not enough args for mandatory number + None + >> f('"coucou les potes"') # One mandatory, and use the default value + # for the first optional + ['coucou les potes, 'default for first arg'] + >> f('"un et demi" deux trois quatre cinq six') # We have three trailing arguments + ['un et demi', 'deux', 'trois quatre cinq six'] + + """ + def first(func): + def second(self, args, *a, **kw): + default_args = defaults + args = common.shell_split(args) + if len(args) < mandatory: + return func(self, None, *a, **kw) + res, args = args[:mandatory], args[mandatory:] + opt_args = args[:optional] + if opt_args: + res += opt_args + args = args[len(opt_args):] + default_args = default_args[len(opt_args):] + res += default_args + if args and res and not ignore_trailing_arguments: + res[-1] += " " + " ".join(args) + return func(self, res, *a, **kw) + return second + return first + +command_args_parser = CommandArgParser() diff --git a/src/tabs/basetabs.py b/src/tabs/basetabs.py index 0a55640c..9c74c239 100644 --- a/src/tabs/basetabs.py +++ b/src/tabs/basetabs.py @@ -35,7 +35,7 @@ from decorators import refresh_wrapper from logger import logger from text_buffer import TextBuffer from theming import get_theme, dump_tuple - +from decorators import command_args_parser # getters for tab colors (lambdas, so that they are dynamic) STATE_COLORS = { @@ -544,11 +544,12 @@ class ChatTab(Tab): self.command_say(xhtml.convert_simple_to_full_colors(txt)) self.cancel_paused_delay() - def command_xhtml(self, arg): + @command_args_parser.raw + def command_xhtml(self, xhtml): """" /xhtml <custom xhtml> """ - message = self.generate_xhtml_message(arg) + message = self.generate_xhtml_message(xhtml) if message: message.send() @@ -573,7 +574,7 @@ class ChatTab(Tab): return self.name @refresh_wrapper.always - def command_clear(self, args): + def command_clear(self, ignored): """ /clear """ @@ -637,6 +638,7 @@ class ChatTab(Tab): self.core.remove_timed_event(self.timed_event_paused) self.timed_event_paused = None + @command_args_parser.raw def command_correct(self, line): """ /correct <fixed message> @@ -672,6 +674,7 @@ class ChatTab(Tab): if self.text_win.pos != 0: self.state = 'scrolled' + @command_args_parser.raw def command_say(self, line, correct=False): pass @@ -728,8 +731,9 @@ class OneToOneTab(ChatTab): jid=self.get_dest_jid(), timeout=5, callback=self.features_checked) - def command_attention(self, message=''): - "/attention [message]" + @command_args_parser.raw + def command_attention(self, message): + """/attention [message]""" if message is not '': self.command_say(message, attention=True) else: @@ -738,6 +742,7 @@ class OneToOneTab(ChatTab): msg['attention'] = True msg.send() + @command_args_parser.raw def command_say(self, line, correct=False, attention=False): pass diff --git a/src/tabs/conversationtab.py b/src/tabs/conversationtab.py index 52c503d7..2ab33bb9 100644 --- a/src/tabs/conversationtab.py +++ b/src/tabs/conversationtab.py @@ -29,6 +29,7 @@ from config import config from decorators import refresh_wrapper from roster import roster from theming import get_theme, dump_tuple +from decorators import command_args_parser class ConversationTab(OneToOneTab): """ @@ -88,6 +89,7 @@ class ConversationTab(OneToOneTab): def completion(self): self.complete_commands(self.input) + @command_args_parser.raw def command_say(self, line, attention=False, correct=False): msg = self.core.xmpp.make_message(self.get_dest_jid()) msg['type'] = 'chat' @@ -149,19 +151,21 @@ class ConversationTab(OneToOneTab): self.text_win.refresh() self.input.refresh() - def command_xhtml(self, arg): - message = self.generate_xhtml_message(arg) + @command_args_parser.raw + def command_xhtml(self, xhtml): + message = self.generate_xhtml_message(xhtml) if message: message.send() self.core.add_message_to_text_buffer(self._text_buffer, message['body'], None, self.core.own_nick) self.refresh() - def command_last_activity(self, arg): + @command_args_parser.quoted(0, 1) + def command_last_activity(self, args): """ /activity [jid] """ - if arg.strip(): - return self.core.command_last_activity(arg) + if args is not None: + return self.core.command_last_activity(arg[0]) def callback(iq): if iq['type'] != 'result': @@ -191,7 +195,8 @@ class ConversationTab(OneToOneTab): self.core.xmpp.plugin['xep_0012'].get_last_activity(self.general_jid, callback=callback) @refresh_wrapper.conditional - def command_info(self, arg): + @command_args_parser.ignored + def command_info(self): contact = roster[self.get_dest_jid()] jid = safeJID(self.get_dest_jid()) if contact: @@ -210,12 +215,14 @@ class ConversationTab(OneToOneTab): self._text_buffer.add_message("\x19%(info_col)s}No information available\x19o" % {'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)}) return True - def command_unquery(self, arg): + @command_args_parser.ignored + def command_unquery(self): self.core.close_tab() - def command_version(self, arg): + @command_args_parser.quoted(0, 1) + def command_version(self, args): """ - /version + /version [jid] """ def callback(res): if not res: @@ -225,8 +232,8 @@ class ConversationTab(OneToOneTab): res.get('version') or _('unknown'), res.get('os') or _('an unknown platform')) self.core.information(version, 'Info') - if arg: - return self.core.command_version(arg) + if args: + return self.core.command_version(args[0]) jid = safeJID(self.name) if not jid.resource: if jid in roster: diff --git a/src/tabs/muctab.py b/src/tabs/muctab.py index 547830cb..8ea73e5c 100644 --- a/src/tabs/muctab.py +++ b/src/tabs/muctab.py @@ -29,7 +29,7 @@ import windows import xhtml from common import safeJID from config import config -from decorators import refresh_wrapper +from decorators import refresh_wrapper, command_args_parser from logger import logger from roster import roster from theming import get_theme, dump_tuple @@ -300,15 +300,12 @@ class MucTab(ChatTab): return the_input.new_completion(possible_affiliations, 2, '', quotify=True) + @command_args_parser.quoted(1, 1, ['']) def command_invite(self, args): """/invite <jid> [reason]""" - args = common.shell_split(args) - if len(args) == 1: - jid, reason = args[0], '' - elif len(args) == 2: - jid, reason = args - else: + if args is None: return self.core.command_help('invite') + jid, reason = args self.core.command_invite('%s %s "%s"' % (jid, self.name, reason)) def completion_invite(self, the_input): @@ -327,15 +324,17 @@ class MucTab(ChatTab): self.user_win.refresh(self.users) self.input.refresh() - def command_info(self, arg): + @command_args_parser.quoted(1) + def command_info(self, args): """ /info <nick> """ - if not arg: + if args is None: return self.core.command_help('info') - user = self.get_user_by_name(arg) + nick = args[0] + user = self.get_user_by_name(nick) if not user: - return self.core.information(_("Unknown user: %s") % arg) + return self.core.information(_("Unknown user: %s") % nick) theme = get_theme() if user.jid: user_jid = ' (\x19%s}%s\x19o)' % ( @@ -346,7 +345,7 @@ class MucTab(ChatTab): info = _('\x19%s}%s\x19o%s: show: \x19%s}%s\x19o, affiliation:' ' \x19%s}%s\x19o, role: \x19%s}%s\x19o%s') % ( dump_tuple(user.color), - arg, + nick, user_jid, dump_tuple(theme.color_show(user.show)), user.show or 'Available', @@ -358,7 +357,8 @@ class MucTab(ChatTab): self.add_message(info, typ=0) self.core.refresh_window() - def command_configure(self, arg): + @command_args_parser.quoted(0) + def command_configure(self, ignored): """ /configure """ @@ -386,20 +386,21 @@ class MucTab(ChatTab): muc.configure_room(self.core.xmpp, self.name, form) self.core.close_tab() - def command_cycle(self, arg): + @command_args_parser.raw + def command_cycle(self, msg): """/cycle [reason]""" - self.command_part(arg) + self.command_part(args) self.disconnect() self.user_win.pos = 0 self.core.disable_private_tabs(self.name) self.core.command_join('"/%s"' % self.own_nick) - def command_recolor(self, arg): + @command_args_parser.quoted(0, 1, ['']) + def command_recolor(self, args): """ /recolor [random] Re-assign color to the participants of the room """ - arg = arg.strip() compare_users = lambda x: x.last_talked users = list(self.users) sorted_users = sorted(users, key=compare_users, reverse=True) @@ -409,7 +410,7 @@ class MucTab(ChatTab): sorted_users.remove(user) user.color = get_theme().COLOR_OWN_NICK colors = list(get_theme().LIST_COLOR_NICKNAMES) - if arg and arg == 'random': + if args[0] == 'random': random.shuffle(colors) for i, user in enumerate(sorted_users): user.color = colors[i % len(colors)] @@ -418,7 +419,8 @@ class MucTab(ChatTab): self.text_win.refresh() self.input.refresh() - def command_version(self, arg): + @command_args_parser.quoted(1) + def command_version(self, args): """ /version <jid or nick> """ @@ -433,23 +435,25 @@ class MucTab(ChatTab): res.get('version') or _('unknown'), res.get('os') or _('an unknown platform')) self.core.information(version, 'Info') - if not arg: + if args is None: return self.core.command_help('version') - if arg in [user.nick for user in self.users]: + nick = args[0] + if nick in [user.nick for user in self.users]: jid = safeJID(self.name).bare - jid = safeJID(jid + '/' + arg) + jid = safeJID(jid + '/' + nick) else: - jid = safeJID(arg) + jid = safeJID(nick) fixes.get_version(self.core.xmpp, jid, - callback=callback) + callback=callback) - def command_nick(self, arg): + @command_args_parser.quoted(1) + def command_nick(self, args): """ /nick <nickname> """ - if not arg: + if args is None: return self.core.command_help('nick') - nick = arg + nick = args[0] if not self.joined: return self.core.information(_('/nick only works in joined rooms'), _('Info')) @@ -460,11 +464,12 @@ class MucTab(ChatTab): current_status.message, current_status.show) - def command_part(self, arg): + @command_args_parser.quoted(0, 1, ['']) + def command_part(self, args): """ /part [msg] """ - arg = arg.strip() + arg = args[0] msg = None if self.joined: info_col = dump_tuple(get_theme().COLOR_INFORMATION_TEXT) @@ -505,37 +510,39 @@ class MucTab(ChatTab): self.refresh() self.core.doupdate() - def command_close(self, arg): + @command_args_parser.raw + def command_close(self, msg): """ /close [msg] """ - self.command_part(arg) + self.command_part(args) self.core.close_tab() - def command_query(self, arg): + @command_args_parser.quoted(1, 1) + def command_query(self, args): """ /query <nick> [message] """ - args = common.shell_split(arg) - if len(args) < 1: - return + if args is None: + return self.core.command_help('query') nick = args[0] r = None for user in self.users: if user.nick == nick: r = self.core.open_private_window(self.name, user.nick) - if r and len(args) > 1: + if r and len(args) == 2: msg = args[1] self.core.current_tab().command_say( xhtml.convert_simple_to_full_colors(msg)) if not r: self.core.information(_("Cannot find user: %s" % nick), 'Error') - def command_topic(self, arg): + @command_args_parser.raw + def command_topic(self, subject): """ /topic [new topic] """ - if not arg.strip(): + if not subject: self._text_buffer.add_message( _("\x19%s}The subject of the room is: %s %s") % (dump_tuple(get_theme().COLOR_INFORMATION_TEXT), @@ -544,10 +551,11 @@ class MucTab(ChatTab): else '')) self.refresh() return - subject = arg + muc.change_subject(self.core.xmpp, self.name, subject) - def command_names(self, arg=None): + @command_args_parser.quoted(0) + def command_names(self, args): """ /names """ @@ -618,29 +626,28 @@ class MucTab(ChatTab): return the_input.new_completion(word_list, 1, quotify=True) - def command_kick(self, arg): + @command_args_parser.quoted(1, 1) + def command_kick(self, args): """ /kick <nick> [reason] """ - args = common.shell_split(arg) - if not args: - self.core.command_help('kick') + if args is None: + return self.core.command_help('kick') + if len(args) == 2: + msg = ' "%s"' % args[1] else: - if len(args) > 1: - msg = ' "%s"' % args[1] - else: - msg = '' - self.command_role('"'+args[0]+ '" none'+msg) + msg = '' + self.command_role('"'+args[0]+ '" none'+msg) - def command_ban(self, arg): + @command_args_parser.quoted(1, 1) + def command_ban(self, args): """ /ban <nick> [reason] """ def callback(iq): if iq['type'] == 'error': self.core.room_error(iq, self.name) - args = common.shell_split(arg) - if not args: + if args is None: return self.core.command_help('ban') if len(args) > 1: msg = args[1] @@ -659,7 +666,8 @@ class MucTab(ChatTab): if not res: self.core.information('Could not ban user', 'Error') - def command_role(self, arg): + @command_args_parser.quoted(2, 1, ['']) + def command_role(self, args): """ /role <nick> <role> [reason] Changes the role of an user @@ -668,24 +676,25 @@ class MucTab(ChatTab): def callback(iq): if iq['type'] == 'error': self.core.room_error(iq, self.name) - args = common.shell_split(arg) - if len(args) < 2: - self.core.command_help('role') - return - nick, role = args[0], args[1] - if len(args) > 2: - reason = ' '.join(args[2:]) - else: - reason = '' - if not self.joined or \ - not role in ('none', 'visitor', 'participant', 'moderator'): - return + + if args is None: + return self.core.command_help('role') + + nick, role, reason = args[0], args[1].lower(), args[2] + + valid_roles = ('none', 'visitor', 'participant', 'moderator') + + if not self.joined or role not in valid_roles: + return self.core.information(_('The role must be one of ' + ', '.join(valid_roles)), + _('Error')) + if not safeJID(self.name + '/' + nick): return self.core('Invalid nick', 'Info') muc.set_user_role(self.core.xmpp, self.name, nick, reason, role, callback=callback) - def command_affiliation(self, arg): + @command_args_parser.quoted(2) + def command_affiliation(self, args): """ /affiliation <nick> <role> Changes the affiliation of an user @@ -694,16 +703,20 @@ class MucTab(ChatTab): def callback(iq): if iq['type'] == 'error': self.core.room_error(iq, self.name) - args = common.shell_split(arg) - if len(args) < 2: - self.core.command_help('affiliation') - return + + if args is None: + return self.core.command_help('affiliation') + nick, affiliation = args[0], args[1].lower() + if not self.joined: return - if affiliation not in ('outcast', 'none', 'member', 'admin', 'owner'): - self.core.command_help('affiliation') - return + + valid_affiliations = ('outcast', 'none', 'member', 'admin', 'owner') + if affiliation not in valid_affiliations: + return self.core.information(_('The affiliation must be one of ' + ', '.join(valid_affiliations)), + _('Error')) + if nick in [user.nick for user in self.users]: res = muc.set_user_affiliation(self.core.xmpp, self.name, affiliation, nick=nick, @@ -715,6 +728,7 @@ class MucTab(ChatTab): if not res: self.core.information(_('Could not set affiliation'), _('Error')) + @command_args_parser.raw def command_say(self, line, correct=False): """ /say <message> @@ -753,20 +767,22 @@ class MucTab(ChatTab): msg.send() self.chat_state = needed - def command_xhtml(self, arg): - message = self.generate_xhtml_message(arg) + @command_args_parser.raw + def command_xhtml(self, msg): + message = self.generate_xhtml_message(msg) if message: message['type'] = 'groupchat' message.send() - def command_ignore(self, arg): + @command_args_parser.quoted(1) + def command_ignore(self, args): """ /ignore <nick> """ - if not arg: - self.core.command_help('ignore') - return - nick = arg + if arg is None: + return self.core.command_help('ignore') + + nick = args[0] user = self.get_user_by_name(nick) if not user: self.core.information(_('%s is not in the room') % nick) @@ -776,14 +792,15 @@ class MucTab(ChatTab): self.ignores.append(user) self.core.information(_("%s is now ignored") % nick, 'info') - def command_unignore(self, arg): + @command_args_parser.quoted(1) + def command_unignore(self, args): """ /unignore <nick> """ - if not arg: - self.core.command_help('unignore') - return - nick = arg + if arg is None: + return self.core.command_help('unignore') + + nick = args[0] user = self.get_user_by_name(nick) if not user: self.core.information(_('%s is not in the room') % nick) @@ -1552,4 +1569,23 @@ class MucTab(ChatTab): def matching_names(self): return [(1, safeJID(self.name).user), (3, self.name)] - + def enable_self_ping_event(self): + delay = config.get_by_tabname("self_ping_delay", self.general_jid, default=60) + if delay <= 0: # use 0 or some negative value to disable it + return + self.self_ping_event = timed_events.DelayedEvent(delay, self.send_self_ping) + self.core.add_timed_event(self.self_ping_event) + + def send_self_ping(self): + to = self.name + "/" + self.own_nick + self.core.xmpp.plugin['xep_0199'].send_ping(jid=to, + callback=self.on_self_ping_result, + timeout_callback=self.on_self_ping_failed, + timeout=10) + + def on_self_ping_result(self, iq): + if iq["type"] == "error": + self.command_part(iq["error"]["text"] or "not in this room") + self.core.refresh_window() + else: # Re-send a self-ping in a few seconds + self.enable_self_ping_event() diff --git a/src/tabs/privatetab.py b/src/tabs/privatetab.py index 4c01cd70..aa19d4d4 100644 --- a/src/tabs/privatetab.py +++ b/src/tabs/privatetab.py @@ -27,6 +27,7 @@ from config import config from decorators import refresh_wrapper from logger import logger from theming import get_theme, dump_tuple +from decorators import command_args_parser class PrivateTab(OneToOneTab): """ @@ -120,6 +121,7 @@ class PrivateTab(OneToOneTab): empty_after = self.input.get_text() == '' or (self.input.get_text().startswith('/') and not self.input.get_text().startswith('//')) self.send_composing_chat_state(empty_after) + @command_args_parser.raw def command_say(self, line, attention=False, correct=False): if not self.on: return @@ -182,13 +184,15 @@ class PrivateTab(OneToOneTab): self.text_win.refresh() self.input.refresh() - def command_unquery(self, arg): + @command_args_parser.ignored + def command_unquery(self): """ /unquery """ self.core.close_tab() - def command_version(self, arg): + @command_args_parser.quoted(0, 1) + def command_version(self, args): """ /version """ @@ -200,18 +204,19 @@ class PrivateTab(OneToOneTab): res.get('version') or _('unknown'), res.get('os') or _('an unknown platform')) self.core.information(version, 'Info') - if arg: - return self.core.command_version(arg) + if args: + return self.core.command_version(args[0]) jid = safeJID(self.name) fixes.get_version(self.core.xmpp, jid, callback=callback) + @command_args_parser.quoted(0, 1) def command_info(self, arg): """ /info """ - if arg: - self.parent_muc.command_info(arg) + if args: + self.parent_muc.command_info(args[0]) else: user = safeJID(self.name).resource self.parent_muc.command_info(user) diff --git a/src/tabs/rostertab.py b/src/tabs/rostertab.py index 878e89ed..2e588b72 100644 --- a/src/tabs/rostertab.py +++ b/src/tabs/rostertab.py @@ -25,6 +25,7 @@ from contact import Contact, Resource from decorators import refresh_wrapper from roster import RosterGroup, roster from theming import get_theme, dump_tuple +from decorators import command_args_parser class RosterInfoTab(Tab): """ @@ -158,7 +159,8 @@ class RosterInfoTab(Tab): } tab.add_message(message) - def command_block(self, arg): + @command_args_parser.quoted(0, 1) + def command_block(self, args): """ /block [jid] """ @@ -169,8 +171,8 @@ class RosterInfoTab(Tab): return self.core.information('Contact blocked.', 'Info') item = self.roster_win.selected_row - if arg: - jid = safeJID(arg) + if args: + jid = safeJID(args[0]) elif isinstance(item, Contact): jid = item.bare_jid elif isinstance(item, Resource): @@ -185,7 +187,8 @@ class RosterInfoTab(Tab): jids = roster.jids() return the_input.new_completion(jids, 1, '', quotify=False) - def command_unblock(self, arg): + @command_args_parser.quoted(0, 1) + def command_unblock(self, args): """ /unblock [jid] """ @@ -196,8 +199,8 @@ class RosterInfoTab(Tab): return self.core.information('Contact unblocked.', 'Info') item = self.roster_win.selected_row - if arg: - jid = safeJID(arg) + if args: + jid = safeJID(args[0]) elif isinstance(item, Contact): jid = item.bare_jid elif isinstance(item, Resource): @@ -218,7 +221,8 @@ class RosterInfoTab(Tab): self.core.xmpp.plugin['xep_0191'].get_blocked(callback=on_result) return True - def command_list_blocks(self, arg=None): + @command_args_parser.ignored + def command_list_blocks(self): """ /list_blocks """ @@ -236,7 +240,8 @@ class RosterInfoTab(Tab): self.core.xmpp.plugin['xep_0191'].get_blocked(callback=callback) - def command_reconnect(self, args=None): + @command_args_parser.ignored + def command_reconnect(self): """ /reconnect """ @@ -245,19 +250,21 @@ class RosterInfoTab(Tab): else: self.core.xmpp.connect() - def command_disconnect(self, args=None): + @command_args_parser.ignored + def command_disconnect(self): """ /disconnect """ self.core.disconnect() - def command_last_activity(self, arg=None): + @command_args_parser.quoted(0, 1) + def command_last_activity(self, args): """ /activity [jid] """ item = self.roster_win.selected_row - if arg: - jid = arg + if args: + jid = args[0] elif isinstance(item, Contact): jid = item.bare_jid elif isinstance(item, Resource): @@ -335,7 +342,8 @@ class RosterInfoTab(Tab): return the_input.auto_completion(end_list, '') - def command_clear(self, arg=''): + @command_args_parser.ignored + def command_clear(self): """ /clear """ @@ -344,7 +352,8 @@ class RosterInfoTab(Tab): self.core.information_win.rebuild_everything(self.core.information_buffer) self.refresh() - def command_password(self, arg): + @command_args_parser.quoted(1) + def command_password(self, args): """ /password <password> """ @@ -352,19 +361,18 @@ class RosterInfoTab(Tab): if iq['type'] == 'result': self.core.information('Password updated', 'Account') if config.get('password'): - config.silent_set('password', arg) + config.silent_set('password', args[0]) else: self.core.information('Unable to change the password', 'Account') - self.core.xmpp.plugin['xep_0077'].change_password(arg, callback=callback) + self.core.xmpp.plugin['xep_0077'].change_password(args[0], callback=callback) - - - def command_deny(self, arg): + @command_args_parser.quoted(0, 1) + def command_deny(self, args): """ /deny [jid] Denies a JID from our roster """ - if not arg: + if not args: item = self.roster_win.selected_row if isinstance(item, Contact): jid = item.bare_jid @@ -372,7 +380,7 @@ class RosterInfoTab(Tab): self.core.information('No subscription to deny') return else: - jid = safeJID(arg).bare + jid = safeJID(args[0]).bare if not jid in [jid for jid in roster.jids()]: self.core.information('No subscription to deny') return @@ -383,12 +391,13 @@ class RosterInfoTab(Tab): self.core.information('Subscription to %s was revoked' % jid, 'Roster') + @command_args_parser.quoted(1) def command_add(self, args): """ Add the specified JID to the roster, and set automatically accept the reverse subscription """ - jid = safeJID(safeJID(args.strip()).bare) + jid = safeJID(safeJID(args[0]).bare) if not jid: self.core.information(_('No JID specified'), 'Error') return @@ -398,7 +407,8 @@ class RosterInfoTab(Tab): roster.modified() self.core.information('%s was added to the roster' % jid, 'Roster') - def command_name(self, arg): + @command_args_parser.quoted(1) + def command_name(self, args): """ Set a name for the specified JID in your roster """ @@ -406,7 +416,6 @@ class RosterInfoTab(Tab): if not iq: self.core.information('The name could not be set.', 'Error') log.debug('Error in /name:\n%s', iq) - args = common.shell_split(arg) if not args: return self.core.command_help('name') jid = safeJID(args[0]).bare @@ -424,13 +433,13 @@ class RosterInfoTab(Tab): self.core.xmpp.update_roster(jid, name=name, groups=groups, subscription=subscription, callback=callback) + @command_args_parser.quoted(2) def command_groupadd(self, args): """ Add the specified JID to the specified group """ - args = common.shell_split(args) - if len(args) != 2: - return + if args is None: + return self.core.command_help('groupadd') jid = safeJID(args[0]).bare group = args[1] @@ -464,12 +473,12 @@ class RosterInfoTab(Tab): self.core.xmpp.update_roster(jid, name=name, groups=new_groups, subscription=subscription, callback=callback) - def command_groupmove(self, arg): + @command_args_parser.quoted(3) + def command_groupmove(self, args): """ Remove the specified JID from the first specified group and add it to the second one """ - args = common.shell_split(arg) - if len(args) != 3: + if args is None: return self.core.command_help('groupmove') jid = safeJID(args[0]).bare group_from = args[1] @@ -519,13 +528,14 @@ class RosterInfoTab(Tab): self.core.xmpp.update_roster(jid, name=name, groups=new_groups, subscription=subscription, callback=callback) + @command_args_parser.quoted(2) def command_groupremove(self, args): """ Remove the specified JID from the specified group """ - args = common.shell_split(args) - if len(args) != 2: - return + if args is None: + return self.core.command_help('groupremove') + jid = safeJID(args[0]).bare group = args[1] @@ -559,13 +569,14 @@ class RosterInfoTab(Tab): self.core.xmpp.update_roster(jid, name=name, groups=new_groups, subscription=subscription, callback=callback) + @command_args_parser.quoted(0, 1) def command_remove(self, args): """ Remove the specified JID from the roster. i.e.: unsubscribe from its presence, and cancel its subscription to our. """ - if args.strip(): - jid = safeJID(args.strip()).bare + if args: + jid = safeJID(args[0]).bare else: item = self.roster_win.selected_row if isinstance(item, Contact): @@ -576,12 +587,12 @@ class RosterInfoTab(Tab): roster.remove(jid) del roster[jid] - def command_import(self, arg): + @command_args_parser.quoted(0, 1) + def command_import(self, args): """ Import the contacts """ - args = common.shell_split(arg) - if len(args): + if args: if args[0].startswith('/'): filepath = args[0] else: @@ -603,12 +614,12 @@ class RosterInfoTab(Tab): self.command_add(jid.lstrip('\n')) self.core.information('Contacts imported from %s' % filepath, 'Info') - def command_export(self, arg): + @command_args_parser.quoted(0, 1) + def command_export(self, args): """ Export the contacts """ - args = common.shell_split(arg) - if len(args): + if args: if args[0].startswith('/'): filepath = args[0] else: @@ -697,11 +708,12 @@ class RosterInfoTab(Tab): if contact.pending_in) return the_input.new_completion(jids, 1, '', quotify=False) - def command_accept(self, arg): + @command_args_parser.quoted(0, 1) + def command_accept(self, args): """ Accept a JID from in roster. Authorize it AND subscribe to it """ - if not arg: + if not args: item = self.roster_win.selected_row if isinstance(item, Contact): jid = item.bare_jid @@ -709,7 +721,7 @@ class RosterInfoTab(Tab): self.core.information('No subscription to accept') return else: - jid = safeJID(arg).bare + jid = safeJID(args[0]).bare nodepart = safeJID(jid).user jid = safeJID(jid) # crappy transports putting resources inside the node part diff --git a/src/tabs/xmltab.py b/src/tabs/xmltab.py index 083e97c5..84ecf00c 100644 --- a/src/tabs/xmltab.py +++ b/src/tabs/xmltab.py @@ -19,6 +19,7 @@ from . import Tab import windows from xhtml import clean_text +from decorators import command_args_parser class XMLTab(Tab): def __init__(self): @@ -70,46 +71,53 @@ class XMLTab(Tab): self.text_win.toggle_lock() self.refresh() - def command_filter_xmlmask(self, arg): + @command_args_parser.raw + def command_filter_xmlmask(self, mask): """/filter_xmlmask <xml mask>""" try: - handler = Callback('custom matcher', matcher.MatchXMLMask(arg), + handler = Callback('custom matcher', matcher.MatchXMLMask(mask), self.core.incoming_stanza) self.core.xmpp.remove_handler('custom matcher') self.core.xmpp.register_handler(handler) self.filter_type = "XML Mask Filter" - self.filter = arg + self.filter = mask self.refresh() except: self.core.information('Invalid XML Mask', 'Error') self.command_reset('') - def command_filter_id(self, arg): + @command_args_parser.quoted(1) + def command_filter_id(self, args): """/filter_id <id>""" + if args is None: + return self.core.command_help('filter_id') + self.core.xmpp.remove_handler('custom matcher') handler = Callback('custom matcher', matcher.MatcherId(arg), self.core.incoming_stanza) self.core.xmpp.register_handler(handler) self.filter_type = "Id Filter" - self.filter = arg + self.filter = args[0] self.refresh() - def command_filter_xpath(self, arg): + @command_args_parser.raw + def command_filter_xpath(self, xpath): """/filter_xpath <xpath>""" try: handler = Callback('custom matcher', matcher.MatchXPath( - arg.replace('%n', self.core.xmpp.default_ns)), + xpath.replace('%n', self.core.xmpp.default_ns)), self.core.incoming_stanza) self.core.xmpp.remove_handler('custom matcher') self.core.xmpp.register_handler(handler) self.filter_type = "XPath Filter" - self.filter = arg + self.filter = xpath self.refresh() except: self.core.information('Invalid XML Path', 'Error') self.command_reset('') - def command_reset(self, arg): + @command_args_parser.ignored + def command_reset(self): """/reset""" self.core.xmpp.remove_handler('custom matcher') self.core.xmpp.register_handler(self.core.all_stanzas) @@ -117,11 +125,14 @@ class XMLTab(Tab): self.filter = '' self.refresh() - def command_dump(self, arg): + @command_args_parser.quoted(1) + def command_dump(self, args): """/dump <filename>""" + if args is None: + return self.core.command_help('dump') xml = self.core.xml_buffer.messages[:] text = '\n'.join(('%s %s' % (msg.str_time, clean_text(msg.txt)) for msg in xml)) - filename = os.path.expandvars(os.path.expanduser(arg)) + filename = os.path.expandvars(os.path.expanduser(args[0])) try: with open(filename, 'w') as fd: fd.write(text) @@ -151,7 +162,8 @@ class XMLTab(Tab): def on_scroll_down(self): return self.text_win.scroll_down(self.text_win.height-1) - def command_clear(self, args): + @command_args_parser.ignored + def command_clear(self): """ /clear """ |