Improved CLI and client connection.

parent ef23cef8
...@@ -2,23 +2,31 @@ import asyncore, socket ...@@ -2,23 +2,31 @@ import asyncore, socket
from Queue import Queue from Queue import Queue
import re 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) asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect((host, port))
self.verified = False self.verified = False
self.authenticated = False self.authenticated = False
self.authentification_sent = False self.authentification_sent = False
self.nickname = nickname self.nickname = 'Anonymous'
self.send_queue = Queue() self.send_queue = Queue()
self.send_buffer = '' self.send_buffer = ''
def connect(self, addr):
self.host = addr[0]
self.port = addr[1]
super(ClientConnection, self).connect(addr)
def handle_connect(self): 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 pass
def handle_close(self): def handle_close(self):
...@@ -53,6 +61,13 @@ class ChatClient(asyncore.dispatcher): ...@@ -53,6 +61,13 @@ class ChatClient(asyncore.dispatcher):
else: else:
self.parse_notification(buf) 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): def verify_server(self, buf):
match = re.match('^CHAT/(\d\.\d)/([^\x00-\x1F/:]+)$', buf) match = re.match('^CHAT/(\d\.\d)/([^\x00-\x1F/:]+)$', buf)
...@@ -76,13 +91,7 @@ class ChatClient(asyncore.dispatcher): ...@@ -76,13 +91,7 @@ class ChatClient(asyncore.dispatcher):
def writable(self): def writable(self):
return not self.send_queue.empty() or len(self.send_buffer) 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__': if __name__ == '__main__':
client = ChatClient('ow150.science.uva.nl', 16897) client = ClientConnection()
client.connect(('ow150.science.uva.nl', 16897))
asyncore.loop() asyncore.loop()
...@@ -17,33 +17,7 @@ class ChatWindow: ...@@ -17,33 +17,7 @@ class ChatWindow:
# True, if help screen is currently displayed. # True, if help screen is currently displayed.
self.displayed_help = False self.displayed_help = False
def handle_close(self): def clear(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
self.window.clear() 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() self.window.refresh()
import curses import curses
import sys import sys
from async import ClientConnection
from chat_window import ChatWindow from chat_window import ChatWindow
from info_bar import InfoBar from info_bar import InfoBar
from command_bar import CommandBar from command_bar import CommandBar
...@@ -50,6 +51,12 @@ class CLI: ...@@ -50,6 +51,12 @@ class CLI:
# Initialise color scheme. # Initialise color scheme.
curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE) 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. # Window composition.
self.init_windows() self.init_windows()
...@@ -67,7 +74,7 @@ class CLI: ...@@ -67,7 +74,7 @@ class CLI:
self.max_y, self.max_x = self.stdscr.getmaxyx() self.max_y, self.max_x = self.stdscr.getmaxyx()
self.stdscr.refresh() self.stdscr.refresh()
# Initalise chat window. # Initialise chat window.
self.chat_window = ChatWindow(self, 0, 0, self.max_y - 2, self.max_x) self.chat_window = ChatWindow(self, 0, 0, self.max_y - 2, self.max_x)
# Info bar between chat window and command bar # Info bar between chat window and command bar
...@@ -88,21 +95,144 @@ class CLI: ...@@ -88,21 +95,144 @@ class CLI:
try: try:
c = self.stdscr.getch() c = self.stdscr.getch()
except KeyboardInterrupt: except KeyboardInterrupt:
self.quit() self.execute('quit')
if c != curses.ERR and self.command_bar.handle_input(c): if c != curses.ERR and self.command_bar.handle_input(c):
break break
def quit(self): def init_command_handler(self):
# Reverse the curses-friendly terminal settings.
curses.nocbreak();
self.stdscr.keypad(0);
curses.echo()
# Restore the terminal to its original operating mode. def close(main):
curses.endwin() # 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): def __del__(self):
import curses import curses
......
import curses import curses
from curses.textpad import Textbox from curses.textpad import Textbox
import re
class CommandBar: 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): def __init__(self, main, top, left, height, width):
# Reference to the main window # Reference to the main window
self.main = main self.main = main
...@@ -17,18 +24,10 @@ class CommandBar: ...@@ -17,18 +24,10 @@ class CommandBar:
self.textbox = Textbox(self.window) self.textbox = Textbox(self.window)
def handle_input(self, c): def handle_input(self, c):
#if c == ord('Q'): #if c == curses.KEY_HOME:
# return True
#if c == ord('q'):
# return self.handle_quit()
#if c == ord('h'):
# self.display_help()
#elif c == curses.KEY_HOME:
# self.x = self.y = 0 # self.x = self.y = 0
#if 32 <= c <= 127: #elif 32 <= c <= 127:
# self.append_command(c) # self.append_command(c)
#elif c == 10: # Enter key
# return self.execute_command()
self.append_command(c) self.append_command(c)
def append_command(self, c): def append_command(self, c):
...@@ -42,21 +41,19 @@ class CommandBar: ...@@ -42,21 +41,19 @@ class CommandBar:
command = self.textbox.gather() command = self.textbox.gather()
self.window.deleteln() self.window.deleteln()
self.window.move(0, 0) 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: if not command:
return return
if command[0] == '/': if command[0] == '/':
cmd = command.split(' ', 1)[0][1:] # Split command line into command identifier and arguments.
if cmd == 'help': parts = re.split('\s+', command)
self.main.chat_window.display_help() cmd = parts[0][1:]
elif cmd == 'quit': args = parts[1:]
self.main.quit()
elif cmd == 'close': self.main.execute(cmd, args, command)
self.main.chat_window.handle_close()
else: else:
pass pass
#!/usr/bin/env python #!/usr/bin/env python
from cli import CLI from cli import CLI
import curses
try: try:
cli = CLI() 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