summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client.py40
-rw-r--r--src/common.py95
-rw-r--r--src/config.py55
-rw-r--r--src/connection.py76
-rw-r--r--src/gui.py290
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)
diff --git a/src/gui.py b/src/gui.py
index f0b06af0..5f368266 100644
--- a/src/gui.py
+++ b/src/gui.py
@@ -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()