#!/usr/bin/env python from eagle import * import re import time import webbrowser import socket import struct class Chat(object): def __init__(self, host, port, app, callback): self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.conn = None self.app = app self.callback = callback self.cb_id = None self.client_mode = self.connect(host, port) if not self.client_mode: self.serve(port) def __setup_callback__(self): self.cb_id = self.app.io_watch(self.conn, self.callback, on_in=True, on_error=True, on_hungup=True) def __remove_callback__(self): if self.cb_id: self.app.remove_event_source(self.cb_id) def disconnect(self): if self.cb_id is not None: self.app.remove_event_source(self.cb_id) self.sock.close() def connect(self, host, port): try: self.sock.connect((host, port)) self.conn = self.sock self.__setup_callback__() return True except socket.error, e: self.__remove_callback__() print "could not connect to server at %s:%s: %s" % \ (host, port, e) return False def accept_connection(self, app, sock, *ign, **kign): conn, address = sock.accept() if conn: self.conn = conn self.__setup_callback__() else: self.__remove_callback__() return False def serve(self, port): self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.sock.bind(("", port)) self.sock.listen(5) self.app.io_watch(self.sock, self.accept_connection, on_in=True) def read(self): try: size_bin = self.conn.recv(struct.calcsize("!l")) size = struct.unpack("!l", size_bin)[0] return self.conn.recv(size) except (socket.error, struct.error), e: return None def send(self, msg): try: self.conn.send(struct.pack("!l", len(msg))) self.conn.send(msg) return True except socket.error, e: return False re_smile = re.compile("(?P:-?\))") re_sad = re.compile("(?P:-?\()") re_link = re.compile("(?Phttp://\S+)") smile = Image(id="smile", filename="smile.png") sad = Image(id="sad", filename="sad.png") def format(text): text = text.replace("<", "<").replace(">", ">") text = re_smile.sub("", text) text = re_sad.sub("", text) text = re_link.sub("\">\\g", text) return text def link_clicked(app, view, url, offset): webbrowser.open_new(url) def send_message(app, button): msg = app["entry"].strip() if not msg: return if not app.chat.conn: info("No connection established!") return t = format(msg) timestamp = time.strftime("%Y-%m-%d %H:%M:%S") view = app["view"] view.append("""\

(%s) you: %s

""" % (timestamp, t)) app["entry"] = "" app.chat.send(msg) def receive_message(app, file, on_in, on_out, on_urgent, on_error, on_hungup): if on_error or on_hungup: app.idle_add(reconnect) return False msg = app.chat.read() if msg is None: app.idle_add(reconnect) return False msg = msg.strip() if not msg: return True t = format(msg) timestamp = time.strftime("%Y-%m-%d %H:%M:%S") view = app["view"] view.append("""\

(%s) other: %s

""" % (timestamp, t)) return True # keep it running def create_connection(app): if app.chat: app.chat.disconnect() if app.last_status_message is not None: app.remove_status_message(app.last_status_message) host = app["host"] port = app["port"] app.chat = Chat(host, port, app, receive_message) if app.chat.client_mode: mid = app.status_message("Connected to server %s:%d." % (host, port)) else: mid = app.status_message("Waiting for clients at port %d." % port) app.last_status_message = mid def reconnect(app, *ign): create_connection(app) app = App(title="Chat", statusbar=True, preferences=(Entry(id="host", label="Host", value="localhost", ), UIntSpin(id="port", label="Port", value=12345, ), ), top=(PreferencesButton(), Button(id="reconnect", label="Reconnect", callback=reconnect, ), ), center=(RichText(id="view", label="History:", callback=link_clicked, ), ), bottom=(Entry(id="entry", multiline=True, ), Button(id="send", stock="ok", callback=send_message, ) ) ) app.last_status_message = None app.chat = None create_connection(app) run()