From f4d4a205f136c6373c415657dd35b4f078f25c69 Mon Sep 17 00:00:00 2001 From: "louiz@4325f9fc-e183-4c21-96ce-0ab188b42d13" Date: Mon, 15 Nov 2010 11:59:09 +0000 Subject: [PATCH] a few renamings, and some other stuff --- CHANGELOG | 13 ++++-- README | 29 ++++++------ data/default_config.cfg | 11 +---- data/poezio.1 | 7 ++- launch.sh | 1 - src/{window.py => buffers.py} | 8 ++++ src/contact.py | 14 +++--- src/core.py | 4 +- src/handler.py | 83 ----------------------------------- src/logger.py | 26 +---------- src/message.py | 4 ++ src/multiuserchat.py | 2 +- src/singleton.py | 23 ---------- src/tab.py | 70 ++++++++++++++--------------- src/text_buffer.py | 4 ++ src/user.py | 6 +++ 16 files changed, 100 insertions(+), 205 deletions(-) rename src/{window.py => buffers.py} (99%) delete mode 100644 src/handler.py delete mode 100644 src/singleton.py diff --git a/CHANGELOG b/CHANGELOG index e033a32c..c0eee2b5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,15 +3,20 @@ For more detailed changelog, see the roadmap: http://codingteam.net/project/poezio/roadmap * Poezio 0.7 - dev -"Complete the MUC support" -- All poezio colors can be changed with a theme file -- /say command +"Roster" +- Introduce the roster +- One to one conversations +- Roster search +- Resizable mini-buffer displaying various informations +- All colors can be changed with a theme file +- /say and // commands - Warn user about publicly logged rooms - Possibility to limit the number of history messages received from MUC - auto-rejoin when kicked from a MUC - The number of lines available to scroll down is displayed - Possibility to use a modified nickname automatically when a nick is reserved -- A line indicates the new messages in the window +- A line separates the alread-read messages + from the new messages in a conversation - Information messages are more colored - bugfixes diff --git a/README b/README index 834c14ec..89667b19 100644 --- a/README +++ b/README @@ -10,24 +10,27 @@ Homepage: http://poezio.eu Forge Page: http://codingteam.net/projet/poezio -Poezio is a console Jabber/XMPP client. Its goal is to use anonymous +Poezio is a console Jabber/XMPP client. Its goal is to use anonymous connections to simply let the user join MultiUserChats. This way, the user doesn't have to create a Jabber account, exactly like people are using -IRC. Poezio's commands are designed to be (if possible) like IRC +IRC. Poezio's commands are designed to be (if possible) like IRC clients (weechat, irssi, etc). Since version 0.7, poezio can handle real Jabber accounts along with -roster and one to one conversation, making it a full-featured console +roster and one to one conversations, making it a full-featured console Jabber client, but still MultiUserChats-centered. +In the futur, poezio should implement at a 100% level all XEP related to +MUCs, especially XEP 0045. +All other IM-related XEP (wherever possible) should be implemented through +plugins or directly in poezio's core. ======================= Install ======================= You need python 3.0 or higher, and the SleekXMPP python library. -You can find my patched version at http://github.com/louiz/SleekXMPP until -my changes (required to properly run poezio) are merged upstream. +SleekXMPP can be found at http://github.com/fritzy/SleekXMPP. you can launch poezio with -sh launch.sh +./launch.sh or you can install it with (as root or with sudo) make install @@ -36,7 +39,7 @@ you can now simply launch `poezio' You can edit the config file (~/.config/poezio/poezio.cfg by default) or data/default_config.cfg (if you want to edit the config before the -first launch) +first launch). The default config file is fully commented. Please, see the online documentation for more information on installing, configuring or using poezio: @@ -66,10 +69,10 @@ Report a bug: http://codingteam.net/project/poezio/bugs/add Poezio is Free Software. (learn more: http://www.gnu.org/philosophy/free-sw.html) -Poezio is released under the Gnu GPLv3 license -Please read the COPYING file for details +Poezio is released under the Gnu GPLv3 license. +Please read the COPYING file for details. -The artwork logo is made by Gaëtan Ribémont and released under +The artwork logo was made by Gaëtan Ribémont and released under the Creative Commons BY license (http://creativecommons.org/licenses/by/2.0/) ======================= @@ -85,9 +88,3 @@ the Creative Commons BY license (http://creativecommons.org/licenses/by/2.0/) FlashCode (weechat dev) - Useful advices on how to use ncurses efficiently = Project = Gajim - send_vcard method and common.py - -======================= - Donate -======================= -If you're willing to thank me, or ask for an on-demand feature, please -see the page http://louiz.org/donate.html and contact me. diff --git a/data/default_config.cfg b/data/default_config.cfg index 5d1ddd5e..aaa607b2 100644 --- a/data/default_config.cfg +++ b/data/default_config.cfg @@ -17,7 +17,7 @@ port = 5222 resource = # the nick you will use when joining a room with no associated nick -# If this is empty, the $USER variable will be used +# If this is empty, the $USER environn. +""" +Define all the buffers. +A buffer is a little part of the screen, for example the input buffer, +the text bufferr, the roster buffer, etc. +A Tab (see tab.py) is composed of multiple Buffers +A buffer can also be called Window, even if it's not prefered. +""" + from gettext import (bindtextdomain, textdomain, bind_textdomain_codeset, gettext as _) from os.path import isfile diff --git a/src/contact.py b/src/contact.py index 1eb41f72..8341d4b8 100644 --- a/src/contact.py +++ b/src/contact.py @@ -15,12 +15,15 @@ # along with Poezio. If not, see . """ -Defines the Resource and Contact classes +Defines the Resource and Contact classes, which are used in +the roster """ -from sleekxmpp.xmlstream.stanzabase import JID + import logging log = logging.getLogger(__name__) +from sleekxmpp.xmlstream.stanzabase import JID + class Resource(object): """ Defines a roster item. @@ -80,8 +83,7 @@ class Contact(object): def get_highest_priority_resource(self): """ - There must be, at any time, at least ONE resource. - And they always should be ordered by priority. + Return the resource with the highest priority """ ret = None for resource in self._resources: @@ -94,8 +96,10 @@ class Contact(object): Called, for example, when a new resource get offline (the first, or any subsequent one) """ - # TODO sort by priority + def f(o): + return o.get_priority() self._resources.append(resource) + self._resources = sorted(self._resources, key=f, reverse=True) def remove_resource(self, resource): """ diff --git a/src/core.py b/src/core.py index 40d727b4..ce271613 100644 --- a/src/core.py +++ b/src/core.py @@ -38,9 +38,9 @@ log = logging.getLogger(__name__) import multiuserchat as muc from connection import connection -from handler import Handler from config import config from tab import MucTab, InfoTab, PrivateTab, RosterInfoTab, ConversationTab +from logger import logger from user import User from room import Room from roster import Roster, RosterGroup, roster @@ -822,6 +822,8 @@ class Core(object): body = message['body'] if body: date = date if delayed == True else None + if not delayed: + logger.groupchat(room_from, nick_from, body) self.add_message_to_text_buffer(room, body, date, nick_from) self.refresh_window() self.doupdate() diff --git a/src/handler.py b/src/handler.py deleted file mode 100644 index 350ea0ed..00000000 --- a/src/handler.py +++ /dev/null @@ -1,83 +0,0 @@ -# Copyright 2009, 2010 Erwan Briand -# Copyright 2010, Florent Le Coz - -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation version 3 of the License. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -from singleton import Singleton - -#Todo, it's not a singleton. Oh, also, remove-me - -class Handler(Singleton): - """ - This class is the global handler for the software's signals. - """ - __is_first_instance = True - - def __init__(self): - if Handler.__is_first_instance: - Handler.__is_first_instance = False - - self.__signals__ = { - - 'on-connected': list(), - # At the end of a successful connection process. - # emitted when presence confirmation is received - # Args: jid - - 'join-room': list(), - # Join a room. - # Args: room, nick - - 'room-presence': list(), - # A presence is received - # Args: the stanza object - - 'room-message': list(), - # A message is received - # Args: the stanza object - - 'private-message': list(), - # A message is received - # Args: the stanza object - - 'room-delayed-message': list(), - # A message is received - # Args: the stanza object - - 'send-version': list(), - # We send our version - # Args: the stanza we reply to - - 'send-time': list(), - # We send our time - # Args: the stanza we reply to - - 'error-message': list(), - # We send our time - # Args: the stanza we reply to - - 'error': list() - # We send our time - # Args: the stanza we reply to - } - - def connect(self, signal, func): - """Connect a function to a signal.""" - if func not in self.__signals__[signal]: - self.__signals__[signal].append(func) - - def emit(self, signal, **kwargs): - """Emit a signal.""" - if signal in self.__signals__: - for func in self.__signals__[signal]: - func(**kwargs) diff --git a/src/logger.py b/src/logger.py index 4cfb7793..a1479f9c 100644 --- a/src/logger.py +++ b/src/logger.py @@ -29,32 +29,10 @@ class Logger(object): """ def __init__(self):# , logfile, loglevel): self.logfile = config.get('logfile', 'logs') - self.loglevel = config.get('loglevel', 3) - # self.logfile = logfile - # self.loglevel = loglevel - def info(self, msg): - if self.logfile and self.loglevel >= 3: - fd = open(self.logfile, 'a') - fd.write(datetime.now().strftime("%H:%M:%S") + ' Info [' + msg + ']\n') - fd.close() - - def warning(self, msg): - if self.logfile and self.loglevel >= 2: - fd = open(self.logfile, 'a') - fd.write(datetime.now().strftime("%H:%M:%S") + ' Warning [' + msg + ']\n') - fd.close() - - def error(self, msg): - if self.logfile and self.loglevel >= 1: - fd = open(self.logfile, 'a') - fd.write(datetime.now().strftime("%H:%M:%S") + ' Error [' + msg + ']\n') - fd.close() - sys.exit(-1) - - def message(self, room, nick, msg): + def groupchat(self, room, nick, msg): """ - log the message in the appropriate room + log the message in the appropriate room's file """ if config.get('use_log', 'false') == 'false': return diff --git a/src/message.py b/src/message.py index 07526819..6cf7a3d3 100644 --- a/src/message.py +++ b/src/message.py @@ -14,6 +14,10 @@ # You should have received a copy of the GNU General Public License # along with Poezio. If not, see . +""" +Define the Message class +""" + from datetime import datetime class Message(object): diff --git a/src/multiuserchat.py b/src/multiuserchat.py index 3927fc38..0fc5cb3c 100644 --- a/src/multiuserchat.py +++ b/src/multiuserchat.py @@ -46,7 +46,7 @@ def change_show(xmpp, jid, own_nick, show, status): Change our 'Show' """ pres = xmpp.makePresence(pto='%s/%s' % (jid, own_nick)) - if show: # if show is None, don't put a tag. It means "online" + if show: # if show is None, don't put a tag. It means "available" pres['type'] = show if status: pres['status'] = status diff --git a/src/singleton.py b/src/singleton.py deleted file mode 100644 index 688d20f0..00000000 --- a/src/singleton.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright 2009, 2010 Erwan Briand -# Copyright 2010, Florent Le Coz - -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -class Singleton(object): - """ Canonic Singleton implementation. - """ - _instance = None - def __new__(cls, *args, **kwargs): - if cls._instance is None: - cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs) - return cls._instance diff --git a/src/tab.py b/src/tab.py index 5086735b..bef42be8 100644 --- a/src/tab.py +++ b/src/tab.py @@ -15,11 +15,11 @@ # along with Poezio. If not, see . """ -a Tab object is a way to organize various Window (see window.py) +a Tab object is a way to organize various Buffers (see buffers.py) around the screen at once. -A tab is then composed of multiple Window. -Each Tab object has different refresh() and resize() methods, defining of its -Window are displayed, etc +A tab is then composed of multiple Buffer. +Each Tab object has different refresh() and resize() methods, defining how its +Buffer are displayed, resized, etc """ MIN_WIDTH = 50 @@ -28,7 +28,7 @@ MIN_HEIGHT = 16 import logging log = logging.getLogger(__name__) -import window +import buffers import theme import curses import difflib @@ -145,9 +145,9 @@ class InfoTab(Tab): """ def __init__(self, stdscr, core, name): Tab.__init__(self, stdscr, core) - self.tab_win = window.GlobalInfoBar(1, self.width, self.height-2, 0, stdscr, self.visible) - self.text_win = window.TextWin(self.height-2, self.width, 0, 0, stdscr, self.visible) - self.input = window.Input(1, self.width, self.height-1, 0, stdscr, self.visible) + self.tab_win = buffers.GlobalInfoBar(1, self.width, self.height-2, 0, stdscr, self.visible) + self.text_win = buffers.TextWin(self.height-2, self.width, 0, 0, stdscr, self.visible) + self.input = buffers.Input(1, self.width, self.height-1, 0, stdscr, self.visible) self.name = name self.color_state = theme.COLOR_TAB_NORMAL @@ -209,14 +209,14 @@ class MucTab(Tab): """ Tab.__init__(self, stdscr, core) self._room = room - self.topic_win = window.Topic(1, self.width, 0, 0, stdscr, self.visible) - self.text_win = window.TextWin(self.height-4-self.core.information_win_size, (self.width//10)*9, 1, 0, stdscr, self.visible) - self.v_separator = window.VerticalSeparator(self.height-3, 1, 1, 9*(self.width//10), stdscr, self.visible) - self.user_win = window.UserList(self.height-3, (self.width//10), 1, 9*(self.width//10)+1, stdscr, self.visible) - self.info_header = window.MucInfoWin(1, (self.width//10)*9, self.height-3-self.core.information_win_size, 0, stdscr, self.visible) - self.info_win = window.TextWin(self.core.information_win_size, (self.width//10)*9, self.height-2-self.core.information_win_size, 0, stdscr, self.visible) - self.tab_win = window.GlobalInfoBar(1, self.width, self.height-2, 0, stdscr, self.visible) - self.input = window.MessageInput(1, self.width, self.height-1, 0, stdscr, self.visible) + self.topic_win = buffers.Topic(1, self.width, 0, 0, stdscr, self.visible) + self.text_win = buffers.TextWin(self.height-4-self.core.information_win_size, (self.width//10)*9, 1, 0, stdscr, self.visible) + self.v_separator = buffers.VerticalSeparator(self.height-3, 1, 1, 9*(self.width//10), stdscr, self.visible) + self.user_win = buffers.UserList(self.height-3, (self.width//10), 1, 9*(self.width//10)+1, stdscr, self.visible) + self.info_header = buffers.MucInfoWin(1, (self.width//10)*9, self.height-3-self.core.information_win_size, 0, stdscr, self.visible) + self.info_win = buffers.TextWin(self.core.information_win_size, (self.width//10)*9, self.height-2-self.core.information_win_size, 0, stdscr, self.visible) + self.tab_win = buffers.GlobalInfoBar(1, self.width, self.height-2, 0, stdscr, self.visible) + self.input = buffers.MessageInput(1, self.width, self.height-1, 0, stdscr, self.visible) def resize(self, stdscr): """ @@ -333,11 +333,11 @@ class PrivateTab(Tab): def __init__(self, stdscr, core, room): Tab.__init__(self, stdscr, core) self._room = room - self.text_win = window.TextWin(self.height-3-self.core.information_win_size, self.width, 0, 0, stdscr, self.visible) - self.info_header = window.PrivateInfoWin(1, self.width, self.height-3-self.core.information_win_size, 0, stdscr, self.visible) - self.info_win = window.TextWin(self.core.information_win_size, self.width, self.height-2-self.core.information_win_size, 0, stdscr, self.visible) - self.tab_win = window.GlobalInfoBar(1, self.width, self.height-2, 0, stdscr, self.visible) - self.input = window.MessageInput(1, self.width, self.height-1, 0, stdscr, self.visible) + self.text_win = buffers.TextWin(self.height-3-self.core.information_win_size, self.width, 0, 0, stdscr, self.visible) + self.info_header = buffers.PrivateInfoWin(1, self.width, self.height-3-self.core.information_win_size, 0, stdscr, self.visible) + self.info_win = buffers.TextWin(self.core.information_win_size, self.width, self.height-2-self.core.information_win_size, 0, stdscr, self.visible) + self.tab_win = buffers.GlobalInfoBar(1, self.width, self.height-2, 0, stdscr, self.visible) + self.input = buffers.MessageInput(1, self.width, self.height-1, 0, stdscr, self.visible) def resize(self, stdscr): Tab.resize(self, stdscr) @@ -420,12 +420,12 @@ class RosterInfoTab(Tab): self.name = "Roster" roster_width = self.width//2 info_width = self.width-roster_width-1 - self.v_separator = window.VerticalSeparator(self.height-2, 1, 0, roster_width, stdscr, self.visible) - self.tab_win = window.GlobalInfoBar(1, self.width, self.height-2, 0, stdscr, self.visible) - self.info_win = window.TextWin(self.height-2, info_width, 0, roster_width+1, stdscr, self.visible) - self.roster_win = window.RosterWin(self.height-2-3, roster_width, 0, 0, stdscr, self.visible) - self.contact_info_win = window.ContactInfoWin(3, roster_width, self.height-2-3, 0, stdscr, self.visible) - self.default_help_message = window.HelpText(1, self.width, self.height-1, 0, stdscr, self.visible, "Enter commands with “/”. “o”: toggle offline show") + self.v_separator = buffers.VerticalSeparator(self.height-2, 1, 0, roster_width, stdscr, self.visible) + self.tab_win = buffers.GlobalInfoBar(1, self.width, self.height-2, 0, stdscr, self.visible) + self.info_win = buffers.TextWin(self.height-2, info_width, 0, roster_width+1, stdscr, self.visible) + self.roster_win = buffers.RosterWin(self.height-2-3, roster_width, 0, 0, stdscr, self.visible) + self.contact_info_win = buffers.ContactInfoWin(3, roster_width, self.height-2-3, 0, stdscr, self.visible) + self.default_help_message = buffers.HelpText(1, self.width, self.height-1, 0, stdscr, self.visible, "Enter commands with “/”. “o”: toggle offline show") self.input = self.default_help_message self.set_color_state(theme.COLOR_TAB_NORMAL) @@ -491,7 +491,7 @@ class RosterInfoTab(Tab): '/' is pressed, we enter "input mode" """ curses.curs_set(1) - self.input = window.CommandInput(1, self.width, self.height-1, 0, self.default_help_message, self.visible, "", self.reset_help_message, self.execute_slash_command) + self.input = buffers.CommandInput(1, self.width, self.height-1, 0, self.default_help_message, self.visible, "", self.reset_help_message, self.execute_slash_command) self.input.do_command("/") # we add the slash def reset_help_message(self, _=None): @@ -551,7 +551,7 @@ class RosterInfoTab(Tab): in it. """ curses.curs_set(1) - self.input = window.CommandInput(1, self.width, self.height-1, 0, self.default_help_message, self.visible, "[Search]", self.on_search_terminate, self.on_search_terminate, self.set_roster_filter) + self.input = buffers.CommandInput(1, self.width, self.height-1, 0, self.default_help_message, self.visible, "[Search]", self.on_search_terminate, self.on_search_terminate, self.set_roster_filter) return True def set_roster_filter(self, txt): @@ -578,12 +578,12 @@ class ConversationTab(Tab): self._text_buffer = text_buffer self.color_state = theme.COLOR_TAB_NORMAL self._name = jid # a conversation tab is linked to one specific full jid OR bare jid - self.text_win = window.TextWin(self.height-4-self.core.information_win_size, self.width, 1, 0, stdscr, self.visible) - self.upper_bar = window.ConversationStatusMessageWin(1, self.width, 0, 0, stdscr, self.visible) - self.info_header = window.ConversationInfoWin(1, self.width, self.height-3-self.core.information_win_size, 0, stdscr, self.visible) - self.info_win = window.TextWin(self.core.information_win_size, self.width, self.height-2-self.core.information_win_size, 0, stdscr, self.visible) - self.tab_win = window.GlobalInfoBar(1, self.width, self.height-2, 0, stdscr, self.visible) - self.input = window.MessageInput(1, self.width, self.height-1, 0, stdscr, self.visible) + self.text_win = buffers.TextWin(self.height-4-self.core.information_win_size, self.width, 1, 0, stdscr, self.visible) + self.upper_bar = buffers.ConversationStatusMessageWin(1, self.width, 0, 0, stdscr, self.visible) + self.info_header = buffers.ConversationInfoWin(1, self.width, self.height-3-self.core.information_win_size, 0, stdscr, self.visible) + self.info_win = buffers.TextWin(self.core.information_win_size, self.width, self.height-2-self.core.information_win_size, 0, stdscr, self.visible) + self.tab_win = buffers.GlobalInfoBar(1, self.width, self.height-2, 0, stdscr, self.visible) + self.input = buffers.MessageInput(1, self.width, self.height-1, 0, stdscr, self.visible) def resize(self, stdscr): Tab.resize(self, stdscr) diff --git a/src/text_buffer.py b/src/text_buffer.py index 203a966e..43bf008f 100644 --- a/src/text_buffer.py +++ b/src/text_buffer.py @@ -14,6 +14,10 @@ # You should have received a copy of the GNU General Public License # along with Poezio. If not, see . +""" +Define the TextBuffer class +""" + from message import Message from datetime import datetime import theme diff --git a/src/user.py b/src/user.py index eb8587f8..ba39a8fb 100644 --- a/src/user.py +++ b/src/user.py @@ -14,6 +14,11 @@ # You should have received a copy of the GNU General Public License # along with Poezio. If not, see . +""" +Define the user class. +An user is a MUC participant, not a roster contact (see contact.py) +""" + from random import randrange, choice from config import config from datetime import timedelta, datetime @@ -26,6 +31,7 @@ ROLE_DICT = { 'participant':2, 'moderator':3 } + class User(object): """ keep trace of an user in a Room