diff options
Diffstat (limited to 'src/core.py')
-rw-r--r-- | src/core.py | 150 |
1 files changed, 146 insertions, 4 deletions
diff --git a/src/core.py b/src/core.py index 8abaec63..1fb06b38 100644 --- a/src/core.py +++ b/src/core.py @@ -16,6 +16,7 @@ import threading import traceback from datetime import datetime +from inspect import getargspec import common import theming @@ -36,6 +37,8 @@ import windows import connection import timed_events +from plugin_manager import PluginManager + from data_forms import DataFormsTab from config import config, options from logger import logger @@ -46,6 +49,7 @@ from contact import Contact, Resource from text_buffer import TextBuffer from keyboard import read_char from theming import get_theme +from fifo import Fifo # http://xmpp.org/extensions/xep-0045.html#errorstatus ERROR_AND_STATUS_CODES = { @@ -76,6 +80,13 @@ class Core(object): """ User interface using ncurses """ + + # dict containing the name of the internal events + # used with the plugins, the key is the name of the event + # and the value is the number of arguments the handler must take + internal_events = { + 'enter': 2, + } def __init__(self): # All uncaught exception are given to this callback, instead # of being displayed on the screen and exiting the program. @@ -84,6 +95,7 @@ class Core(object): sys.excepthook = self.on_exception self.running = True self.xmpp = singleton.Singleton(connection.Connection) + self.remote_fifo = None # a unique buffer used to store global informations # that are displayed in almost all tabs, in an # information window. @@ -102,6 +114,7 @@ class Core(object): # a completion function, taking a Input as argument. Can be None) # The completion function should return True if a completion was # made ; False otherwise + self.plugin_manager = PluginManager(self) self.commands = { 'help': (self.command_help, '\_o< KOIN KOIN KOIN', self.completion_help), 'join': (self.command_join, _("Usage: /join [room_name][@server][/nick] [password]\nJoin: Join the specified room. You can specify a nickname after a slash (/). If no nickname is specified, you will use the default_nick in the configuration file. You can omit the room name: you will then join the room you\'re looking at (useful if you were kicked). You can also provide a room_name without specifying a server, the server of the room you're currently in will be used. You can also provide a password to join the room.\nExamples:\n/join room@server.tld\n/join room@server.tld/John\n/join room2\n/join /me_again\n/join\n/join room@server.tld/my_nick password\n/join / password"), self.completion_join), @@ -125,7 +138,8 @@ class Core(object): '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 reconnects in all the rooms in domain.'), None), '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 than the Up key.'), None), -# nope 'pubsub': (self.command_pubsub, _('Usage: /pubsub <domain>\nPubsub: Open a pubsub browser on the given domain'), 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), } self.key_func = { @@ -145,7 +159,6 @@ class Core(object): 'M-z': self.go_to_previous_tab, '^L': self.full_screen_redraw, 'M-j': self.go_to_room_number, -# 'M-c': self.coucou, } # Add handlers @@ -171,8 +184,13 @@ class Core(object): self.timed_events = set() - def coucou(self): - self.command_pubsub('pubsub.louiz.org') + self.connected_events = {} + self.autoload_plugins() + + def autoload_plugins(self): + plugins = config.get('plugins_autoload', '') + for plugin in plugins.split(): + self.plugin_manager.load(plugin) def start(self): """ @@ -200,6 +218,57 @@ class Core(object): )) self.refresh_window() + def connect(self, event, handler): + """ + Connect an handler to an internal event of poezio + (eg "enter pressed in a chattab") + """ + # Fail if the method doesn’t take at least the good number of arguments + # or if the event is unknown + if not event in self.internal_events \ + or len(getargspec(handler).args) < self.internal_events[event]: + return False + + module_name = handler.__module__ + if not event in self.connected_events: + self.connected_events[event] = {} + if not module_name in self.connected_events[event]: + self.connected_events[event][module_name] = [] + + self.connected_events[event][module_name].append(handler) + return True + + def run_event(self, event, **kwargs): + """ + Call the handlers associated with an event + """ + if event in self.connected_events: + for module in self.connected_events[event]: + for handler in self.connected_events[event][module]: + try: + handler(**kwargs) + except: + import traceback + tp = traceback.format_exc() + module_name = handler.__name__ + log.debug('ERROR: in plugin %s, \n%s' % (module_name, tp)) + + def disconnect(self, event, handler): + """ + Disconnect a handler from an event + """ + if not event in self.internal_events: + return False + + module_name = getmodule(handler).__name__ + if not event in self.connected_events: + return False + if not module_name in self.connected_events[event]: + return False + + self.connected_events[event][module_name].remove(handler) + return True + def resize_global_information_win(self): """ Resize the global_information_win only once at each resize. @@ -1124,6 +1193,28 @@ class Core(object): def completion_status(self, the_input): return the_input.auto_completion([status for status in possible_show], ' ') + def command_load(self, arg): + """ + /load <plugin> + """ + args = arg.split() + if len(args) != 1: + self.command_help('load') + return + filename = args[0] + self.plugin_manager.load(filename) + + def command_unload(self, arg): + """ + /unload <plugin> + """ + args = arg.split() + if len(args) != 1: + self.command_help('unload') + return + filename = args[0] + self.plugin_manager.unload(filename) + def command_message(self, arg): """ /message <jid> [message] @@ -1604,3 +1695,54 @@ class Core(object): if not self.running or self.background is True: return curses.doupdate() + + def send_message(self, msg): + """ + Function to use in plugins to send a message in the current conversation. + Returns False if the current tab is not a conversation tab + """ + if not isinstance(self.current_tab(), tabs.ChatTab): + return False + self.current_tab().command_say(msg) + return True + + def exec_command(self, command): + """ + Execute an external command on the local or a remote + machine, depending on the conf. For example, to open a link in a + browser, do exec_command("firefox http://poezio.eu"), + and this will call the command on the correct computer. + The remote execution is done by writing the command on a fifo. + That fifo has to be on the machine where poezio is running, and + accessible (through sshfs for example) from the local machine (where + poezio is not running). A very simple daemon reads on that fifo, + and executes any command that is read in it. + """ + command = '%s\n' % (command,) + if config.get('exec_remote', 'false') == 'true': + # We just write the command in the fifo + if not self.remote_fifo: + try: + self.remote_fifo = Fifo(os.path.join(config.get('remote_fifo_path', './'), 'poezio.fifo'), 'w') + except (OSError, IOError) as e: + self.information('Could not open fifo file for writing: %s' % (e,), 'Error') + return + try: + self.remote_fifo.write(command) + except (IOError) as e: + self.information('Could not execute [%s]: %s' % (command, e,), 'Error') + self.remote_fifo = None + else: + pass + + def get_conversation_messages(self): + """ + Returns a list of all the messages in the current chat. + If the current tab is not a ChatTab, returns None. + + Messages are namedtuples of the form + ('txt nick_color time str_time nickname user') + """ + if not isinstance(self.current_tab(), tabs.ChatTab): + return None + return self.current_tab().get_conversation_messages() |