Improved CLI and client connection.

parent ef23cef8
......@@ -2,23 +2,31 @@ import asyncore, socket
from Queue import Queue
import re
class ChatClient(asyncore.dispatcher):
class ClientConnection(object, asyncore.dispatcher):
def __init__(self, host, port, nickname='Foo'):
def __init__(self):
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect((host, port))
self.verified = False
self.authenticated = False
self.authentification_sent = False
self.nickname = nickname
self.nickname = 'Anonymous'
self.send_queue = Queue()
self.send_buffer = ''
def connect(self, addr):
self.host = addr[0]
self.port = addr[1]
super(ClientConnection, self).connect(addr)
def handle_connect(self):
"""
Called when a connection is established. This method will/should be
overwritten by the Command Line Interface or an other implementation.
"""
pass
def handle_close(self):
......@@ -53,6 +61,13 @@ class ChatClient(asyncore.dispatcher):
else:
self.parse_notification(buf)
def handle_write(self):
if not self.send_buffer:
self.send_buffer = self.send_queue.get() + '\r\n'
print '> %s' % self.send_buffer[:-1]
sent = self.send(self.send_buffer)
self.send_buffer = self.send_buffer[sent:]
def verify_server(self, buf):
match = re.match('^CHAT/(\d\.\d)/([^\x00-\x1F/:]+)$', buf)
......@@ -76,13 +91,7 @@ class ChatClient(asyncore.dispatcher):
def writable(self):
return not self.send_queue.empty() or len(self.send_buffer)
def handle_write(self):
if not self.send_buffer:
self.send_buffer = self.send_queue.get() + '\r\n'
print '> %s' % self.send_buffer[:-1]
sent = self.send(self.send_buffer)
self.send_buffer = self.send_buffer[sent:]
if __name__ == '__main__':
client = ChatClient('ow150.science.uva.nl', 16897)
client = ClientConnection()
client.connect(('ow150.science.uva.nl', 16897))
asyncore.loop()
......@@ -17,33 +17,7 @@ class ChatWindow:
# True, if help screen is currently displayed.
self.displayed_help = False
def handle_close(self):
# If the help screen is currently displayed, hide it.
if self.displayed_help:
self.displayed_help = False
self.window.clear()
self.window.refresh()
else:
# Stop user input handler (which will exit the application).
self.main.quit()
def display_help(self):
if self.displayed_help:
return
self.displayed_help = True
def clear(self):
self.window.clear()
help_text = 'Help screen\n' \
+ '\n' \
+ 'All commands listed below should be preceded by a slash:\n' \
+ '\n' \
+ ' Command\tDescription\n' \
+ ' close\t\tClose visible chat window (or this help screen).\n' \
+ ' help\t\tThis help page.\n' \
+ ' quit\t\tQuit this chat application (shortcut: ^c).\n' \
for i, line in enumerate(help_text.split('\n')):
self.window.addstr(i, 0, line)
self.window.refresh()
import curses
import sys
from async import ClientConnection
from chat_window import ChatWindow
from info_bar import InfoBar
from command_bar import CommandBar
......@@ -50,6 +51,12 @@ class CLI:
# Initialise color scheme.
curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE)
# No default connection
self.connection = None
# Initialise command handler
self.init_command_handler()
# Window composition.
self.init_windows()
......@@ -67,7 +74,7 @@ class CLI:
self.max_y, self.max_x = self.stdscr.getmaxyx()
self.stdscr.refresh()
# Initalise chat window.
# Initialise chat window.
self.chat_window = ChatWindow(self, 0, 0, self.max_y - 2, self.max_x)
# Info bar between chat window and command bar
......@@ -88,21 +95,144 @@ class CLI:
try:
c = self.stdscr.getch()
except KeyboardInterrupt:
self.quit()
self.execute('quit')
if c != curses.ERR and self.command_bar.handle_input(c):
break
def quit(self):
# Reverse the curses-friendly terminal settings.
curses.nocbreak();
self.stdscr.keypad(0);
curses.echo()
def init_command_handler(self):
# Restore the terminal to its original operating mode.
curses.endwin()
def close(main):
# If the help screen is currently displayed, hide it.
if main.chat_window.displayed_help:
main.chat_window.displayed_help = False
main.chat_window.clear()
else:
# Stop user input handler (which will exit the application).
main.execute('quit')
def connect(main, host, port=16897):
if getattr(port, '__class__') != '<type \'int\'>':
port = int(port)
if main.connection:
main.execute('disconnect')
main.display_info('Connecting to %s:%d...' % (host, port))
def handle_connect(conn):
"""Called when a connection is established."""
conn.main.display_info('Connected to %s' % conn.host)
main.connection = ClientConnection()
main.connection.main = main
main.connection.handle_connect = handle_connect
main.connection.connect((host, port))
def disconnect(main):
"""
Disconnect from chat server. When there is no active connection,
this operating will only set the 'Offline' message in the
information bar. Otherwise, the active connection is set to None
and the 'Offline' message is set to the info bar.
"""
self.connection = None
self.display_info('Offline. Type "/connect HOST" to connect' \
+ ' to another chat server.')
def help(main):
if main.chat_window.displayed_help:
return
main.chat_window.displayed_help = True
main.chat_window.clear()
help_text = """Help screen
All commands listed below should be preceded by a slash:
Command Description
close Close visible chat window (or this help screen).
connect Connect to a chat server using HOSTNAME and optionally a PORT.
If no PORT is specified, the default port 16897 is used.
Synopsis: /connect HOSTNAME [PORT]
For example, "/connect ow150" will connect to the provided test
server of this assignment (when you're using the UvA network).
disconnect Disconnect from the currently connected chat server.
exec Execute python code. For example. to list all variables and
function of the CLI, type: "/exec print dir(main)".
help This help page.
quit Quit this chat application (shortcut: ^c).
"""
for i, line in enumerate(help_text.split('\n')):
main.chat_window.window.addstr(i, 0, line)
main.chat_window.window.refresh()
def quit(main):
# Reverse the curses-friendly terminal settings.
curses.nocbreak();
self.stdscr.keypad(0);
curses.echo()
# Restore the terminal to its original operating mode.
curses.endwin()
sys.exit(0)
def _exec(main, cmd):
# Skip the leading "/exec " of the command
exec cmd[6:]
# List of builtin command
self.cmd_list = {
'close': close,
'connect': connect,
'disconnect': disconnect,
'exec': _exec,
'help': help,
'quit': quit
}
def display_info(self, msg):
"""
Display a message on the information bar (info_bar). This will erase the
existing message on the info bar.
"""
sys.exit(0)
self.info_bar.display(msg)
def execute(self, cmd, args=[], raw_command=''):
if not raw_command:
raw_command = cmd
# Select command from command list.
try:
cmd_fn = self.cmd_list[cmd]
except KeyError:
self.display_info('Unknown command "/%s".' % cmd)
return
# Call command handler.
try:
if cmd == 'exec':
cmd_fn(self, raw_command)
else:
cmd_fn(self, *args)
except Exception, e:
self.display_info(str(e))
return
def __del__(self):
import curses
......
import curses
from curses.textpad import Textbox
import re
class CommandBar:
"""
Command bar is part of the Command Line Interface (CLI) of the chat
application. The command bar is responsible for handling the user input.
This inclues executing commands and (re)drawing the command line.
"""
def __init__(self, main, top, left, height, width):
# Reference to the main window
self.main = main
......@@ -17,18 +24,10 @@ class CommandBar:
self.textbox = Textbox(self.window)
def handle_input(self, c):
#if c == ord('Q'):
# return True
#if c == ord('q'):
# return self.handle_quit()
#if c == ord('h'):
# self.display_help()
#elif c == curses.KEY_HOME:
#if c == curses.KEY_HOME:
# self.x = self.y = 0
#if 32 <= c <= 127:
#elif 32 <= c <= 127:
# self.append_command(c)
#elif c == 10: # Enter key
# return self.execute_command()
self.append_command(c)
def append_command(self, c):
......@@ -42,21 +41,19 @@ class CommandBar:
command = self.textbox.gather()
self.window.deleteln()
self.window.move(0, 0)
# Handle the given/typed command.
self.handle_command(command)
def handle_command(self, command):
command = command.strip()
if not command:
return
if command[0] == '/':
cmd = command.split(' ', 1)[0][1:]
if cmd == 'help':
self.main.chat_window.display_help()
elif cmd == 'quit':
self.main.quit()
elif cmd == 'close':
self.main.chat_window.handle_close()
# Split command line into command identifier and arguments.
parts = re.split('\s+', command)
cmd = parts[0][1:]
args = parts[1:]
self.main.execute(cmd, args, command)
else:
pass
#!/usr/bin/env python
from cli import CLI
import curses
try:
cli = CLI()
......
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