import socketimport threadingimport reimport timeimport messagesDEFAULT_PORT = 6667DEFAULT_BUFSIZE = 512"""Client provides the outer and friendlier layer for connectingto an IRC server and interacting with it. The client willhandle all messages received and invoke message handlers."""class Client: """ Initialize Client """ def __init__(self, host, **kwargs): self.host = host self.port = kwargs.get('port' , DEFAULT_PORT) self.bufsize = kwargs.get('bufsize' , DEFAULT_BUFSIZE) self.messages = kwargs.get('messages', messages.DEFAULT) self.sock = Socket(host, self.port) self.lock = threading.Lock() self.handlers = [] # Append default handlers. # self.handlers.append(...) """ Parse a given line and return its corresponding message object. """ def parse(self, line): # Take the origin out of the line, for easier parsing. origin, line = re.match('^(?::(\S+) )?(.+?)$', line).groups() # Attempt to match an IRC message. match = re.match('^(\S+)(?: (.*?))?(?: :(.+?))?$', line) # Is it a match? If not, create the UnknownMessage. if match: command, args, data = match.groups() # Empty args list. if not args : args = [] else : args = args.split(' ') # Special treatment for NOTICE AUTH. if command == 'NOTICE' and args and args[0] == 'AUTH': args.pop(0) command = 'NOTICE AUTH' # Check for a NOTICE AUTH, a numeric, or any other # message and attempt to create their message objects. if command == 'NOTICE AUTH' : key = 'noticeauth' elif command.isdigit() : key = 'numeric' else : key = command.lower() else: # Defaults for an unknown message. args = [] data = None key = 'unknown' # Now create the message object and return it. return self.messages[key](self, line, command, args, data) """ Call this to put the client into its main loop. During this time the client will receive data from the server, parse it, and invoke handlers. """ def main(self): while 1: line = self.sock.gets(self.bufsize) # - Read data message = self.parse(line.strip()) # - Parse into message"""The Socket class is a light wrapper around standard sockets thatmakes it easy to receive *lines* from a socket, rather thanuncomfortably having to deal with bytes."""class Socket: """ Initialize Socket """ def __init__(self, host, port): self.buffer = [] self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.SOL_TCP) self.sock.connect((host, port)) """ Write to the socket """ def send(data): self.sock.send(data) """ Read a line from the socket """ def gets(self, bufsize = DEFAULT_BUFSIZE): # Simple switch indicating whether a buffer needs # concatenating (continuing off last line) or # appending (adding a whole new line) buffer_is_incomplete = False # Loop until there is even a single line to return. while 1: # Test whether the buffer is empty or not. if self.buffer: line = self.buffer[0] # Even if the buffer isn't empty, we still need to # make sure there's a *complete* line to return. if line.endswith('\n'): self.buffer.pop(0) return line else: buffer_is_incomplete = True # Attempt to read from the socket... could be nothing. data = self.sock.recv(bufsize) if data: lines = data.splitlines(True) # If the buffer isn't complete, that means we # need to concatenate, not append. if buffer_is_incomplete: self.buffer[0] += lines[0] lines.pop(0) # Here we go, appending new lines! for line in lines: self.buffer.append(line) else: # Let's sleep a little if there was nothing to read. time.sleep(0.1)"""A blank object used for the sake of being able to setattributes on it."""class OpenStruct: pass