Implemented entire chat client according to given protocol.

parent e9191165
...@@ -41,7 +41,7 @@ class ClientConnection(object, asyncore.dispatcher): ...@@ -41,7 +41,7 @@ class ClientConnection(object, asyncore.dispatcher):
self.port = addr[1] self.port = addr[1]
super(ClientConnection, self).connect(addr) super(ClientConnection, self).connect(addr)
def send(self, cmd): def send_raw(self, cmd):
""" """
Send raw command to the chat server. Send raw command to the chat server.
""" """
...@@ -52,6 +52,8 @@ class ClientConnection(object, asyncore.dispatcher): ...@@ -52,6 +52,8 @@ class ClientConnection(object, asyncore.dispatcher):
Retreive list of users active at the chat server. Retreive list of users active at the chat server.
""" """
self.request_user_list_sent = True
self.retrieved_user_list = False
self.send_queue.put('NAMES') self.send_queue.put('NAMES')
def username(self, name=''): def username(self, name=''):
...@@ -94,7 +96,6 @@ class ClientConnection(object, asyncore.dispatcher): ...@@ -94,7 +96,6 @@ class ClientConnection(object, asyncore.dispatcher):
# Remove trailing '\n\r' from multi line response # Remove trailing '\n\r' from multi line response
buf = buf[:-2] buf = buf[:-2]
break break
multi_line = False
buf += chunk buf += chunk
buf = buf[:-1] buf = buf[:-1]
...@@ -110,8 +111,9 @@ class ClientConnection(object, asyncore.dispatcher): ...@@ -110,8 +111,9 @@ class ClientConnection(object, asyncore.dispatcher):
def handle_write(self): def handle_write(self):
if not self.send_buffer: if not self.send_buffer:
self.send_buffer = self.send_queue.get() + '\r\n' self.send_buffer = self.send_queue.get()
self.debug_log('> %s' % self.send_buffer[:-2]) self.debug_log('> %s' % self.send_buffer)
self.send_buffer += '\r\n'
sent = self.send(self.send_buffer) sent = self.send(self.send_buffer)
self.send_buffer = self.send_buffer[sent:] self.send_buffer = self.send_buffer[sent:]
...@@ -134,27 +136,40 @@ class ClientConnection(object, asyncore.dispatcher): ...@@ -134,27 +136,40 @@ class ClientConnection(object, asyncore.dispatcher):
if 'response' in self.event_list: if 'response' in self.event_list:
self.event_list['response'](self, buf) self.event_list['response'](self, buf)
# First authenticate the user.
if not self.authenticated: if not self.authenticated:
if not self.authentification_sent: if not self.authentification_sent:
self.send_queue.put('USER %s' % self.user) self.send_queue.put('USER %s' % self.user)
self.authentification_sent = True self.authentification_sent = True
elif buf[1:10] == 'Username': elif buf[1:9] == 'Username':
# TODO: handle 'username is taken'. # TODO: handle 'username is taken'.
self.authenticated = True self.authenticated = True
if 'authenticated' in self.event_list: if 'authenticated' in self.event_list:
self.event_list['authenticated'](self, self.user) self.event_list['authenticated'](self, self.user)
#if not self.retrieved_user_list: if not self.authenticated:
# if not self.request_user_list_sent: return
# self.request_user_list_sent = True
# self.retrieve_users() # After authentification, fetch the list of users currently logged in.
# else: if not self.retrieved_user_list:
# # TODO: process user list if not self.request_user_list_sent:
# self.retrieved_user_list = True self.request_user_list_sent = True
# #self.debug_log('i Users: %s' % str(buf.split('\r\n')[:-1])) self.retrieve_users()
else:
# TODO: process user list
self.retrieved_user_list = True
msg = 'Users: [%s]' % '] ['.join(buf.split('\r\n')[1:])
if 'info' in self.event_list:
self.event_list['info'](self, msg)
else:
self.debug_log(msg)
def parse_notification(self, buf): def parse_notification(self, buf):
if 'notify' in self.event_list: match = re.match('^SAY ([^\r\n:/]+)/(.+)', buf)
if match:
if 'message' in self.event_list:
self.event_list['message'](self, match.group(1), match.group(2))
elif 'notify' in self.event_list:
self.event_list['notify'](self, buf) self.event_list['notify'](self, buf)
def writable(self): def writable(self):
......
import curses import curses
from Queue import Queue from Queue import Queue
from time import strftime
class BaseWindow(object): class BaseWindow(object):
def __init__(self, main, top, left, height, width): def __init__(self, main, top, left, height, width):
...@@ -28,7 +29,7 @@ class BaseWindow(object): ...@@ -28,7 +29,7 @@ class BaseWindow(object):
while not self.line_queue.empty(): while not self.line_queue.empty():
self.lines.append(self.line_queue.get()) self.lines.append(self.line_queue.get())
if self.displayed_help: if hasattr(self,'displayed_help') and self.displayed_help:
return return
for y, line in enumerate(self.lines[-self.height:]): for y, line in enumerate(self.lines[-self.height:]):
...@@ -42,7 +43,9 @@ class BaseWindow(object): ...@@ -42,7 +43,9 @@ class BaseWindow(object):
self.window.refresh() self.window.refresh()
def append(self, msg): def append(self, msg):
time_prefix = strftime('%H:%M') + ' '
while msg: while msg:
msg = time_prefix + msg
self.line_queue.put(msg[:self.width]) self.line_queue.put(msg[:self.width])
msg = msg[self.width:] msg = msg[self.width:]
self.redraw() self.redraw()
......
...@@ -81,7 +81,7 @@ class CLI: ...@@ -81,7 +81,7 @@ class CLI:
self.chat_window = ChatWindow(self, 0, 0, self.max_y - 8, self.max_x) self.chat_window = ChatWindow(self, 0, 0, self.max_y - 8, self.max_x)
# Debug bar between chat window and info bar # Debug bar between chat window and info bar
self.debug_window = DebugWindow(self, self.max_y - 5, 0, 3, self.max_x) self.debug_window = DebugWindow(self, self.max_y - 7, 0, 5, self.max_x)
# Info bar between debug window and command bar # Info bar between debug window and command bar
self.info_bar = InfoBar(self, self.max_y - 2, 0, self.max_x) self.info_bar = InfoBar(self, self.max_y - 2, 0, self.max_x)
...@@ -118,7 +118,7 @@ class CLI: ...@@ -118,7 +118,7 @@ class CLI:
def connect(main, host, port=16897): def connect(main, host, port=16897):
if getattr(port, '__class__') != '<type \'int\'>': if not isinstance(port, int):
port = int(port) port = int(port)
# Disconnect an active connection. # Disconnect an active connection.
...@@ -132,6 +132,8 @@ class CLI: ...@@ -132,6 +132,8 @@ class CLI:
main.connection.main = main main.connection.main = main
main.connection.event_list['debug'] = main.debug_event main.connection.event_list['debug'] = main.debug_event
main.connection.event_list['connect'] = main.connect_event main.connection.event_list['connect'] = main.connect_event
main.connection.event_list['info'] = main.info_event
main.connection.event_list['message'] = main.message_event
main.connection.event_list['notify'] = main.notify_event main.connection.event_list['notify'] = main.notify_event
main.connection.event_list['authenticated'] = \ main.connection.event_list['authenticated'] = \
main.authenticated_event main.authenticated_event
...@@ -185,8 +187,15 @@ All commands listed below should be preceded by a slash: ...@@ -185,8 +187,15 @@ All commands listed below should be preceded by a slash:
help This help page. help This help page.
names Retrieve and display a list of usernames currently logged in on
the chat server.
quit Quit this chat application (shortcut: ^c). quit Quit this chat application (shortcut: ^c).
raw Send a raw command to the server. This is useful during debugging
the client and/or server since you do not have to restart the
application to send an unimplemented message to the server/client.
user Change your username to USER. user Change your username to USER.
""" """
...@@ -199,7 +208,7 @@ All commands listed below should be preceded by a slash: ...@@ -199,7 +208,7 @@ All commands listed below should be preceded by a slash:
e = None e = None
try: try:
del self.connection del self.connection
except: except Exception, e:
pass pass
# Reverse the curses-friendly terminal settings. # Reverse the curses-friendly terminal settings.
...@@ -216,9 +225,16 @@ All commands listed below should be preceded by a slash: ...@@ -216,9 +225,16 @@ All commands listed below should be preceded by a slash:
sys.exit(0) sys.exit(0)
def raw(main, cmd): def raw(main, cmd):
main.connection.send(cmd) # Skip "/raw " and send the remaining command to the server.
if main.connection:
main.connection.send_raw(cmd[5:])
def names(main):
if main.connection:
main.connection.retrieve_users()
def user(main, name): def user(main, name):
if main.connection:
main.connection.username(name) main.connection.username(name)
def _exec(main, cmd): def _exec(main, cmd):
...@@ -232,11 +248,15 @@ All commands listed below should be preceded by a slash: ...@@ -232,11 +248,15 @@ All commands listed below should be preceded by a slash:
'disconnect': disconnect, 'disconnect': disconnect,
'exec': _exec, 'exec': _exec,
'help': help, 'help': help,
'names': names,
'quit': quit, 'quit': quit,
'raw': raw, 'raw': raw,
'user': user, 'user': user,
} }
def say(self, msg):
self.connection.send_raw('SAY %s' % msg)
def refresh(self): def refresh(self):
self.chat_window.refresh() self.chat_window.refresh()
self.debug_window.refresh() self.debug_window.refresh()
...@@ -255,11 +275,19 @@ All commands listed below should be preceded by a slash: ...@@ -255,11 +275,19 @@ All commands listed below should be preceded by a slash:
self.refresh() self.refresh()
def debug_event(self, conn, msg): def debug_event(self, conn, msg):
self.chat_window.append(msg) self.debug_window.append(msg)
self.refresh()
def message_event(self, conn, user, msg):
self.chat_window.append('%s: %s' % (user, msg))
self.refresh()
def info_event(self, conn, msg):
self.chat_window.append('i %s' % msg)
self.refresh() self.refresh()
def notify_event(self, conn, msg): def notify_event(self, conn, msg):
self.chat_window.append(msg) self.chat_window.append('~ %s' % msg)
self.refresh() self.refresh()
def display_info(self, msg): def display_info(self, msg):
......
...@@ -54,5 +54,6 @@ class CommandBar(BaseBar): ...@@ -54,5 +54,6 @@ class CommandBar(BaseBar):
self.main.execute(cmd, args, command) self.main.execute(cmd, args, command)
else: else:
pass # User sends a chat message.
self.main.say(command)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment