diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client.py | 40 | ||||
-rw-r--r-- | src/common.py | 95 | ||||
-rw-r--r-- | src/config.py | 55 | ||||
-rw-r--r-- | src/connection.py | 76 | ||||
-rw-r--r-- | src/gui.py | 290 |
5 files changed, 401 insertions, 155 deletions
diff --git a/src/client.py b/src/client.py index dfc31f3c..3e6d4fef 100644 --- a/src/client.py +++ b/src/client.py @@ -17,6 +17,10 @@ # You should have received a copy of the GNU General Public License # along with Poezio. If not, see <http://www.gnu.org/licenses/>. +""" +Starting point of poezio. Launches both the Connection and Gui +""" + import sys # disable any printout (this would mess the display) @@ -26,46 +30,26 @@ sys.stderr = open('errors', 'w') from connection import Connection from multiuserchat import MultiUserChat from config import config -from handler import Handler from gui import Gui from curses import initscr -import curses -import threading from common import exception_handler import signal -signal.signal(signal.SIGINT, signal.SIG_IGN) +signal.signal(signal.SIGINT, signal.SIG_IGN) # ignore ctrl-c sys.excepthook = exception_handler -class Client(object): - """ - Main class - Just read some configuration and instantiate the classes - """ - def __init__(self): - self.handler = Handler() - - self.resource = config.get('resource', 'poezio') - self.server = config.get('server', 'louiz.org') - self.connection = Connection(self.server, self.resource) - self.connection.start() - self.stdscr = initscr() - self.gui = Gui(self.stdscr, MultiUserChat(self.connection.client)) - - def launch(self): - """ - launch the gui - """ - self.gui.main_loop(self.stdscr) - def main(): """ main function """ - client = Client() - client.launch() - sys.exit() + resource = config.get('resource', 'poezio') + server = config.get('server', 'louiz.org') + connection = Connection(server, resource) + connection.start() + stdscr = initscr() + gui = Gui(stdscr, MultiUserChat(connection.client)) + gui.main_loop(stdscr) if __name__ == '__main__': main() diff --git a/src/common.py b/src/common.py index 1c119c1e..1dabcc06 100644 --- a/src/common.py +++ b/src/common.py @@ -30,7 +30,9 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -__doc__ = "various useful functions" +""" +various useful functions +""" import base64 import os @@ -40,11 +42,18 @@ import subprocess import curses import traceback import sys +import select +import errno -def debug(s): - f = open("debug", 'a') - f.write(s+'\n') - f.close() +def debug(string): + """ + Print a string in a file. + Useful since debuging cannot be displayed on screen because it's + a CLI software + """ + fdes = open("debug", 'a') + fdes.write(string+'\n') + fdes.close() def exception_handler(type_, value, trace): """ @@ -57,19 +66,25 @@ def exception_handler(type_, value, trace): sys.exit(2) def get_base64_from_file(path): + """ + Convert the content of a file to base64 + """ if not os.path.isfile(path): return (None, None, "File does not exist") size = os.path.getsize(path) if size > 16384: return (None, None,"File is too big") - fd = open(path, 'rb') - data = fd.read() + fdes = open(path, 'rb') + data = fdes.read() encoded = base64.encodestring(data) sha1 = hashlib.sha1(data).hexdigest() mime_type = mimetypes.guess_type(path)[0] return (encoded, mime_type, sha1) def get_output_of_command(command): + """ + Runs a command and returns its output + """ try: child_stdin, child_stdout = os.popen2(command) except ValueError: @@ -99,18 +114,16 @@ def is_in_path(command, return_abs_path=False): pass return False -distro_info = { +DISTRO_INFO = { 'Arch Linux': '/etc/arch-release', 'Aurox Linux': '/etc/aurox-release', 'Conectiva Linux': '/etc/conectiva-release', 'CRUX': '/usr/bin/crux', - 'Debian GNU/Linux': '/etc/debian_release', 'Debian GNU/Linux': '/etc/debian_version', 'Fedora Linux': '/etc/fedora-release', 'Gentoo Linux': '/etc/gentoo-release', 'Linux from Scratch': '/etc/lfs-release', 'Mandrake Linux': '/etc/mandrake-release', - 'Slackware Linux': '/etc/slackware-release', 'Slackware Linux': '/etc/slackware-version', 'Solaris/Sparc': '/etc/release', 'Source Mage': '/etc/sourcemage_version', @@ -122,52 +135,53 @@ distro_info = { # so Redhat is the last 'Redhat Linux': '/etc/redhat-release' } + +def temp_failure_retry(func, *args, **kwargs): + """ + workaround for a temporary and specific failure + """ + while True: + try: + return func(*args, **kwargs) + except (os.error, IOError, select.error), ex: + if ex.errno == errno.EINTR: + continue + else: + raise + def get_os_info(): - if os.name == 'nt': # could not happen, but... - ver = sys.getwindowsversion() - ver_format = ver[3], ver[0], ver[1] - win_version = { - (1, 4, 0): '95', - (1, 4, 10): '98', - (1, 4, 90): 'ME', - (2, 4, 0): 'NT', - (2, 5, 0): '2000', - (2, 5, 1): 'XP', - (2, 5, 2): '2003', - (2, 6, 0): 'Vista', - (2, 6, 1): '7', - } - if ver_format in win_version: - os_info = 'Windows' + ' ' + win_version[ver_format] - else: - os_info = 'Windows' - return os_info - elif os.name == 'posix': + """ + Returns a detailed and well formated string containing + informations about the operating system + """ + if os.name == 'posix': executable = 'lsb_release' params = ' --description --codename --release --short' full_path_to_executable = is_in_path(executable, return_abs_path = True) if full_path_to_executable: command = executable + params - p = subprocess.Popen([command], shell=True, stdin=subprocess.PIPE, - stdout=subprocess.PIPE, close_fds=True) - p.wait() - output = temp_failure_retry(p.stdout.readline).strip() + process = subprocess.Popen([command], shell=True, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + close_fds=True) + process.wait() + output = temp_failure_retry(process.stdout.readline).strip() # some distros put n/a in places, so remove those output = output.replace('n/a', '').replace('N/A', '') return output # lsb_release executable not available, so parse files - for distro_name in distro_info: - path_to_file = distro_info[distro_name] + for distro_name in DISTRO_INFO: + path_to_file = DISTRO_INFO[distro_name] if os.path.exists(path_to_file): if os.access(path_to_file, os.X_OK): # the file is executable (f.e. CRUX) # yes, then run it and get the first line of output. text = get_output_of_command(path_to_file)[0] else: - fd = open(path_to_file) - text = fd.readline().strip() # get only first line - fd.close() + fdes = open(path_to_file) + text = fdes.readline().strip() # get only first line + fdes.close() if path_to_file.endswith('version'): # sourcemage_version and slackware-version files # have all the info we need (name and version of distro) @@ -179,7 +193,8 @@ def get_os_info(): path_to_file.endswith('arch-release'): # file doesn't have version text = distro_name - elif path_to_file.endswith('lfs-release'): # file just has version + elif path_to_file.endswith('lfs-release'): + # file just has version text = distro_name + ' ' + text os_info = text.replace('\n', '') return os_info diff --git a/src/config.py b/src/config.py index 8237c7b7..1b9ba287 100644 --- a/src/config.py +++ b/src/config.py @@ -18,6 +18,11 @@ # You should have received a copy of the GNU General Public License # along with Poezio. If not, see <http://www.gnu.org/licenses/>. +""" +Defines the global config instance, used to get or set (and save) values +from/to the config file +""" + from ConfigParser import RawConfigParser, NoOptionError from os import environ, makedirs from shutil import copy2 @@ -39,7 +44,7 @@ class Config(RawConfigParser): The type of default defines the type returned """ - try: + try: if type(default) == int: res = self.getint(option) elif type(default) == float: @@ -52,45 +57,65 @@ class Config(RawConfigParser): return default return res - def _get(self, option): + def __get(self, option): + """ + facility for RawConfigParser.get + """ return RawConfigParser.get(self, self.defsection, option) def getstr(self, option): - return self._get(option) + """ + get a value and returns it as a string + """ + return self.__get(option) def getint(self, option): + """ + get a value and returns it as an int + """ try: - return int(self._get(option)) + return int(self.__get(option)) except ValueError: return -1 def getfloat(self, option): - return float(self._get(option)) + """ + get a value and returns it as a float + """ + return float(self.__get(option)) def getboolean(self, option): + """ + get a value and returns it as a boolean + """ return RawConfigParser.getboolean(self, self.defsection, option) - def set(self, option, value): - RawConfigParser.set(self, self.defsection, option, value) - def save(self): - f = open(self.file_name, "w") - RawConfigParser.write(self, f) - f.close() + """ + save the configuration in the file + """ + fdes = open(self.file_name, "w") + RawConfigParser.write(self, fdes) + fdes.close() def set_and_save(self, option, value): - self.set(option, value) + """ + set the value in the configuration then save it + to the file + """ + RawConfigParser.set(self, self.defsection, option, value) self.save() +# creates the configuration directory if it doesn't exist +# and copy the default config in it CONFIG_HOME = environ.get("XDG_CONFIG_HOME") if not CONFIG_HOME: CONFIG_HOME = environ.get('HOME')+'/.config' CONFIG_PATH = CONFIG_HOME + '/poezio/' - try: makedirs(CONFIG_PATH) copy2('../data/default_config.cfg', CONFIG_PATH+'poezio.cfg') -except:pass - +except OSError: + pass config = Config(CONFIG_PATH+'poezio.cfg') diff --git a/src/connection.py b/src/connection.py index a95191ba..b7326291 100644 --- a/src/connection.py +++ b/src/connection.py @@ -17,6 +17,10 @@ # You should have received a copy of the GNU General Public License # along with Poezio. If not, see <http://www.gnu.org/licenses/>. +""" +Defines the Connection class +""" + from gettext import (bindtextdomain, textdomain, bind_textdomain_codeset, gettext as _) @@ -35,7 +39,6 @@ from logging import logger from handler import Handler from common import exception_handler import threading -import thread class Connection(threading.Thread): """ @@ -77,74 +80,117 @@ class Connection(threading.Thread): {'host': config.get("proxy_server", ""), 'port': config.get("proxy_port", 1080), 'user': config.get("proxy_user", ""), - 'password': config.get("proxy_password", "") + 'password': config.get("proxy_password", + "") }) else: return self.client.connect((server, port)) def authenticate(self, anon=True): + """ + Authenticate to the server + """ if anon: try: self.client.auth(None, "", self.resource) return True except TypeError: - self.handler.emit('error', msg=_('Error: Could not authenticate. Please make sure the server you chose (%s) supports anonymous authentication' % (config.get('server', '')))) + self.handler.emit('error', msg=_('Error: Could not \ + authenticate. Please make sure the server you chose \ + (%s) supports anonymous authentication' + % (config.get('server', '')))) return None else: - log.error('Non-anonymous connections not handled currently') + logger.error('Non-anonymous connections not handled currently') return None def register_handlers(self): """ - register handlers from xmpppy signals + registers handlers from xmpppy signals """ - self.client.RegisterHandler('iq', self.on_get_time, typ='get', ns="urn:xmpp:time") - self.client.RegisterHandler('iq', self.on_get_version, typ='get', ns=xmpp.NS_VERSION) + self.client.RegisterHandler('iq', self.on_get_time, typ='get', + ns="urn:xmpp:time") + self.client.RegisterHandler('iq', self.on_get_version, typ='get', + ns=xmpp.NS_VERSION) self.client.RegisterHandler('presence', self.handler_presence) self.client.RegisterHandler('message', self.handler_message) def error_message(self, stanza): + """ + handles the error messages + """ room_name = stanza.getFrom().getStripped() - self.handler.emit('error-message', room=room_name, error=stanza.getTag('error'), msg=stanza.getError()) + self.handler.emit('error-message', room=room_name, + error=stanza.getTag('error'), + msg=stanza.getError()) raise xmpp.protocol.NodeProcessed def handler_presence(self, connection, presence): + """ + handles the presence messages + """ + if not connection: + return if presence.getType() == 'error': self.error_message(presence) return fro = presence.getFrom() - to = presence.getAttr('to') - if fro == to: # own presence + toj = presence.getAttr('to') + if fro == toj: # own presence self.online = 2 - self.jid = to + self.jid = toj self.handler.emit('on-connected', jid=fro) return self.handler.emit('room-presence', stanza=presence) raise xmpp.protocol.NodeProcessed def handler_delayed_message(self, connection, message): + """ + handles the delayed messages + These are received when we join a muc and we are sent the + recent history + """ + if not connection: + return self.handler.emit('room-delayed-message', stanza=message) raise xmpp.protocol.NodeProcessed def handler_message(self, connection, message): + """ + handles the common messages + """ + if not connection: + return if message.getType() == 'error': self.error_message(message) return self.handler.emit('room-message', stanza=message) raise xmpp.protocol.NodeProcessed - def handler_error(self, connection, error): - pass - def process(self, timeout=10): + """ + Main connection loop + It just waits for something to process (something is received + or something has to be sent) + """ if self.online: self.client.Process(timeout) else: - log.warning('disconnecting...') + logger.warning('disconnecting...') sys.exit() def on_get_version(self, connection, iq): + """ + Handles the iq requesting our software version + """ + if not connection: + return self.handler.emit('send-version', iq_obj=iq) def on_get_time(self, connection, iq): + """ + handles the iq requesting our time + """ + if not connection: + return self.handler.emit('send-time', iq_obj=iq) @@ -17,12 +17,9 @@ # You should have received a copy of the GNU General Public License # along with Poezio. If not, see <http://www.gnu.org/licenses/>. -from common import debug - from gettext import (bindtextdomain, textdomain, bind_textdomain_codeset, gettext as _) - bindtextdomain('poezio') textdomain('poezio') bind_textdomain_codeset('poezio', 'utf-8') @@ -32,17 +29,11 @@ locale.setlocale(locale.LC_ALL, '') import sys import curses -import xmpp from datetime import datetime -from time import (altzone, daylight, gmtime, localtime, mktime, strftime, - time as time_time, timezone, tzname) -from calendar import timegm import common from handler import Handler -from logging import logger -from random import randrange from config import config from window import Window from user import User @@ -50,13 +41,13 @@ from room import Room class Gui(object): """ - Graphical user interface using ncurses + User interface using ncurses """ def __init__(self, stdscr=None, muc=None): self.room_number = 0 self.init_curses(stdscr) self.stdscr = stdscr - self.rooms = [Room('Info', '', self.next_room_number())] # current_room is self.rooms[0] + self.rooms = [Room('Info', '', self.next_room_number())] self.window = Window(stdscr) self.window.new_room(self.current_room()) self.window.refresh(self.rooms) @@ -65,26 +56,73 @@ class Gui(object): self.commands = { 'help': (self.command_help, _('OLOL, this is SOOO recursive')), - 'join': (self.command_join, _('Usage: /join [room_name][/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 password to join the room.\nExamples:\n/join room@server.tld\n/join room@server.tld/John\n/join /me_again\n/join\n/join room@server.tld/my_nick password\n/join / pass')), - 'quit': (self.command_quit, _('Usage: /quit\nQuit: Just disconnect from the server and exit poezio.')), - 'exit': (self.command_quit, _('Usage: /exit\nExit: Just disconnect from the server and exit poezio.')), - 'next': (self.rotate_rooms_right, _('Usage: /next\nNext: Go to the next room.')), - 'n': (self.rotate_rooms_right, _('Usage: /n\nN: Go to the next room.')), - 'prev': (self.rotate_rooms_left, _('Usage: /prev\nPrev: Go to the previous room.')), - 'p': (self.rotate_rooms_left, _('Usage: /p\nP: Go to the previous room.')), - 'win': (self.command_win, _('Usage: /win <number>\nWin: Go to the specified room.')), - 'w': (self.command_win, _('Usage: /w <number>\nW: Go to the specified room.')), - 'part': (self.command_part, _('Usage: /part [message]\nPart: disconnect from a room. You can specify an optional message.')), - 'show': (self.command_show, _(u'Usage: /show <availability> [status]\nShow: Change your availability and (optionaly) your status. The <availability> argument is one of "avail, available, ok, here, chat, away, afk, dnd, busy, xa" and the optional [message] argument will be your status message')), - 'away': (self.command_away, _('Usage: /away [message]\nAway: Sets your availability to away and (optional) sets your status message. This is equivalent to "/show away [message]"')), - 'busy': (self.command_busy, _('Usage: /busy [message]\nBusy: Sets your availability to busy and (optional) sets your status message. This is equivalent to "/show busy [message]"')), - 'avail': (self.command_avail, _('Usage: /avail [message]\nAvail: Sets your availability to available and (optional) sets your status message. This is equivalent to "/show available [message]"')), - 'available': (self.command_avail, _('Usage: /available [message]\nAvailable: Sets your availability to available and (optional) sets your status message. This is equivalent to "/show available [message]"')), - '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 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)')), - 'set': (self.command_set, _('Usage: /set <option> [value]\nSet: Sets 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>.')), - 'kick': (self.command_kick, _('Usage: /kick <nick> [reason]\nKick: Kick the user with the specified nickname. You also can give an optional reason.')), - 'topic': (self.command_topic, _('Usage: /topic <subject>\nTopic: Change the subject of the room')), - 'nick': (self.command_nick, _('Usage: /nick <nickname>\nNick: Change your nickname in the current room')) + 'join': (self.command_join, _("""Usage: /join [room_name][/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 password + to join the room.\nExamples:\n/join room@server.tld\n/join room@server.tld/ + John\n/join /me_again\n/join\n/join room@server.tld/my_nick password\n/join / + pass""")), + 'quit': (self.command_quit, _("""Usage: /quit\nQuit: Just + disconnect from the server and exit poezio.""")), + 'exit': (self.command_quit, _("""Usage: /exit\nExit: Just + disconnect from the server and exit poezio.""")), + 'next': (self.rotate_rooms_right, _("""Usage: /next\nNext: + Go to the next room.""")), + 'n': (self.rotate_rooms_right, _("""Usage: /n\nN: Go to the + next room.""")), + 'prev': (self.rotate_rooms_left, _("""Usage: /prev\nPrev: + Go to the previous room.""")), + 'p': (self.rotate_rooms_left, _("""Usage: /p\nP: Go to the + previous room.""")), + 'win': (self.command_win, _("""Usage: /win <number>\nWin: Go + to the specified room.""")), + 'w': (self.command_win, _("""Usage: /w <number>\nW: Go to + the specified room.""")), + 'ignore': (self.command_ignore, _("""Usage: /ignore <nickname> +\Ignore: Ignore a specified nickname.""")), + 'unignore': (self.command_unignore, _("""Usage: /unignore + <nickname>\Unignore: Remove the specified nickname from the ignore list.""")), + 'part': (self.command_part, _("""Usage: /part [message]\n +Part: disconnect from a room. You can specify an optional message.""")), + 'show': (self.command_show, _("""Usage: /show <availability> + [status]\nShow: Change your availability and (optionaly) your status. + The <availability> argument is one of "avail, available, ok, here, + chat, away, afk, dnd, busy, xa" and the optional [message] argument + will be your status message""")), + 'away': (self.command_away, _("""Usage: /away [message]\nAway: + Sets your availability to away and (optional) sets your status message. + This is equivalent to '/show away [message]'""")), + 'busy': (self.command_busy, _("""Usage: /busy [message]\nBusy: + Sets your availability to busy and (optional) sets your status message. + This is equivalent to '/show busy [message]'""")), + 'avail': (self.command_avail, _("""Usage: /avail [message]\n +Avail: Sets your availability to available and (optional) sets your status + message. This is equivalent to '/show available [message]'""")), + 'available': (self.command_avail, _("""Usage: /available + [message]\nAvailable: Sets your availability to available and (optional) + sets your status message. This is equivalent to '/show + available [message]'""")), + '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 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)""")), + 'set': (self.command_set, _("""Usage: /set <option> + [value]\nSet: Sets 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>.""")), + 'kick': (self.command_kick, _("""Usage: /kick <nick> + [reason]\nKick: Kick the user with the specified nickname. + You also can give an optional reason.""")), + 'topic': (self.command_topic, _("""Usage: /topic <subject> +\nTopic: Change the subject of the room""")), + 'nick': (self.command_nick, _("""Usage: /nick <nickname> +\nNick: Change your nickname in the current room""")) } self.key_func = { @@ -112,6 +150,9 @@ class Gui(object): self.handler.connect('error', self.information) def main_loop(self, stdscr): + """ + main loop waiting for the user to press a key + """ while 1: stdscr.leaveok(1) curses.doupdate() @@ -146,27 +187,40 @@ class Gui(object): self.window.do_command(key) def next_room_number(self): + """ + Increments the room number and returns the new number + """ nb = self.room_number self.room_number += 1 return nb def current_room(self): + """ + returns the current room, the one we are viewing + """ return self.rooms[0] def get_room_by_name(self, name): - for room in self.rooms: - if room.name == name: - return room - return None + """ + returns the room that has this name + """ + for room in self.rooms: + if room.name == name: + return room + return None def init_curses(self, stdscr): + """ + ncurses initialization + """ curses.start_color() curses.noecho() # curses.cbreak() # curses.raw() curses.use_default_colors() stdscr.keypad(True) - curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE) + curses.init_pair(1, curses.COLOR_WHITE, + curses.COLOR_BLUE) curses.init_pair(2, curses.COLOR_BLUE, -1) curses.init_pair(3, curses.COLOR_RED, -1) # Admin curses.init_pair(4, curses.COLOR_BLUE, -1) # Participant @@ -175,23 +229,39 @@ class Gui(object): curses.init_pair(7, curses.COLOR_GREEN, -1) curses.init_pair(8, curses.COLOR_MAGENTA, -1) curses.init_pair(9, curses.COLOR_YELLOW, -1) - curses.init_pair(10, curses.COLOR_WHITE, curses.COLOR_CYAN) # current room - curses.init_pair(11, curses.COLOR_WHITE, curses.COLOR_BLUE) # normal room - curses.init_pair(12, curses.COLOR_WHITE, curses.COLOR_MAGENTA) # new message room - curses.init_pair(13, curses.COLOR_WHITE, curses.COLOR_RED) # highlight room - curses.init_pair(14, curses.COLOR_WHITE, curses.COLOR_YELLOW) - curses.init_pair(15, curses.COLOR_WHITE, curses.COLOR_GREEN) + curses.init_pair(10, curses.COLOR_WHITE, + curses.COLOR_CYAN) # current room + curses.init_pair(11, curses.COLOR_WHITE, + curses.COLOR_BLUE) # normal room + curses.init_pair(12, curses.COLOR_WHITE, + curses.COLOR_MAGENTA) # new message room + curses.init_pair(13, curses.COLOR_WHITE, + curses.COLOR_RED) # highlight room + curses.init_pair(14, curses.COLOR_WHITE, + curses.COLOR_YELLOW) + curses.init_pair(15, curses.COLOR_WHITE, + curses.COLOR_GREEN) def reset_curses(self): - curses.echo() + """ + Reset terminal capabilities to what they were before ncurses + init + """ + curses.echo() curses.nocbreak() curses.endwin() def on_connected(self, jid): + """ + When we are connected authentification confirmation is received + """ self.information(_("Welcome on Poezio \o/!")) self.information(_("Your JID is %s") % jid) def join_room(self, room, nick): + """ + join the specified room (muc), using the specified nick + """ r = Room(room, nick, self.next_room_number()) self.current_room().set_color_state(11) if self.current_room().nb == 0: @@ -207,28 +277,46 @@ class Gui(object): self.window.refresh(self.rooms) def auto_completion(self): + """ + Called when Tab is pressed, complete the nickname in the input + """ self.window.input.auto_completion(self.current_room().users) def rotate_rooms_right(self, args=None): + """ + rotate the rooms list to the right + """ self.current_room().set_color_state(11) self.rooms.append(self.rooms.pop(0)) self.window.refresh(self.rooms) def rotate_rooms_left(self, args=None): + """ + rotate the rooms list to the right + """ self.current_room().set_color_state(11) self.rooms.insert(0, self.rooms.pop()) self.window.refresh(self.rooms) def room_error(self, room, error, msg): - r = self.get_room_by_name(room) + """ + Display the error on the room window + """ + room = self.get_room_by_name(room) code = error.getAttr('code') typ = error.getAttr('type') body = error.getTag('text').getData() - self.add_info(r, _('Error: %(code)s-%(msg)s: %(body)s' % {'msg':msg, 'code':code, 'body':body})) + self.add_info(r, _('Error: %(code)s-%(msg)s: %(body)s' % + {'msg':msg, 'code':code, 'body':body})) if code == '401': - self.add_info(r, _('To provide a password in order to join the room, type "/join / password" (replace "password" by the real password)')) + self.add_info(room, _("""To provide a password in order + to join the room, type "/join / password" (replace "password" + by the real password)""")) def room_message(self, stanza, date=None): + """ + Display the message on the room window + """ delay_tag = stanza.getTag('delay', namespace='urn:xmpp:delay') if delay_tag and not date: delayed = True @@ -247,9 +335,11 @@ class Gui(object): subject = stanza.getSubject() if subject: if nick_from: - self.add_info(room, _("%(nick)s changed the subject to: %(subject)s") % {'nick':nick_from, 'subject':subject}, date) + self.add_info(room, _("""%(nick)s changed the subject to: + %(subject)s""") % {'nick':nick_from, 'subject':subject}, date) else: - self.add_info(room, _("The subject is: %(subject)s") % {'subject':subject}, date) + self.add_info(room, _("The subject is: %(subject)s") % + {'subject':subject}, date) room.topic = subject.encode('utf-8').replace('\n', '|') if room == self.current_room(): self.window.topic_win.refresh(room.topic) @@ -262,6 +352,10 @@ class Gui(object): curses.doupdate() def room_presence(self, stanza): + """ + Display the presence on the room window and update the + presence information of the concerned user + """ if len(sys.argv) > 1: self.information(str(stanza)) from_nick = stanza.getFrom().getResource() @@ -276,28 +370,35 @@ class Gui(object): status = stanza.getStatus() role = stanza.getRole() if not room.joined: # user in the room BEFORE us. - room.users.append(User(from_nick, affiliation, show, status, role)) + room.users.append(User(from_nick, affiliation, show, status, + role)) if from_nick.encode('utf-8') == room.own_nick: room.joined = True self.add_info(room, _("Your nickname is %s") % (from_nick)) else: - self.add_info(room, _("%s is in the room") % (from_nick.encode('utf-8'))) + self.add_info(room, _("%s is in the room") % + (from_nick.encode('utf-8'))) else: change_nick = stanza.getStatusCode() == '303' kick = stanza.getStatusCode() == '307' user = room.get_user_by_name(from_nick) # New user if not user: - room.users.append(User(from_nick, affiliation, show, status, role)) + room.users.append(User(from_nick, affiliation, + show, status, role)) hide_exit_join = config.get('hide_exit_join', -1) if hide_exit_join != 0: - self.add_info(room, _('%(nick)s joined the room %(roomname)s') % {'nick':from_nick, 'roomname': room.name}) + self.add_info(room, _("""%(nick)s joined the + room %(roomname)s""") % {'nick':from_nick, 'roomname': room.name}) # nick change elif change_nick: if user.nick == room.own_nick: room.own_nick = stanza.getNick().encode('utf-8') user.change_nick(stanza.getNick()) - self.add_info(room, _('%(old_nick)s is now known as %(new_nick)s') % {'old_nick':from_nick, 'new_nick':stanza.getNick()}) + self.add_info(room, + _('%(old)s is now known as %(new)s') % + {'old':from_nick, + 'new':stanza.getNick()}) # kick elif kick: room.users.remove(user) @@ -312,14 +413,20 @@ class Gui(object): if from_nick == room.own_nick: # we are kicked room.disconnect() if by: - self.add_info(room, _('You have been kicked by %(by)s. Reason: %(reason)s') % {'by':by, 'reason':reason}) + self.add_info(room, _("""You have been kicked by + %(by)s. Reason: %(reason)s""") % {'by':by, 'reason':reason}) else: - self.add_info(room, _('You have been kicked. Reason: %s') % (reason)) + self.add_info(room, _("""You have been + kicked. Reason: %s""") % (reason)) else: if by: - self.add_info(room, _('%(nick)s has been kicked by %(by)s. Reason: %(reason)s') % {'nick':from_nick, 'by':by, 'reason':reason}) + self.add_info(room, _("""%(nick)s has been kicked + by %(by)s. Reason: %(reason)s""") % + {'nick':from_nick, 'by':by, 'reason':reason}) else: - self.add_info(room, _('%(nick)s has been kicked. Reason: %(reason)s') % {'nick':from_nick, 'reason':reason}) + self.add_info(room, _("""%(nick)s has been kicked. + Reason: %(reason)s""") % + {'nick':from_nick, 'reason':reason}) # user quit elif status == 'offline' or role == 'none': room.users.remove(user) @@ -360,6 +467,9 @@ class Gui(object): curses.doupdate() def add_message(self, room, nick_from, body, date=None, delayed=False): + """ + Just add a message + """ if not date: date = datetime.now() color = room.add_message(nick_from, body, date) @@ -370,6 +480,9 @@ class Gui(object): self.window.info_win.refresh(self.rooms, self.current_room()) def execute(self): + """ + Execute the /command or just send the line on the current room + """ line = self.window.input.get_text() self.window.input.clear_text() self.window.input.refresh() @@ -390,6 +503,9 @@ class Gui(object): curses.doupdate() def command_help(self, args): + """ + /help <command_name> + """ room = self.current_room() if len(args) == 0: msg = _('Available commands are:') @@ -404,6 +520,9 @@ class Gui(object): self.add_info(room, msg) def command_win(self, args): + """ + /win <number> + """ if len(args) != 1: self.command_help(['win']) return @@ -425,6 +544,9 @@ class Gui(object): self.window.refresh(self.rooms) def command_kick(self, args): + """ + /kick <nick> [reason] + """ if len(args) < 1: self.command_help(['kick']) return @@ -439,6 +561,9 @@ class Gui(object): self.muc.eject_user(roomname, 'kick', nick, reason) def command_join(self, args): + """ + /join [room][/nick] [password] + """ password = None if len(args) == 0: r = self.current_room() @@ -475,6 +600,9 @@ class Gui(object): r.users = [] def command_bookmark(self, args): + """ + /bookmark [room][/nick] + """ nick = None if len(args) == 0: room = self.current_room() @@ -506,6 +634,9 @@ class Gui(object): config.set_and_save('rooms', bookmarked+':'+res) def command_set(self, args): + """ + /set <option> [value] + """ if len(args) != 2 and len(args) != 1: self.command_help(['set']) return @@ -520,6 +651,9 @@ class Gui(object): self.add_info(room, msg) def command_show(self, args): + """ + /show <status> [msg] + """ possible_show = {'avail':'None', 'available':'None', 'ok':'None', @@ -545,19 +679,49 @@ class Gui(object): if room.joined: self.muc.change_show(room.name, room.own_nick, show, msg) + def command_ignore(self, args): + """ + /ignore <nick> + """ + # TODO + if len(args) != 1: + self.command_help([ignore]) + return + + def command_unignore(self, args): + """ + /unignore <nick> + """ + # TODO + if len(args) != 1: + self.command_help([ignore]) + return + def command_away(self, args): + """ + /away [msg] + """ args.insert(0, 'away') self.command_show(args) def command_busy(self, args): + """ + /busy [msg] + """ args.insert(0, 'busy') self.command_show(args) def command_avail(self, args): + """ + /avail [msg] + """ args.insert(0, 'available') self.command_show(args) def command_part(self, args): + """ + /part [msg] + """ reason = None room = self.current_room() if room.name == 'Info': @@ -572,6 +736,9 @@ class Gui(object): self.window.refresh(self.rooms) def command_topic(self, args): + """ + /topic [new topic] + """ subject = ' '.join(args) room = self.current_room() if not room.joined or room.name == "Info": @@ -579,6 +746,9 @@ class Gui(object): self.muc.change_subject(room.name, subject) def command_nick(self, args): + """ + /nick <nickname> + """ if len(args) != 1: return nick = args[0] @@ -588,9 +758,15 @@ class Gui(object): self.muc.change_nick(room.name, nick) def information(self, msg): + """ + Displays an informational message in the "Info" room window + """ room = self.get_room_by_name("Info") self.add_info(room, msg) def command_quit(self, args): + """ + /quit + """ self.reset_curses() sys.exit() |