summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorent Le Coz <louiz@louiz.org>2014-11-25 16:58:26 +0100
committerFlorent Le Coz <louiz@louiz.org>2014-11-25 17:07:45 +0100
commite1808a8455aadc9fac300c68e397b712a030ae29 (patch)
treedf5ad7cf84a8e9faa4cd018189bf585ee9d7bdeb
parent61b5c6a91e58b81811bcbf8e789b0bf112d3a416 (diff)
downloadpoezio-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
-rw-r--r--src/core/commands.py292
-rwxr-xr-xsrc/daemon.py3
-rw-r--r--src/decorators.py92
-rw-r--r--src/tabs/basetabs.py17
-rw-r--r--src/tabs/conversationtab.py29
-rw-r--r--src/tabs/muctab.py208
-rw-r--r--src/tabs/privatetab.py17
-rw-r--r--src/tabs/rostertab.py100
-rw-r--r--src/tabs/xmltab.py36
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
"""