summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/en/usage.txt8
-rw-r--r--plugins/gpg/__init__.py47
-rw-r--r--plugins/test.py6
-rw-r--r--src/core.py156
-rw-r--r--src/plugin.py19
-rw-r--r--src/plugin_manager.py5
-rw-r--r--src/tabs.py119
7 files changed, 307 insertions, 53 deletions
diff --git a/doc/en/usage.txt b/doc/en/usage.txt
index 30bdf886..c54f8da5 100644
--- a/doc/en/usage.txt
+++ b/doc/en/usage.txt
@@ -204,14 +204,18 @@ These commands work in *any* tab.
on its own, the room will be bookmarked with the nickname you're currently
using in this room (instead of default_nick).
-*/set <option> [value]*:: Sets the value to the option in your configuration
+*/set <option> [value]*:: Set the value to the option in your configuration
file. You can, for example, change your default nickname by doing "/set
default_nick toto" or your resource with "/set resource blabla". You can also
set an empty value (nothing) by providing no [value] after <option>.
+*/set_plugin <plugin> <option> <value>*:: Set the value of the option in a
+ plugin configuration file.
+
*/theme*:: Reload the theme defined in the config file.
-*/presence <jid> [type] [status]*:: Send a directed presence to _jid_ using _type_ and _status_ if provided.
+*/presence <jid> [type] [status]*:: Send a directed presence to _jid_ using
+ _type_ and _status_ if provided.
*/rawxml*:: Send a custom XML stanza.
diff --git a/plugins/gpg/__init__.py b/plugins/gpg/__init__.py
index 5662bd4f..01ca6ab2 100644
--- a/plugins/gpg/__init__.py
+++ b/plugins/gpg/__init__.py
@@ -1,4 +1,6 @@
from gpg import gnupg
+from sleekxmpp.xmlstream.stanzabase import JID
+
from xml.etree import cElementTree as ET
import xml.sax.saxutils
@@ -54,7 +56,7 @@ class Plugin(BasePlugin):
self.add_event_handler('conversation_say_after', self.on_conversation_say)
self.add_event_handler('conversation_msg', self.on_conversation_msg)
- self.add_tab_command(ConversationTab, 'gpg', self.command_gpg, "Usage: /gpg <force|disable>\nGpg: Force or disable gpg encryption with this fulljid.", lambda the_input: the_input.auto_completion(['force', 'disable']))
+ self.add_command('gpg', self.command_gpg, "Usage: /gpg <force|disable|setkey> [JID] [keyid]\nGpg: Force or disable gpg encryption with the fulljid of the current conversation. The setkey argument lets you associate a keyid with the given bare JID.", self.gpg_completion)
ConversationTab.add_information_element('gpg', self.display_encryption_status)
def cleanup(self):
@@ -121,7 +123,7 @@ class Plugin(BasePlugin):
return
signed = to.full in self.contacts.keys()
if signed:
- veryfied = self.contacts[to.full] == 'valid'
+ veryfied = self.contacts[to.full] in ('valid', 'forced')
else:
veryfied = False
if veryfied:
@@ -155,11 +157,46 @@ class Plugin(BasePlugin):
"""
if jid.full not in self.contacts.keys():
return ''
- return ' GPG Key: %s' % self.contacts[jid.full]
+ status = self.contacts[jid.full]
+ self.core.information('%s' % (status,))
+ if status in ('valid', 'invalid', 'signed'):
+ return ' GPG Key: %s (%s)' % (status, 'encrypted' if status == 'valid' else 'NOT encrypted',)
+ else:
+ return ' GPG: Encryption %s' % (status,)
def command_gpg(self, args):
- # TODO
- return
+ """
+ A command to force or disable the encryption, or to assign a keyid to a JID
+ """
+ args = args.split()
+ if not args:
+ return self.core.command_help("gpg")
+ if len(args) >= 2:
+ jid = JID(args[1])
+ else:
+ if isinstance(self.core.current_tab(), ConversationTab):
+ jid = JID(self.core.current_tab().get_name())
+ command = args[0]
+ if command == 'force' or command == 'enable':
+ # we can force encryption only with contact having an associated
+ # key, otherwise we cannot encrypt at all
+ if self.config.has_section('keys') and jid.bare in self.config.options('keys'):
+ self.contacts[JID(jid).full] = 'forced'
+ else:
+ self.core.information('Cannot force encryption: no key associated with %s' % (jid.bare), 'Info')
+ elif command == 'disable':
+ self.contacts[JID(jid).full] = 'disabled'
+ elif command == 'setkey':
+ if len(args) != 3:
+ return self.core.command_help("gpg")
+ if not self.config.has_section('keys'):
+ self.config.add_section('keys')
+ self.config.set(jid.bare, args[2], 'keys')
+ self.config.write()
+ self.core.refresh_window()
+
+ def gpg_completion(self, the_input):
+ return the_input.auto_completion(['force', 'disable', 'setkey'], ' ')
def remove_gpg_headers(self, text):
lines = text.splitlines()
diff --git a/plugins/test.py b/plugins/test.py
index 13ba1e9c..dcb2f7f5 100644
--- a/plugins/test.py
+++ b/plugins/test.py
@@ -1,8 +1,10 @@
from plugin import BasePlugin
+import tabs
class Plugin(BasePlugin):
def init(self):
self.add_command('plugintest', self.command_plugintest, 'Test command')
+ self.add_tab_command(tabs.MucTab, 'plugintest', self.command_tab_plugintest, 'Test command')
self.add_event_handler('message', self.on_message)
self.core.information("Plugin loaded")
@@ -12,5 +14,9 @@ class Plugin(BasePlugin):
def on_message(self, message):
self.core.information("Test plugin received message: {}".format(message))
+ def command_tab_plugintest(self, args):
+ self.core.information("Command for MucTabs! With args {}".format(args))
+ self.del_tab_command(tabs.MucTab, 'plugintest')
+
def command_plugintest(self, args):
self.core.information("Command! With args {}".format(args))
diff --git a/src/core.py b/src/core.py
index 1f93324d..ce675f04 100644
--- a/src/core.py
+++ b/src/core.py
@@ -121,20 +121,21 @@ class Core(object):
'w': (self.command_win, _("Usage: /w <number>\nW: Go to the specified room."), self.completion_win),
'show': (self.command_status, _('Usage: /show <availability> [status message]\nShow: Sets your availability and (optionally) your status message. The <availability> argument is one of \"available, chat, away, afk, dnd, busy, xa\" and the optional [status message] argument will be your status message.'), self.completion_status),
'status': (self.command_status, _('Usage: /status <availability> [status message]\nStatus: Sets your availability and (optionally) your status message. The <availability> argument is one of \"available, chat, away, afk, dnd, busy, xa\" and the optional [status message] argument will be your status message.'), self.completion_status),
- 'bookmark': (self.command_bookmark, _("Usage: /bookmark [roomname][/nick]\nBookmark: Bookmark the specified room (you will then auto-join it on each poezio start). This commands uses almost the same syntaxe as /join. Type /help join for syntaxe examples. Note that when typing \"/bookmark\" on its own, the room will be bookmarked with the nickname you\'re currently using in this room (instead of default_nick)"), None),
- 'set': (self.command_set, _("Usage: /set <option> [value]\nSet: Set the value of the option in your configuration file. You can, for example, change your default nickname by doing `/set default_nick toto` or your resource with `/set resource blabla`. You can also set an empty value (nothing) by providing no [value] after <option>."), None),
- 'theme': (self.command_theme, _('Usage: /theme [theme_name]\nTheme: Reload the theme defined in the config file. If theme_name is provided, set that theme before reloading it.'), None),
+ 'bookmark': (self.command_bookmark, _("Usage: /bookmark [roomname][/nick]\nBookmark: Bookmark the specified room (you will then auto-join it on each poezio start). This commands uses almost the same syntaxe as /join. Type /help join for syntaxe examples. Note that when typing \"/bookmark\" on its own, the room will be bookmarked with the nickname you\'re currently using in this room (instead of default_nick)"), self.completion_bookmark),
+ 'set': (self.command_set, _("Usage: /set <option> [value]\nSet: Set the value of the option in your configuration file. You can, for example, change your default nickname by doing `/set default_nick toto` or your resource with `/set resource blabla`. You can also set an empty value (nothing) by providing no [value] after <option>."), self.completion_set),
+ 'theme': (self.command_theme, _('Usage: /theme [theme_name]\nTheme: Reload the theme defined in the config file. If theme_name is provided, set that theme before reloading it.'), self.completion_theme),
'list': (self.command_list, _('Usage: /list\nList: Get the list of public chatrooms on the specified server.'), self.completion_list),
- 'message': (self.command_message, _('Usage: /message <jid> [optional message]\nMessage: Open a conversation with the specified JID (even if it is not in our roster), and send a message to it, if the message is specified.'), None),
- 'version': (self.command_version, _('Usage: /version <jid>\nVersion: Get the software version of the given JID (usually its XMPP client and Operating System).'), None),
+ 'message': (self.command_message, _('Usage: /message <jid> [optional message]\nMessage: Open a conversation with the specified JID (even if it is not in our roster), and send a message to it, if the message is specified.'), self.completion_version),
+ 'version': (self.command_version, _('Usage: /version <jid>\nVersion: Get the software version of the given JID (usually its XMPP client and Operating System).'), self.completion_version),
'connect': (self.command_reconnect, _('Usage: /connect\nConnect: Disconnect from the remote server if you are currently connected and then connect to it again.'), None),
- 'server_cycle': (self.command_server_cycle, _('Usage: /server_cycle [domain] [message]\nServer Cycle: Disconnect and reconnect in all the rooms in domain.'), None),
+ 'server_cycle': (self.command_server_cycle, _('Usage: /server_cycle [domain] [message]\nServer Cycle: Disconnect and reconnect in all the rooms in domain.'), self.completion_server_cycle),
'bind': (self.command_bind, _('Usage: /bind <key> <equ>\nBind: Bind a key to an other key or to a “command”. For example "/bind ^H KEY_UP" makes Control + h do the same same as the Up key.'), None),
'load': (self.command_load, _('Usage: /load <plugin>\nLoad: Load the specified plugin'), self.plugin_manager.completion_load),
'unload': (self.command_unload, _('Usage: /unload <plugin>\nUnload: Unload the specified plugin'), self.plugin_manager.completion_unload),
'plugins': (self.command_plugins, _('Usage: /plugins\nPlugins: Show the plugins in use.'), None),
- 'presence': (self.command_presence, _('Usage: /presence <JID> [type] [status]\nPresence: Send a directed presence to <JID> and using [type] and [status] if provided.'), None),
+ 'presence': (self.command_presence, _('Usage: /presence <JID> [type] [status]\nPresence: Send a directed presence to <JID> and using [type] and [status] if provided.'), self.completion_presence),
'rawxml': (self.command_rawxml, _('Usage: /rawxml\nRawXML: Send a custom xml stanza.'), None),
+ 'set_plugin': (self.command_set_plugin, _("Usage: /set_plugin <plugin> <option> [value]\nSet Plugin: Set the value of the option in a plugin configuration file."), self.completion_set_plugin),
}
self.key_func = {
@@ -1208,8 +1209,25 @@ class Core(object):
log.debug(_("Could not send directed presence:\n") + traceback.format_exc())
def completion_status(self, the_input):
+ """
+ Completion of /status
+ """
return the_input.auto_completion([status for status in possible_show], ' ')
+ def completion_presence(self, the_input):
+ """
+ Completion of /presence
+ """
+ text = the_input.get_text()
+ args = text.split()
+ n = len(args)
+ if text.endswith(' '):
+ n += 1
+ if n == 2:
+ return the_input.auto_completion([contact.bare_jid for contact in roster.get_contacts()], '')
+ elif n == 3:
+ return the_input.auto_completion([status for status in possible_show], '')
+
def command_load(self, arg):
"""
/load <plugin>
@@ -1296,6 +1314,7 @@ class Core(object):
self.xmpp.plugin['xep_0030'].get_items(jid=server, block=False, callback=list_tab.on_muc_list_item_received)
def command_theme(self, arg):
+ """/theme <theme name>"""
args = arg.split()
if len(args) == 1:
self.command_set('theme %s' % (args[0],))
@@ -1304,6 +1323,23 @@ class Core(object):
self.information(warning, 'Warning')
self.refresh_window()
+ def completion_theme(self, the_input):
+ """ Completion for /theme"""
+ themes_dir = config.get('themes_dir', '')
+ themes_dir = themes_dir or\
+ os.path.join(os.environ.get('XDG_DATA_HOME') or\
+ os.path.join(os.environ.get('HOME'), '.local', 'share'),
+ 'poezio', 'themes')
+ try:
+ names = os.listdir(themes_dir)
+ except OSError as e:
+ log.debug(_('Completion failed: %s') % e)
+ return
+ theme_files = [name[:-3] for name in names if name.endswith('.py')]
+ if not 'default' in theme_files:
+ theme_files.append('default')
+ return the_input.auto_completion(theme_files, '')
+
def command_win(self, arg):
"""
/win <number>
@@ -1375,6 +1411,41 @@ class Core(object):
the_input.auto_completion(serv_list, '')
return True
+ def completion_bookmark(self, the_input):
+ """Completion for /bookmark"""
+ txt = the_input.get_text()
+ args = common.shell_split(txt)
+ n = len(args)
+ if txt.endswith(' '):
+ n += 1
+
+ if len(args) == 1:
+ jid = JID('')
+ else:
+ jid = JID(args[1])
+ if jid.server and (jid.resource or jid.full.endswith('/')):
+ tab = self.get_tab_by_name(jid.bare, tabs.MucTab)
+ nicks = [tab.own_nick] if tab else []
+ default = os.environ.get('USER') if os.environ.get('USER') else 'poezio'
+ nick = config.get('default_nick', '')
+ if not nick:
+ if not default in nicks:
+ nicks.append(default)
+ else:
+ if not nick in nicks:
+ nicks.append(nick)
+ jids_list = ['%s/%s' % (jid.bare, nick) for nick in nicks]
+ return the_input.auto_completion(jids_list, '')
+ muc_list = [tab.get_name() for tab in self.tabs if isinstance(tab, tabs.MucTab)]
+ return the_input.auto_completion(muc_list, '')
+
+ def completion_version(self, the_input):
+ """Completion for /version"""
+ n = len(the_input.get_text().split())
+ if n > 2 or (n == 2 and the_input.get_text().endswith(' ')):
+ return
+ return the_input.auto_completion([contact.bare_jid for contact in roster.get_contacts()], '')
+
def completion_list(self, the_input):
muc_serv_list = []
for tab in self.tabs: # TODO, also from an history
@@ -1519,6 +1590,77 @@ class Core(object):
msg = "%s=%s" % (option, value)
self.information(msg, 'Info')
+ def completion_server_cycle(self, the_input):
+ """Completion for /server_cycle"""
+ txt = the_input.get_text()
+ args = txt.split()
+ n = len(args)
+ if txt.endswith(' '):
+ n += 1
+ if n == 2:
+ serv_list = []
+ for tab in self.tabs:
+ if isinstance(tab, tabs.MucTab):
+ serv = JID(tab.get_name()).server
+ if not serv in serv_list:
+ serv_list.append(serv)
+ return the_input.auto_completion(serv_list, ' ')
+
+ def completion_set(self, the_input):
+ """Completion for /set"""
+ txt = the_input.get_text()
+ args = txt.split()
+ n = len(args)
+ if txt.endswith(' '):
+ n += 1
+ if n == 2:
+ return the_input.auto_completion(config.options('Poezio'), '')
+ elif n == 3:
+ return the_input.auto_completion([config.get(args[1], '')], '')
+
+ def command_set_plugin(self, arg):
+ """
+ /set_plugin <plugin> <option> [value]
+ """
+ args = arg.split()
+ if len(args) != 3 and len(args) != 2:
+ self.command_help('set_plugin')
+ return
+ plugin_name = args[0]
+ if not plugin_name in self.plugin_manager.plugins:
+ return
+ plugin = self.plugin_manager.plugins[plugin_name]
+ option = args[1]
+ if len(args) == 3:
+ value = args[2]
+ else:
+ value = ''
+ plugin.config.set_and_save(option, value, plugin_name)
+ if not plugin.config.write():
+ self.core.information('Could not save the plugin config', 'Error')
+ return
+ msg = "%s=%s" % (option, value)
+ self.information(msg, 'Info')
+
+ def completion_set_plugin(self, the_input):
+ """Completion for /set_plugin"""
+ txt = the_input.get_text()
+ args = txt.split()
+ n = len(args)
+ if txt.endswith(' '):
+ n += 1
+
+ if n == 2:
+ return the_input.auto_completion(list(self.plugin_manager.plugins.keys()), '')
+ elif n == 3:
+ if not args[1] in self.plugin_manager.plugins:
+ return
+ return the_input.auto_completion(self.plugin_manager.plugins[args[1]].config.options(args[1]), '')
+ elif n == 4:
+ if not args[1] in self.plugin_manager.plugins:
+ return
+ return the_input.auto_completion([self.plugin_manager.plugins[args[1]].config.get(args[2], '', args[1])], ' ')
+
def close_tab(self, tab=None):
"""
Close the given tab. If None, close the current one
diff --git a/src/plugin.py b/src/plugin.py
index 4dd88697..92adbc4b 100644
--- a/src/plugin.py
+++ b/src/plugin.py
@@ -1,24 +1,27 @@
import os
-from configparser import ConfigParser
+from configparser import RawConfigParser
import config
import inspect
import traceback
class PluginConfig(config.Config):
- def __init__(self, filename):
- ConfigParser.__init__(self)
- self.__config_file__ = filename
+ def __init__(self, filename, module_name):
+ self.file_name = filename
+ self.module_name = module_name
+ RawConfigParser.__init__(self, None)
self.read()
def read(self):
"""Read the config file"""
- ConfigParser.read(self, self.__config_file__)
+ RawConfigParser.read(self, self.file_name)
+ if not self.has_section(self.module_name):
+ self.add_section(self.module_name)
def write(self):
"""Write the config to the disk"""
try:
- fp = open(self.__config_file__, 'w')
- ConfigParser.write(self, fp)
+ fp = open(self.file_name, 'w')
+ RawConfigParser.write(self, fp)
fp.close()
return True
except IOError:
@@ -59,7 +62,7 @@ class BasePlugin(object, metaclass=SafetyMetaclass):
SafetyMetaclass.core = core
self.plugin_manager = plugin_manager
conf = os.path.join(plugins_conf_dir, self.__module__+'.cfg')
- self.config = PluginConfig(conf)
+ self.config = PluginConfig(conf, self.__module__)
self.init()
def init(self):
diff --git a/src/plugin_manager.py b/src/plugin_manager.py
index fe4d2b7e..437d8ee2 100644
--- a/src/plugin_manager.py
+++ b/src/plugin_manager.py
@@ -99,12 +99,15 @@ class PluginManager(object):
def add_tab_command(self, module_name, tab_type, name, handler, help, completion=None):
commands = self.tab_commands[module_name]
t = tab_type.__name__
+ if name in tab_type.plugin_commands:
+ return
if not t in commands:
commands[t] = []
commands[t].append((name, handler, help, completion))
+ tab_type.plugin_commands[name] = (handler, help, completion)
for tab in self.core.tabs:
if isinstance(tab, tab_type):
- tab.add_plugin_command(name, handler, help, completion)
+ tab.update_commands()
def del_tab_command(self, module_name, tab_type, name):
commands = self.tab_commands[module_name]
diff --git a/src/tabs.py b/src/tabs.py
index 472a15fa..feb4be37 100644
--- a/src/tabs.py
+++ b/src/tabs.py
@@ -31,6 +31,7 @@ import singleton
import xhtml
import weakref
import timed_events
+import os
import multiuserchat as muc
@@ -222,17 +223,10 @@ class Tab(object):
def on_input(self, key):
pass
- def add_plugin_command(self, name, handler, help, completion=None):
- if name in self.plugin_commands or name in self.commands:
- return
- self.plugin_commands[name] = (handler, help, completion)
- self.commands[name] = (handler, help, completion)
- self.update_commands()
-
def update_commands(self):
for c in self.plugin_commands:
if not c in self.commands:
- self.commands[name] = self.plugin_commands[c]
+ self.commands[c] = self.plugin_commands[c]
def on_lose_focus(self):
"""
@@ -310,6 +304,8 @@ class ChatTab(Tab):
_("""Usage: /say <message>\nSay: Just send the message.
Useful if you want your message to begin with a '/'."""), None)
self.commands['xhtml'] = (self.command_xhtml, _("Usage: /xhtml <custom xhtml>\nXHTML: Send custom XHTML."), None)
+ self.commands['clear'] = (self.command_clear,
+ _('Usage: /clear\nClear: Clear the current buffer.'), None)
self.chat_state = None
self.update_commands()
@@ -364,6 +360,15 @@ class ChatTab(Tab):
self.refresh()
msg.send()
+ def command_clear(self, args):
+ """
+ /clear
+ """
+ self._text_buffer.messages = []
+ self.text_win.rebuild_everything(self._text_buffer)
+ self.refresh()
+ self.core.doupdate()
+
def send_chat_state(self, state, always_send=False):
"""
Send an empty chatstate message
@@ -467,27 +472,67 @@ class MucTab(ChatTab):
self.key_func['M-u'] = self.scroll_user_list_down
self.key_func['M-y'] = self.scroll_user_list_up
# commands
- self.commands['ignore'] = (self.command_ignore, _("Usage: /ignore <nickname> \nIgnore: Ignore a specified nickname."), None)
+ self.commands['ignore'] = (self.command_ignore, _("Usage: /ignore <nickname> \nIgnore: Ignore a specified nickname."), self.completion_ignore)
self.commands['unignore'] = (self.command_unignore, _("Usage: /unignore <nickname>\nUnignore: Remove the specified nickname from the ignore list."), self.completion_unignore)
- self.commands['kick'] = (self.command_kick, _("Usage: /kick <nick> [reason]\nKick: Kick the user with the specified nickname. You also can give an optional reason."), None)
- self.commands['role'] = (self.command_role, _("Usage: /role <nick> <role> [reason]\nRole: Set the role of an user. Roles can be: none, visitor, participant, moderator. You also can give an optional reason."), None)
- self.commands['affiliation'] = (self.command_affiliation, _("Usage: /affiliation <nick> <affiliation> [reason]\nAffiliation: Set the affiliation of an user. Affiliations can be: none, member, admin, owner. You also can give an optional reason."), None)
+ self.commands['kick'] = (self.command_kick, _("Usage: /kick <nick> [reason]\nKick: Kick the user with the specified nickname. You also can give an optional reason."), self.completion_ignore)
+ self.commands['role'] = (self.command_role, _("Usage: /role <nick> <role> [reason]\nRole: Set the role of an user. Roles can be: none, visitor, participant, moderator. You also can give an optional reason."), self.completion_role)
+ self.commands['affiliation'] = (self.command_affiliation, _("Usage: /affiliation <nick> <affiliation> [reason]\nAffiliation: Set the affiliation of an user. Affiliations can be: none, member, admin, owner. You also can give an optional reason."), self.completion_affiliation)
self.commands['topic'] = (self.command_topic, _("Usage: /topic <subject>\nTopic: Change the subject of the room."), self.completion_topic)
- self.commands['query'] = (self.command_query, _('Usage: /query <nick> [message]\nQuery: Open a private conversation with <nick>. This nick has to be present in the room you\'re currently in. If you specified a message after the nickname, it will immediately be sent to this user.'), None)
+ self.commands['query'] = (self.command_query, _('Usage: /query <nick> [message]\nQuery: Open a private conversation with <nick>. This nick has to be present in the room you\'re currently in. If you specified a message after the nickname, it will immediately be sent to this user.'), self.completion_ignore)
self.commands['part'] = (self.command_part, _("Usage: /part [message]\nPart: Disconnect from a room. You can specify an optional message."), None)
self.commands['close'] = (self.command_close, _("Usage: /close [message]\nClose: Disconnect from a room and close the tab. You can specify an optional message if you are still connected."), None)
- self.commands['nick'] = (self.command_nick, _("Usage: /nick <nickname>\nNick: Change your nickname in the current room."), None)
+ self.commands['nick'] = (self.command_nick, _("Usage: /nick <nickname>\nNick: Change your nickname in the current room."), self.completion_nick)
self.commands['recolor'] = (self.command_recolor, _('Usage: /recolor\nRecolor: Re-assign a color to all participants of the current room, based on the last time they talked. Use this if the participants currently talking have too many identical colors.'), None)
self.commands['cycle'] = (self.command_cycle, _('Usage: /cycle [message]\nCycle: Leave the current room and rejoin it immediately.'), None)
- self.commands['info'] = (self.command_info, _('Usage: /info <nickname>\nInfo: Display some information about the user in the MUC: its/his/her role, affiliation, status and status message.'), None)
+ self.commands['info'] = (self.command_info, _('Usage: /info <nickname>\nInfo: Display some information about the user in the MUC: its/his/her role, affiliation, status and status message.'), self.completion_ignore)
self.commands['configure'] = (self.command_configure, _('Usage: /configure\nConfigure: Configure the current room, through a form.'), None)
self.commands['version'] = (self.command_version, _('Usage: /version <jid or nick>\nVersion: Get the software version of the given JID or nick in room (usually its XMPP client and Operating System).'), None)
self.commands['names'] = (self.command_names, _('Usage: /names\nNames: Get the list of the users in the room, and the list of the people assuming the different roles.'), None)
- self.commands['clear'] = (self.command_clear,
- _('Usage: /clear\nClear: Clear the current buffer.'), None)
self.resize()
self.update_commands()
+ def completion_nick(self, the_input):
+ """Completion for /nick"""
+ nicks = [os.environ.get('USER'), config.get('default_nick', ''), self.core.get_bookmark_nickname(self.get_name())]
+ while nicks.count(''):
+ nicks.remove('')
+ return the_input.auto_completion(nicks, '')
+
+ def completion_ignore(self, the_input):
+ """Completion for /ignore"""
+ userlist = [user.nick for user in self.users]
+ userlist.remove(self.own_nick)
+ return the_input.auto_completion(userlist, '')
+
+ def completion_role(self, the_input):
+ """Completion for /role"""
+ text = the_input.get_text()
+ args = common.shell_split(text)
+ n = len(args)
+ if text.endswith(' '):
+ n += 1
+ if n == 2:
+ userlist = [user.nick for user in self.users]
+ userlist.remove(self.own_nick)
+ return the_input.auto_completion(userlist, '')
+ elif n == 3:
+ possible_roles = ['none', 'visitor', 'participant', 'moderator']
+ return the_input.auto_completion(possible_roles, '')
+
+ def completion_affiliation(self, the_input):
+ """Completion for /affiliation"""
+ text = the_input.get_text()
+ args = common.shell_split(text)
+ n = len(args)
+ if text.endswith(' '):
+ n += 1
+ if n == 2:
+ userlist = [user.nick for user in self.users]
+ return the_input.auto_completion(userlist, '')
+ elif n == 3:
+ possible_affiliations = ['none', 'member', 'admin', 'owner']
+ return the_input.auto_completion(possible_affiliations, '')
+
def scroll_user_list_up(self):
self.user_win.scroll_up()
self.user_win.refresh(self.users)
@@ -533,15 +578,6 @@ class MucTab(ChatTab):
self.core.xmpp.plugin['xep_0045'].configureRoom(self.get_name(), form)
self.core.close_tab()
- def command_clear(self, args):
- """
- /clear
- """
- self._text_buffer.messages = []
- self.text_win.rebuild_everything(self._text_buffer)
- self.refresh()
- self.core.doupdate()
-
def command_cycle(self, arg):
if self.joined:
muc.leave_groupchat(self.core.xmpp, self.get_name(), self.own_nick, arg)
@@ -705,10 +741,9 @@ class MucTab(ChatTab):
else:
if len(args) > 1:
msg = ' '+args[1]
- self.core.information("-%s-" % msg)
else:
msg = ''
- self.command_role(args[0]+ ' none'+msg)
+ self.command_role('"'+args[0]+ '" none'+msg)
def command_role(self, arg):
"""
@@ -1457,8 +1492,8 @@ class RosterInfoTab(Tab):
self.commands['groupadd'] = (self.command_groupadd, _("Usage: /groupadd <jid> <group>\nAdd the given JID to the given group."), self.completion_groupadd)
self.commands['groupremove'] = (self.command_groupremove, _("Usage: /groupremove <jid> <group>\nRemove the given JID from the given group."), self.completion_groupremove)
self.commands['remove'] = (self.command_remove, _("Usage: /remove [jid]\nRemove: Remove the specified JID from your roster. This wil unsubscribe you from its presence, cancel its subscription to yours, and remove the item from your roster."), self.completion_remove)
- self.commands['export'] = (self.command_export, _("Usage: /export [/path/to/file]\nExport: Export your contacts into /path/to/file if specified, or $HOME/poezio_contacts if not."), None)
- self.commands['import'] = (self.command_import, _("Usage: /import [/path/to/file]\nImport: Import your contacts from /path/to/file if specified, or $HOME/poezio_contacts if not."), None)
+ self.commands['export'] = (self.command_export, _("Usage: /export [/path/to/file]\nExport: Export your contacts into /path/to/file if specified, or $HOME/poezio_contacts if not."), self.completion_file)
+ self.commands['import'] = (self.command_import, _("Usage: /import [/path/to/file]\nImport: Import your contacts from /path/to/file if specified, or $HOME/poezio_contacts if not."), self.completion_file)
self.commands['clear_infos'] = (self.command_clear_infos, _("Usage: /clear_infos\nClear Infos: Use this command to clear the info buffer."), None)
self.resize()
self.update_commands()
@@ -1481,6 +1516,30 @@ class RosterInfoTab(Tab):
not self.input.help_message:
self.complete_commands(self.input)
+ def completion_file(self, the_input):
+ """
+ Completion for /import and /export
+ """
+ text = the_input.get_text()
+ args = text.split()
+ n = len(args)
+ if n == 1:
+ home = os.getenv('HOME') or '/'
+ return the_input.auto_completion([home, '/tmp'], '')
+ else:
+ the_path = text[text.index(' ')+1:]
+ try:
+ names = os.listdir(the_path)
+ except:
+ names = []
+ end_list = []
+ for name in names:
+ value = os.path.join(the_path, name)
+ if not name.startswith('.'):
+ end_list.append(value)
+
+ return the_input.auto_completion(end_list, '')
+
def command_clear_infos(self, arg):
"""
/clear_infos