on peut parler, recevoir des messages, rejoindre des salons (/join), changer...

on peut parler, recevoir des messages, rejoindre des salons (/join), changer de tab (/next, /prev), mais ca blink et c'est nul
parent 6a2917fc
......@@ -17,17 +17,21 @@
# You should have received a copy of the GNU General Public License
# along with Poezio. If not, see <http://www.gnu.org/licenses/>.
import sys
#sys.stderr = open('errors', 'w') # never print anyerror
#sys.stdout = open('salut', 'w')
from connection import Connection
from multiuserchat import MultiUserChat
from config import config
from handler import Handler
from gui import Gui
from curses import wrapper, initscr
class Client(object):
"""
Main class
Do what should be done automatically by the Client:
join the rooms at startup, for example
Just read some configuration and instantiate the classes
"""
def __init__(self):
self.handler = Handler()
......@@ -35,22 +39,15 @@ class Client(object):
self.resource = config.get('resource')
self.server = config.get('server')
self.connection = Connection(self.server, self.resource)
self.connection.start()
self.muc = MultiUserChat(self.connection.client)
self.gui = Gui()
self.rooms = config.get('rooms').split(':')
import time
time.sleep(1) # remove
for room in self.rooms:
self.handler.emit('join-room', room = room.split('/')[0], nick=room.split('/')[1])
while 1:
self.connection.process()
self.stdscr = initscr()
self.connection.start()
self.gui = Gui(self.stdscr, MultiUserChat(self.connection.client))
def main():
client = Client()
client.gui.main_loop(client.stdscr)
sys.exit()
if __name__ == '__main__':
main()
......@@ -28,7 +28,8 @@ from handler import Handler
class Connection(Thread):
"""
Handles all network transactions
Receives everything from Jabber and emits the
appropriate signals
"""
def __init__(self, server, resource):
Thread.__init__(self)
......@@ -47,6 +48,7 @@ class Connection(Thread):
def run(self):
"""
run in a thread
connect to server
"""
self.client = xmpp.Client(self.server, debug=[])
......@@ -59,7 +61,6 @@ class Connection(Thread):
self.client.sendInitPresence()
self.online = 1 # 2 when confirmation of auth is received
self.register_handlers()
self.muc = MultiUserChat(self.client)
while 1:
self.process()
......@@ -75,38 +76,25 @@ class Connection(Thread):
return None
def register_handlers(self):
"""
register handlers from xmpppy signals
"""
self.client.RegisterHandler('message', self.handler_message)
self.client.RegisterHandler('presence', self.handler_presence)
self.client.RegisterHandler('iq', self.handler_iq)
def handler_message(self, connection, message):
# body = message.getTag('body').getData()
# fro = str(message.getAttr('from'))
# room, nick = fro.split('/')
# print 'Message from %s in %s :' % (nick, room), body
self.handler.emit('xmpp-message-handler', message=message)
self.client.RegisterHandler('iq', self.handler_iq)
def handler_presence(self, connection, presence):
affil, role = u'', u''
fro = presence.getFrom()#presence.getAttr('from')
room_from, nick_from = fro.getStripped().encode('utf-8'), fro.getResource().encode('utf-8')
fro = presence.getFrom()
to = presence.getAttr('to')
room_to, nick_to = to.getStripped().encode('utf-8'), to.getResource().encode('utf-8')
if fro == to: # own presence
self.online = 2
self.jid = to
print 'Authentification confirmation received!'
self.handler.emit('on-connected')
return
for x in presence.getTags('x'):
if x.getTag('item'):
affil = x.getTagAttr('item', 'affiliation').encode('utf-8')
role = x.getTagAttr('item', 'role').encode('utf-8')
break
# print '[%s] in room {%s}. (%s - %s)'% (nick_from, room_from, affil, role)
self.handler.emit('xmpp-presence-handler', presence=presence)
self.handler.emit('room-presence', stanza=presence)
def send_join_room(self, room, nick):
self.handler.emit('join-room', room=room, nick=nick)
def handler_message(self, connection, message):
self.handler.emit('room-message', stanza=message)
def handler_iq(self, connection, iq):
pass
......@@ -116,7 +104,7 @@ class Connection(Thread):
self.client.Process(timeout)
else:
log.warning('disconnecting...')
sys.exit()
if __name__ == '__main__':
resource = config.get('resource')
......
......@@ -22,222 +22,162 @@ import curses
from curses import textpad
import locale
from datetime import datetime
locale.setlocale(locale.LC_ALL, '')
code = locale.getpreferredencoding()
import sys
from connection import *
from window import Window
class Win(object):
def __init__(self, height, width, y, x, parent_win):
self._resize(height, width, y, x, parent_win)
def _resize(self, height, width, y, x, parent_win):
self.height, self.width, self.x, self.y = height, width, x, y
try:
self.win = parent_win.subwin(height, width, y, x)
except:
pass
class UserList(Win):
def __init__(self, height, width, y, x, parent_win):
Win.__init__(self, height, width, y, x, parent_win)
self.win.attron(curses.color_pair(2))
self.win.vline(0, 0, curses.ACS_VLINE, self.height)
self.win.attroff(curses.color_pair(2))
self.list = []
def add_user(self, name):
"""
add an user to the list
"""
self.list.append(name)
def refresh(self):
self.win.clear()
self.win.attron(curses.color_pair(2))
self.win.vline(0, 0, curses.ACS_VLINE, self.height)
self.win.attroff(curses.color_pair(2))
y = 0
for name in self.list:
self.win.addstr(y, 1, name)
y += 1
self.win.refresh()
def resize(self, height, width, y, x, stdscr):
self._resize(height, width, y, x, stdscr)
self.refresh()
class Info(Win):
def __init__(self, height, width, y, x, parent_win):
Win.__init__(self, height, width, y, x, parent_win)
self.txt = ""
# self.win.bkgd(ord('p'), curses.COLOR_BLUE)
def set_info(self, text):
self.txt = text
self.refresh()
def resize(self, height, width, y, x, stdscr):
self._resize(height, width, y, x, stdscr)
self.refresh()
def refresh(self):
self.win.clear()
try:
self.win.addstr(0, 0, self.txt + " "*(self.width-len(self.txt)-1)
, curses.color_pair(1))
except:
pass
class TextWin(Win):
def __init__(self, height, width, y, x, parent_win):
Win.__init__(self, height, width, y, x, parent_win)
self.lines = []
def add_line(self, time, nick, text):
self.lines.append((time, nick, text))
self.refresh()
def refresh(self):
self.win.clear()
y = 0
for line in self.lines[-self.height:]:
self.win.addstr(y, 0, line[0] + " : " + line[1] + ": " + line[2])
y += 1
self.win.refresh()
def resize(self, height, width, y, x, stdscr):
self._resize(height, width, y, x, stdscr)
self.refresh()
class Input(Win):
class User(object):
"""
keep trace of an user in a Room
"""
def __init__(self, height, width, y, x, stdscr):
Win.__init__(self, height, width, y, x, stdscr)
self.input = curses.textpad.Textbox(self.win)
self.input.stripspaces = False
self.input.insert_mode = True
self.txt = ''
def resize(self, height, width, y, x, stdscr):
self._resize(height, width, y, x, stdscr)
self.input = curses.textpad.Textbox(self.win)
self.input.insert_mode = True
self.input.stripspaces = False
self.win.clear()
self.win.addstr(self.txt)
def do_command(self, key):
self.input.do_command(key)
# self.win.refresh()
# self.text = self.input.gather()
def __init__(self, nick, affiliation, show, status, role):
self.update(affiliation, show, status, role)
self.change_nick(nick)
# def insert_char(self, key):
# if self.insert:
# self.text = self.text[:self.pos]+key+self.text[self.pos:]
# else:
# self.text = self.text[:self.pos]+key+self.text[self.pos+1:]
# self.pos += 1
# pass
def update(self, affiliation, show, status, role):
self.affiliation = None
self.show = None
self.status = status
self.role = role
def get_text(self):
return self.input.gather()
def change_nick(self, nick):
self.nick = nick
def save_text(self):
self.txt = self.input.gather()
# self.win.clear()
# self.win.addstr(self.txt)
def refresh(self):
# self.win.clear()
# self.win.addstr(self.text)
# self.win.move(0, len(self.text)-1)
self.win.refresh()
def clear_text(self):
self.win.clear()
self.txt = ''
self.pos = 0
self.refresh()
class Tab(object):
class Room(object):
"""
The whole "screen" that can be seen at once in the terminal.
It contains an userlist, an input zone and a chat zone, all
related to one single chat room.
"""
def __init__(self, stdscr, name='info'):
"""
name is the name of the Tab, and it's also
the JID of the chatroom.
A particular tab is the "Info" tab which has no
name (None). This info tab should be unique.
The stdscr should be passed to know the size of the
terminal
"""
def __init__(self, name, nick):
self.name = name
self.size = (self.height, self.width) = stdscr.getmaxyx()
self.own_nick = nick
self.joined = False # false until self presence is received
self.users = []
self.lines = [] # (time, nick, msg) or (time, info)
self.topic = ''
self.user_win = UserList(self.height-3, self.width/7, 1, 6*(self.width/7), stdscr)
self.topic_win = Info(1, self.width, 0, 0, stdscr)
self.info_win = Info(1, self.width, self.height-2, 0, stdscr)
self.text_win = TextWin(self.height-3, (self.width/7)*6, 1, 0, stdscr)
self.input = Input(1, self.width, self.height-1, 0, stdscr)
def add_message(self, nick, msg):
self.lines.append((datetime.now(), nick, msg.encode('utf-8')))
self.info_win.set_info(name)
# debug
self.refresh()
def add_info(self, info):
""" info, like join/quit/status messages"""
self.lines.append((datetime.now(), info.encode('utf-8')))
def resize(self, stdscr):
def on_presence(self, stanza, nick):
"""
Resize the whole tabe. i.e. all its sub-windows
"""
self.size = (self.height, self.width) = stdscr.getmaxyx()
self.user_win.resize(self.height-3, self.width/7, 1, 6*(self.width/7), stdscr)
self.topic_win.resize(1, self.width, 0, 0, stdscr)
self.info_win.resize(1, self.width, self.height-2, 0, stdscr)
self.text_win.resize(self.height-3, (self.width/7)*6, 1, 0, stdscr)
self.input.resize(1, self.width, self.height-1, 0, stdscr)
self.refresh()
def refresh(self):
self.text_win.add_line("fion", "fion", "refresh")
self.text_win.refresh()
self.user_win.refresh()
self.topic_win.refresh()
self.info_win.refresh()
self.input.refresh()
def do_command(self, key):
self.input.do_command(key)
# self.input.save_text()
self.input.refresh()
affiliation = stanza.getAffiliation()
show = stanza.getShow()
status = stanza.getStatus()
role = stanza.getRole()
if not self.joined:
self.users.append(User(nick, affiliation, show, status, role))
if nick == self.own_nick:
self.joined = True
self.add_info("%s is in the room" % (nick))
return
change_nick = stanza.getStatusCode() == '303'
for user in self.users:
if user.nick == nick:
if change_nick:
user.change_nick(stanza.getNick())
self.add_info('%s is now known as %s' % (nick, stanza.getNick()))
return
if status == 'offline':
self.users.remove(user)
self.add_info('%s has left the room' % (nick))
return
user.update(affiliation, show, status, role)
self.add_info('%s, status : %s, %s, %s, %s' % (nick, affiliation, role, show, status))
return
self.users.append(User(nick, affiliation, show, status, role))
self.add_info('%s joined the room %s' % (nick, self.name))
class Gui(object):
"""
Graphical user interface using ncurses
"""
def __init__(self, stdscr):
self.handler = Handler()
def __init__(self, stdscr=None, muc=None):
self.init_curses(stdscr)
self.stdscr = stdscr
self.stdscr.leaveok(True)
self.rooms = [Room('Info', '')] # current_room is self.rooms[0]
self.window = Window(stdscr)
self.window.refresh(self.rooms[0])
self.muc = muc
self.commands = {
'join': self.command_join,
'quit': self.command_quit,
'next': self.rotate_rooms_left,
'prev': self.rotate_rooms_right,
}
self.handler.connect('on-muc-message-received', self.on_message)
self.handler.connect('gui-join-room', self.on_join_room)
self.handler.connect('on-muc-presence-changed', self.on_presence)
self.init_curses(stdscr)
self.stdscr = stdscr
self.handler = Handler()
self.handler.connect('on-connected', self.on_connected)
self.handler.connect('join-room', self.join_room)
self.handler.connect('room-presence', self.room_presence)
self.handler.connect('room-message', self.room_message)
def init_curses(self, stdscr):
curses.start_color()
curses.noecho()
curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE)
curses.init_pair(2, curses.COLOR_BLUE, curses.COLOR_BLACK)
def on_connected(self):
pass
def join_room(self, room, nick):
self.rooms.insert(0, Room(room, nick))
self.window.refresh(self.rooms[0])
def rotate_rooms_left(self, args):
self.rooms.append(self.rooms.pop(0))
self.window.refresh(self.rooms[0])
def rotate_rooms_right(self, args):
self.rooms.insert(0, self.rooms.pop())
self.window.refresh(self.rooms[0])
def room_message(self, stanza):
if stanza.getType() != 'groupchat':
return # ignore all messages not comming from a MUC
room_from = stanza.getFrom().getStripped()
nick_from = stanza.getFrom().getResource()
for room in self.rooms:
if room_from == room.name:
room.add_message(nick_from, stanza.getBody())
if room == self.rooms[0]:
# self.window.text_win.refresh(room.lines)
# self.window.user_win.refresh(room.users)
# self.window.input.refresh()
self.window.refresh(self.rooms[0])
break
def room_presence(self, stanza):
from_nick = stanza.getFrom().getResource()
from_room = stanza.getFrom().getStripped()
for room in self.rooms:
if from_room == room.name:
room.on_presence(stanza, from_nick)
if room == self.rooms[0]:
self.window.text_win.refresh(room.lines)
self.window.user_win.refresh(room.users)
break
def execute(self):
line = self.current_tab.input.get_text()
self.current_tab.input.clear_text()
line = self.window.input.get_text()
self.window.input.clear_text()
if line == "":
return
if line.strip().startswith('/'):
command = line.strip()[:].split()[0][1:]
args = line.strip()[:].split()[1:]
......@@ -245,58 +185,25 @@ class Gui(object):
func = self.commands[command]
func(args)
return
self.current_tab.text_win.add_line("NOW", "louiz'", line)
# TODO, send message to jabber
if self.rooms[0].name != 'Info':
self.muc.send_message(self.rooms[0].name, line)
def command_join(self, args):
room = args[0]
self.on_join_room(room, "poezio")
self.muc.join_room(room, "poezio")
self.join_room(room, 'poezio')
def command_quit(self, args):
sys.exit()
def init_curses(self, stdscr):
stdscr.leaveok(True)
curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE)
curses.init_pair(2, curses.COLOR_BLUE, curses.COLOR_BLACK)
self.current_tab = Tab(stdscr)
self.tabs = [self.current_tab]
def main_loop(self, stdscr):
while 1:
stdscr.refresh()
# self.window.input.refresh()
key = stdscr.getch()
if key == curses.KEY_RESIZE:
self.current_tab.resize(stdscr)
self.window.resize(stdscr)
elif key == 10:
self.execute()
else:
self.current_tab.do_command(key)
def on_message(self, jid, msg, subject, typ, stanza):
print "on_message", jid, msg, subject, typ
def on_join_room(self, room, nick):
sys.stderr.write(room)
self.current_tab = Tab(self.stdscr, room)
self.tabs.append(self.current_tab)
# self.current_tab.resize()
self.current_tab.refresh()
print "on_join_room", room, nick
def on_presence(self, jid, priority, show, status, stanza):
print "on presence", jid, priority, show, status
def main(stdscr):
gui = Gui(stdscr)
gui.main_loop(stdscr)
if __name__ == '__main__':
resource = config.get('resource')
server = config.get('server')
connection = Connection(server, resource)
connection.start()
curses.wrapper(main)
# rooms = config.get('rooms').split(':')
# for room in rooms:
# connection.send_join_room(room.split('/')[0], room.split('/')[1])
self.window.do_command(key)
......@@ -18,7 +18,9 @@
from singleton import Singleton
class Handler(Singleton):
"""This class is the global handler for the software's signals."""
"""
This class is the global handler for the software's signals.
"""
__is_first_instance = True
def __init__(self):
......@@ -26,96 +28,104 @@ class Handler(Singleton):
Handler.__is_first_instance = False
self.__signals__ = {
# - XMPP's core handlers (don't use outside of src/jabber/*.py)
'xmpp-presence-handler': list(),
# A presence is received
# Args: the stanza object
'on-connected': list(),
# At the end of a successful connection process.
# emitted when presence confirmation is received
'xmpp-iq-handler': list(),
# An iq is received
'join-room': list(),
# Join a room.
# Args: room, nick
'room-presence': list(),
# A presence is received
# Args: the stanza object
'xmpp-message-handler': list(),
'room-message': list(),
# A message is received
# Args: the stanza object
# - GUI event
# 'xmpp-presence-handler': list(),
# # A presence is received
# # Args: the stanza object
'on-quit': list(),
# When the user wants to quit.
# 'xmpp-iq-handler': list(),
# # An iq is received
# # Args: the stanza object
# - Roster and presence
# 'xmpp-message-handler': list(),
# # A message is received
# # Args: the stanza object
'on-connected': list(),
# At the end of a successful connection process.
# # - GUI event
'on-disconnected': list(),
# When the user is disconnected from the server.
# 'on-quit': list(),
# # When the user wants to quit.
'on-message-received': list(),
# When a message is received.
# Args: jid, msg, subject, typ
# # - Roster and presence
'send-message': list(),
# Send a message to someone.
# Args: jid, msg, subj, typ
# 'on-disconnected': list(),
# # When the user is disconnected from the server.
# - vCard (XEP-0054)
# 'on-message-received': list(),
# # When a message is received.
# # Args: jid, msg, subject, typ
'vcard-request': list(),
# Request a vcard.
# Args: jid
# 'send-message': list(),
# # Send a message to someone.
# # Args: jid, msg, subj, typ
'on-vcard-received': list(),
# When a vcard is received.
# Args: jid, vcard
# # - vCard (XEP-0054)