17757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# Copyright (C) 1999--2002 Joel Rosdahl 27757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# 37757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# This library is free software; you can redistribute it and/or 47757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# modify it under the terms of the GNU Lesser General Public 57757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# License as published by the Free Software Foundation; either 67757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# version 2.1 of the License, or (at your option) any later version. 77757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# 87757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# This library is distributed in the hope that it will be useful, 97757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# but WITHOUT ANY WARRANTY; without even the implied warranty of 107757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 117757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# Lesser General Public License for more details. 127757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# 137757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# You should have received a copy of the GNU Lesser General Public 147757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# License along with this library; if not, write to the Free Software 157757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 167757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# 177757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# keltus <keltus@users.sourceforge.net> 187757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# 197757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# $Id: irclib.py,v 1.47 2008/09/25 22:00:59 keltus Exp $ 207757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 217757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch"""irclib -- Internet Relay Chat (IRC) protocol client library. 227757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 237757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen MurdochThis library is intended to encapsulate the IRC protocol at a quite 247757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochlow level. It provides an event-driven IRC client framework. It has 257757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdocha fairly thorough support for the basic IRC protocol, CTCP, DCC chat, 267757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochbut DCC file transfers is not yet supported. 277757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 287757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen MurdochIn order to understand how to make an IRC client, I'm afraid you more 297757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochor less must understand the IRC specifications. They are available 307757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochhere: [IRC specifications]. 317757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 327757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen MurdochThe main features of the IRC client framework are: 337757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 347757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch * Abstraction of the IRC protocol. 357757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch * Handles multiple simultaneous IRC server connections. 367757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch * Handles server PONGing transparently. 377757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch * Messages to the IRC server are done by calling methods on an IRC 387757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch connection object. 397757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch * Messages from an IRC server triggers events, which can be caught 407757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch by event handlers. 417757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch * Reading from and writing to IRC server sockets are normally done 427757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch by an internal select() loop, but the select()ing may be done by 437757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch an external main loop. 447757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch * Functions can be registered to execute at specified times by the 457757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch event-loop. 467757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch * Decodes CTCP tagging correctly (hopefully); I haven't seen any 477757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch other IRC client implementation that handles the CTCP 487757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch specification subtilties. 497757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch * A kind of simple, single-server, object-oriented IRC client class 507757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch that dispatches events to instance methods is included. 517757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 527757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen MurdochCurrent limitations: 537757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 547757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch * The IRC protocol shines through the abstraction a bit too much. 557757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch * Data is not written asynchronously to the server, i.e. the write() 567757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch may block if the TCP buffers are stuffed. 577757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch * There are no support for DCC file transfers. 587757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch * The author haven't even read RFC 2810, 2811, 2812 and 2813. 597757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch * Like most projects, documentation is lacking... 607757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 617757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch.. [IRC specifications] http://www.irchelp.org/irchelp/rfc/ 627757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch""" 637757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 647757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochimport bisect 657757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochimport re 667757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochimport select 677757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochimport socket 687757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochimport string 697757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochimport sys 707757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochimport time 717757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochimport types 727757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 737757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen MurdochVERSION = 0, 4, 8 747757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen MurdochDEBUG = 0 757757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 767757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# TODO 777757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# ---- 787757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# (maybe) thread safety 797757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# (maybe) color parser convenience functions 807757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# documentation (including all event types) 817757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# (maybe) add awareness of different types of ircds 827757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# send data asynchronously to the server (and DCC connections) 837757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# (maybe) automatically close unused, passive DCC connections after a while 847757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 857757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# NOTES 867757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# ----- 877757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# connection.quit() only sends QUIT to the server. 887757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# ERROR from the server triggers the error event and the disconnect event. 897757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# dropping of the connection triggers the disconnect event. 907757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 917757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochclass IRCError(Exception): 927757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Represents an IRC exception.""" 937757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch pass 947757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 957757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 967757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochclass IRC: 977757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Class that handles one or several IRC server connections. 987757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 997757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch When an IRC object has been instantiated, it can be used to create 1007757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Connection objects that represent the IRC connections. The 1017757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch responsibility of the IRC object is to provide an event-driven 1027757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch framework for the connections and to keep the connections alive. 1037757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch It runs a select loop to poll each connection's TCP socket and 1047757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch hands over the sockets with incoming data for processing by the 1057757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch corresponding connection. 1067757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 1077757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch The methods of most interest for an IRC client writer are server, 1087757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch add_global_handler, remove_global_handler, execute_at, 1097757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch execute_delayed, process_once and process_forever. 1107757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 1117757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Here is an example: 1127757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 1137757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch irc = irclib.IRC() 1147757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch server = irc.server() 1157757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch server.connect(\"irc.some.where\", 6667, \"my_nickname\") 1167757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch server.privmsg(\"a_nickname\", \"Hi there!\") 1177757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch irc.process_forever() 1187757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 1197757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch This will connect to the IRC server irc.some.where on port 6667 1207757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch using the nickname my_nickname and send the message \"Hi there!\" 1217757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch to the nickname a_nickname. 1227757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 1237757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 1247757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def __init__(self, fn_to_add_socket=None, 1257757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch fn_to_remove_socket=None, 1267757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch fn_to_add_timeout=None): 1277757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Constructor for IRC objects. 1287757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 1297757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Optional arguments are fn_to_add_socket, fn_to_remove_socket 1307757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch and fn_to_add_timeout. The first two specify functions that 1317757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch will be called with a socket object as argument when the IRC 1327757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch object wants to be notified (or stop being notified) of data 1337757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch coming on a new socket. When new data arrives, the method 1347757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch process_data should be called. Similarly, fn_to_add_timeout 1357757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch is called with a number of seconds (a floating point number) 1367757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch as first argument when the IRC object wants to receive a 1377757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch notification (by calling the process_timeout method). So, if 1387757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch e.g. the argument is 42.17, the object wants the 1397757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch process_timeout method to be called after 42 seconds and 170 1407757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch milliseconds. 1417757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 1427757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch The three arguments mainly exist to be able to use an external 1437757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch main loop (for example Tkinter's or PyGTK's main app loop) 1447757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch instead of calling the process_forever method. 1457757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 1467757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch An alternative is to just call ServerConnection.process_once() 1477757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch once in a while. 1487757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 1497757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 1507757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if fn_to_add_socket and fn_to_remove_socket: 1517757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.fn_to_add_socket = fn_to_add_socket 1527757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.fn_to_remove_socket = fn_to_remove_socket 1537757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch else: 1547757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.fn_to_add_socket = None 1557757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.fn_to_remove_socket = None 1567757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 1577757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.fn_to_add_timeout = fn_to_add_timeout 1587757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.connections = [] 1597757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.handlers = {} 1607757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.delayed_commands = [] # list of tuples in the format (time, function, arguments) 1617757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 1627757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.add_global_handler("ping", _ping_ponger, -42) 1637757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 1647757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def server(self): 1657757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Creates and returns a ServerConnection object.""" 1667757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 1677757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch c = ServerConnection(self) 1687757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.connections.append(c) 1697757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return c 1707757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 1717757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def process_data(self, sockets): 1727757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Called when there is more data to read on connection sockets. 1737757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 1747757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Arguments: 1757757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 1767757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch sockets -- A list of socket objects. 1777757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 1787757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch See documentation for IRC.__init__. 1797757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 1807757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch for s in sockets: 1817757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch for c in self.connections: 1827757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if s == c._get_socket(): 1837757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch c.process_data() 1847757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 1857757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def process_timeout(self): 1867757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Called when a timeout notification is due. 1877757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 1887757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch See documentation for IRC.__init__. 1897757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 1907757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch t = time.time() 1917757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch while self.delayed_commands: 1927757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if t >= self.delayed_commands[0][0]: 1937757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.delayed_commands[0][1](*self.delayed_commands[0][2]) 1947757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch del self.delayed_commands[0] 1957757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch else: 1967757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch break 1977757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 1987757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def process_once(self, timeout=0): 1997757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Process data from connections once. 2007757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 2017757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Arguments: 2027757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 2037757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch timeout -- How long the select() call should wait if no 2047757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch data is available. 2057757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 2067757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch This method should be called periodically to check and process 2077757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch incoming data, if there are any. If that seems boring, look 2087757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch at the process_forever method. 2097757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 2107757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch sockets = map(lambda x: x._get_socket(), self.connections) 2117757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch sockets = filter(lambda x: x != None, sockets) 2127757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if sockets: 2137757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch (i, o, e) = select.select(sockets, [], [], timeout) 2147757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.process_data(i) 2157757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch else: 2167757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch time.sleep(timeout) 2177757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.process_timeout() 2187757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 2197757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def process_forever(self, timeout=0.2): 2207757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Run an infinite loop, processing data from connections. 2217757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 2227757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch This method repeatedly calls process_once. 2237757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 2247757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Arguments: 2257757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 2267757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch timeout -- Parameter to pass to process_once. 2277757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 2287757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch while 1: 2297757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.process_once(timeout) 2307757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 2317757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def disconnect_all(self, message=""): 2327757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Disconnects all connections.""" 2337757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch for c in self.connections: 2347757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch c.disconnect(message) 2357757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 2367757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def add_global_handler(self, event, handler, priority=0): 2377757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Adds a global handler function for a specific event type. 2387757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 2397757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Arguments: 2407757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 2417757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch event -- Event type (a string). Check the values of the 2427757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch numeric_events dictionary in irclib.py for possible event 2437757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch types. 2447757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 2457757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch handler -- Callback function. 2467757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 2477757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch priority -- A number (the lower number, the higher priority). 2487757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 2497757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch The handler function is called whenever the specified event is 2507757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch triggered in any of the connections. See documentation for 2517757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch the Event class. 2527757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 2537757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch The handler functions are called in priority order (lowest 2547757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch number is highest priority). If a handler function returns 2557757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch \"NO MORE\", no more handlers will be called. 2567757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 2577757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if not event in self.handlers: 2587757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.handlers[event] = [] 2597757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch bisect.insort(self.handlers[event], ((priority, handler))) 2607757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 2617757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def remove_global_handler(self, event, handler): 2627757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Removes a global handler function. 2637757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 2647757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Arguments: 2657757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 2667757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch event -- Event type (a string). 2677757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 2687757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch handler -- Callback function. 2697757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 2707757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Returns 1 on success, otherwise 0. 2717757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 2727757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if not event in self.handlers: 2737757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return 0 2747757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch for h in self.handlers[event]: 2757757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if handler == h[1]: 2767757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.handlers[event].remove(h) 2777757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return 1 2787757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 2797757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def execute_at(self, at, function, arguments=()): 2807757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Execute a function at a specified time. 2817757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 2827757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Arguments: 2837757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 2847757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch at -- Execute at this time (standard \"time_t\" time). 2857757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 2867757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch function -- Function to call. 2877757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 2887757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch arguments -- Arguments to give the function. 2897757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 2907757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.execute_delayed(at-time.time(), function, arguments) 2917757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 2927757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def execute_delayed(self, delay, function, arguments=()): 2937757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Execute a function after a specified time. 2947757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 2957757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Arguments: 2967757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 2977757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch delay -- How many seconds to wait. 2987757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 2997757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch function -- Function to call. 3007757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3017757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch arguments -- Arguments to give the function. 3027757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 3037757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch bisect.insort(self.delayed_commands, (delay+time.time(), function, arguments)) 3047757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if self.fn_to_add_timeout: 3057757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.fn_to_add_timeout(delay) 3067757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3077757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def dcc(self, dcctype="chat"): 3087757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Creates and returns a DCCConnection object. 3097757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3107757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Arguments: 3117757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3127757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch dcctype -- "chat" for DCC CHAT connections or "raw" for 3137757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch DCC SEND (or other DCC types). If "chat", 3147757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch incoming data will be split in newline-separated 3157757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch chunks. If "raw", incoming data is not touched. 3167757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 3177757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch c = DCCConnection(self, dcctype) 3187757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.connections.append(c) 3197757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return c 3207757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3217757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def _handle_event(self, connection, event): 3227757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """[Internal]""" 3237757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch h = self.handlers 3247757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch for handler in h.get("all_events", []) + h.get(event.eventtype(), []): 3257757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if handler[1](connection, event) == "NO MORE": 3267757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return 3277757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3287757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def _remove_connection(self, connection): 3297757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """[Internal]""" 3307757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.connections.remove(connection) 3317757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if self.fn_to_remove_socket: 3327757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.fn_to_remove_socket(connection._get_socket()) 3337757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3347757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch_rfc_1459_command_regexp = re.compile("^(:(?P<prefix>[^ ]+) +)?(?P<command>[^ ]+)( *(?P<argument> .+))?") 3357757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3367757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochclass Connection: 3377757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Base class for IRC connections. 3387757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3397757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Must be overridden. 3407757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 3417757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def __init__(self, irclibobj): 3427757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.irclibobj = irclibobj 3437757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3447757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def _get_socket(): 3457757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch raise IRCError, "Not overridden" 3467757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3477757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch ############################## 3487757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch ### Convenience wrappers. 3497757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3507757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def execute_at(self, at, function, arguments=()): 3517757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.irclibobj.execute_at(at, function, arguments) 3527757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3537757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def execute_delayed(self, delay, function, arguments=()): 3547757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.irclibobj.execute_delayed(delay, function, arguments) 3557757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3567757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3577757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochclass ServerConnectionError(IRCError): 3587757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch pass 3597757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3607757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochclass ServerNotConnectedError(ServerConnectionError): 3617757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch pass 3627757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3637757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3647757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# Huh!? Crrrrazy EFNet doesn't follow the RFC: their ircd seems to 3657757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# use \n as message separator! :P 3667757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch_linesep_regexp = re.compile("\r?\n") 3677757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3687757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochclass ServerConnection(Connection): 3697757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """This class represents an IRC server connection. 3707757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3717757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch ServerConnection objects are instantiated by calling the server 3727757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch method on an IRC object. 3737757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 3747757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3757757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def __init__(self, irclibobj): 3767757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Connection.__init__(self, irclibobj) 3777757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.connected = 0 # Not connected yet. 3787757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.socket = None 3797757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.ssl = None 3807757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3817757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def connect(self, server, port, nickname, password=None, username=None, 3827757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch ircname=None, localaddress="", localport=0, ssl=False, ipv6=False): 3837757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Connect/reconnect to a server. 3847757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3857757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Arguments: 3867757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3877757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch server -- Server name. 3887757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3897757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch port -- Port number. 3907757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3917757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch nickname -- The nickname. 3927757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3937757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch password -- Password (if any). 3947757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3957757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch username -- The username. 3967757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3977757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch ircname -- The IRC name ("realname"). 3987757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 3997757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch localaddress -- Bind the connection to a specific local IP address. 4007757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 4017757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch localport -- Bind the connection to a specific local port. 4027757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 4037757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch ssl -- Enable support for ssl. 4047757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 4057757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch ipv6 -- Enable support for ipv6. 4067757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 4077757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch This function can be called to reconnect a closed connection. 4087757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 4097757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Returns the ServerConnection object. 4107757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 4117757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if self.connected: 4127757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.disconnect("Changing servers") 4137757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 4147757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.previous_buffer = "" 4157757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.handlers = {} 4167757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.real_server_name = "" 4177757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.real_nickname = nickname 4187757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.server = server 4197757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.port = port 4207757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.nickname = nickname 4217757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.username = username or nickname 4227757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.ircname = ircname or nickname 4237757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.password = password 4247757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.localaddress = localaddress 4257757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.localport = localport 4267757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.localhost = socket.gethostname() 4277757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if ipv6: 4287757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.socket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) 4297757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch else: 4307757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 4317757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch try: 4327757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.socket.bind((self.localaddress, self.localport)) 4337757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.socket.connect((self.server, self.port)) 4347757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if ssl: 4357757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.ssl = socket.ssl(self.socket) 4367757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch except socket.error, x: 4377757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.socket.close() 4387757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.socket = None 4397757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch raise ServerConnectionError, "Couldn't connect to socket: %s" % x 4407757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.connected = 1 4417757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if self.irclibobj.fn_to_add_socket: 4427757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.irclibobj.fn_to_add_socket(self.socket) 4437757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 4447757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # Log on... 4457757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if self.password: 4467757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.pass_(self.password) 4477757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.nick(self.nickname) 4487757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.user(self.username, self.ircname) 4497757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return self 4507757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 4517757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def close(self): 4527757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Close the connection. 4537757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 4547757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch This method closes the connection permanently; after it has 4557757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch been called, the object is unusable. 4567757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 4577757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 4587757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.disconnect("Closing object") 4597757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.irclibobj._remove_connection(self) 4607757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 4617757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def _get_socket(self): 4627757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """[Internal]""" 4637757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return self.socket 4647757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 4657757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def get_server_name(self): 4667757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Get the (real) server name. 4677757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 4687757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch This method returns the (real) server name, or, more 4697757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch specifically, what the server calls itself. 4707757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 4717757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 4727757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if self.real_server_name: 4737757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return self.real_server_name 4747757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch else: 4757757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return "" 4767757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 4777757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def get_nickname(self): 4787757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Get the (real) nick name. 4797757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 4807757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch This method returns the (real) nickname. The library keeps 4817757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch track of nick changes, so it might not be the nick name that 4827757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch was passed to the connect() method. """ 4837757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 4847757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return self.real_nickname 4857757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 4867757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def process_data(self): 4877757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """[Internal]""" 4887757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 4897757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch try: 4907757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if self.ssl: 4917757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch new_data = self.ssl.read(2**14) 4927757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch else: 4937757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch new_data = self.socket.recv(2**14) 4947757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch except socket.error, x: 4957757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # The server hung up. 4967757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.disconnect("Connection reset by peer") 4977757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return 4987757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if not new_data: 4997757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # Read nothing: connection must be down. 5007757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.disconnect("Connection reset by peer") 5017757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return 5027757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 5037757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch lines = _linesep_regexp.split(self.previous_buffer + new_data) 5047757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 5057757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # Save the last, unfinished line. 5067757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.previous_buffer = lines.pop() 5077757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 5087757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch for line in lines: 5097757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if DEBUG: 5107757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch print "FROM SERVER:", line 5117757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 5127757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if not line: 5137757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch continue 5147757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 5157757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch prefix = None 5167757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch command = None 5177757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch arguments = None 5187757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self._handle_event(Event("all_raw_messages", 5197757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.get_server_name(), 5207757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch None, 5217757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch [line])) 5227757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 5237757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch m = _rfc_1459_command_regexp.match(line) 5247757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if m.group("prefix"): 5257757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch prefix = m.group("prefix") 5267757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if not self.real_server_name: 5277757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.real_server_name = prefix 5287757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 5297757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if m.group("command"): 5307757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch command = m.group("command").lower() 5317757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 5327757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if m.group("argument"): 5337757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch a = m.group("argument").split(" :", 1) 5347757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch arguments = a[0].split() 5357757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if len(a) == 2: 5367757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch arguments.append(a[1]) 5377757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 5387757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # Translate numerics into more readable strings. 5397757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if command in numeric_events: 5407757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch command = numeric_events[command] 5417757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 5427757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if command == "nick": 5437757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if nm_to_n(prefix) == self.real_nickname: 5447757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.real_nickname = arguments[0] 5457757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch elif command == "welcome": 5467757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # Record the nickname in case the client changed nick 5477757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # in a nicknameinuse callback. 5487757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.real_nickname = arguments[0] 5497757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 5507757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if command in ["privmsg", "notice"]: 5517757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch target, message = arguments[0], arguments[1] 5527757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch messages = _ctcp_dequote(message) 5537757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 5547757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if command == "privmsg": 5557757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if is_channel(target): 5567757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch command = "pubmsg" 5577757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch else: 5587757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if is_channel(target): 5597757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch command = "pubnotice" 5607757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch else: 5617757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch command = "privnotice" 5627757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 5637757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch for m in messages: 5647757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if type(m) is types.TupleType: 5657757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if command in ["privmsg", "pubmsg"]: 5667757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch command = "ctcp" 5677757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch else: 5687757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch command = "ctcpreply" 5697757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 5707757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch m = list(m) 5717757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if DEBUG: 5727757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch print "command: %s, source: %s, target: %s, arguments: %s" % ( 5737757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch command, prefix, target, m) 5747757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self._handle_event(Event(command, prefix, target, m)) 5757757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if command == "ctcp" and m[0] == "ACTION": 5767757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self._handle_event(Event("action", prefix, target, m[1:])) 5777757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch else: 5787757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if DEBUG: 5797757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch print "command: %s, source: %s, target: %s, arguments: %s" % ( 5807757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch command, prefix, target, [m]) 5817757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self._handle_event(Event(command, prefix, target, [m])) 5827757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch else: 5837757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch target = None 5847757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 5857757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if command == "quit": 5867757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch arguments = [arguments[0]] 5877757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch elif command == "ping": 5887757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch target = arguments[0] 5897757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch else: 5907757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch target = arguments[0] 5917757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch arguments = arguments[1:] 5927757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 5937757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if command == "mode": 5947757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if not is_channel(target): 5957757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch command = "umode" 5967757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 5977757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if DEBUG: 5987757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch print "command: %s, source: %s, target: %s, arguments: %s" % ( 5997757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch command, prefix, target, arguments) 6007757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self._handle_event(Event(command, prefix, target, arguments)) 6017757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 6027757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def _handle_event(self, event): 6037757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """[Internal]""" 6047757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.irclibobj._handle_event(self, event) 6057757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if event.eventtype() in self.handlers: 6067757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch for fn in self.handlers[event.eventtype()]: 6077757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch fn(self, event) 6087757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 6097757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def is_connected(self): 6107757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Return connection status. 6117757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 6127757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Returns true if connected, otherwise false. 6137757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 6147757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return self.connected 6157757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 6167757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def add_global_handler(self, *args): 6177757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Add global handler. 6187757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 6197757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch See documentation for IRC.add_global_handler. 6207757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 6217757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.irclibobj.add_global_handler(*args) 6227757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 6237757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def remove_global_handler(self, *args): 6247757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Remove global handler. 6257757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 6267757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch See documentation for IRC.remove_global_handler. 6277757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 6287757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.irclibobj.remove_global_handler(*args) 6297757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 6307757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def action(self, target, action): 6317757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a CTCP ACTION command.""" 6327757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.ctcp("ACTION", target, action) 6337757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 6347757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def admin(self, server=""): 6357757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send an ADMIN command.""" 6367757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw(" ".join(["ADMIN", server]).strip()) 6377757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 6387757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def ctcp(self, ctcptype, target, parameter=""): 6397757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a CTCP command.""" 6407757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch ctcptype = ctcptype.upper() 6417757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.privmsg(target, "\001%s%s\001" % (ctcptype, parameter and (" " + parameter) or "")) 6427757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 6437757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def ctcp_reply(self, target, parameter): 6447757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a CTCP REPLY command.""" 6457757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.notice(target, "\001%s\001" % parameter) 6467757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 6477757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def disconnect(self, message=""): 6487757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Hang up the connection. 6497757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 6507757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Arguments: 6517757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 6527757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch message -- Quit message. 6537757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 6547757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if not self.connected: 6557757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return 6567757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 6577757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.connected = 0 6587757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 6597757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.quit(message) 6607757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 6617757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch try: 6627757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.socket.close() 6637757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch except socket.error, x: 6647757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch pass 6657757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.socket = None 6667757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self._handle_event(Event("disconnect", self.server, "", [message])) 6677757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 6687757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def globops(self, text): 6697757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a GLOBOPS command.""" 6707757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("GLOBOPS :" + text) 6717757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 6727757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def info(self, server=""): 6737757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send an INFO command.""" 6747757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw(" ".join(["INFO", server]).strip()) 6757757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 6767757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def invite(self, nick, channel): 6777757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send an INVITE command.""" 6787757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw(" ".join(["INVITE", nick, channel]).strip()) 6797757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 6807757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def ison(self, nicks): 6817757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send an ISON command. 6827757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 6837757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Arguments: 6847757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 6857757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch nicks -- List of nicks. 6867757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 6877757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("ISON " + " ".join(nicks)) 6887757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 6897757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def join(self, channel, key=""): 6907757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a JOIN command.""" 6917757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("JOIN %s%s" % (channel, (key and (" " + key)))) 6927757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 6937757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def kick(self, channel, nick, comment=""): 6947757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a KICK command.""" 6957757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("KICK %s %s%s" % (channel, nick, (comment and (" :" + comment)))) 6967757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 6977757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def links(self, remote_server="", server_mask=""): 6987757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a LINKS command.""" 6997757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch command = "LINKS" 7007757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if remote_server: 7017757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch command = command + " " + remote_server 7027757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if server_mask: 7037757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch command = command + " " + server_mask 7047757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw(command) 7057757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 7067757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def list(self, channels=None, server=""): 7077757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a LIST command.""" 7087757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch command = "LIST" 7097757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if channels: 7107757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch command = command + " " + ",".join(channels) 7117757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if server: 7127757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch command = command + " " + server 7137757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw(command) 7147757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 7157757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def lusers(self, server=""): 7167757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a LUSERS command.""" 7177757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("LUSERS" + (server and (" " + server))) 7187757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 7197757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def mode(self, target, command): 7207757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a MODE command.""" 7217757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("MODE %s %s" % (target, command)) 7227757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 7237757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def motd(self, server=""): 7247757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send an MOTD command.""" 7257757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("MOTD" + (server and (" " + server))) 7267757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 7277757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def names(self, channels=None): 7287757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a NAMES command.""" 7297757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("NAMES" + (channels and (" " + ",".join(channels)) or "")) 7307757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 7317757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def nick(self, newnick): 7327757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a NICK command.""" 7337757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("NICK " + newnick) 7347757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 7357757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def notice(self, target, text): 7367757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a NOTICE command.""" 7377757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # Should limit len(text) here! 7387757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("NOTICE %s :%s" % (target, text)) 7397757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 7407757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def oper(self, nick, password): 7417757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send an OPER command.""" 7427757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("OPER %s %s" % (nick, password)) 7437757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 7447757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def part(self, channels, message=""): 7457757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a PART command.""" 7467757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if type(channels) == types.StringType: 7477757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("PART " + channels + (message and (" " + message))) 7487757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch else: 7497757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("PART " + ",".join(channels) + (message and (" " + message))) 7507757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 7517757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def pass_(self, password): 7527757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a PASS command.""" 7537757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("PASS " + password) 7547757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 7557757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def ping(self, target, target2=""): 7567757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a PING command.""" 7577757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("PING %s%s" % (target, target2 and (" " + target2))) 7587757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 7597757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def pong(self, target, target2=""): 7607757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a PONG command.""" 7617757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("PONG %s%s" % (target, target2 and (" " + target2))) 7627757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 7637757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def privmsg(self, target, text): 7647757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a PRIVMSG command.""" 7657757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # Should limit len(text) here! 7667757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("PRIVMSG %s :%s" % (target, text)) 7677757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 7687757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def privmsg_many(self, targets, text): 7697757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a PRIVMSG command to multiple targets.""" 7707757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # Should limit len(text) here! 7717757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("PRIVMSG %s :%s" % (",".join(targets), text)) 7727757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 7737757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def quit(self, message=""): 7747757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a QUIT command.""" 7757757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # Note that many IRC servers don't use your QUIT message 7767757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # unless you've been connected for at least 5 minutes! 7777757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("QUIT" + (message and (" :" + message))) 7787757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 7797757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def send_raw(self, string): 7807757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send raw string to the server. 7817757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 7827757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch The string will be padded with appropriate CR LF. 7837757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 7847757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if self.socket is None: 7857757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch raise ServerNotConnectedError, "Not connected." 7867757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch try: 7877757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if self.ssl: 7887757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.ssl.write(string + "\r\n") 7897757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch else: 7907757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.socket.send(string + "\r\n") 7917757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if DEBUG: 7927757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch print "TO SERVER:", string 7937757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch except socket.error, x: 7947757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # Ouch! 7957757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.disconnect("Connection reset by peer.") 7967757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 7977757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def squit(self, server, comment=""): 7987757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send an SQUIT command.""" 7997757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("SQUIT %s%s" % (server, comment and (" :" + comment))) 8007757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 8017757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def stats(self, statstype, server=""): 8027757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a STATS command.""" 8037757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("STATS %s%s" % (statstype, server and (" " + server))) 8047757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 8057757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def time(self, server=""): 8067757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a TIME command.""" 8077757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("TIME" + (server and (" " + server))) 8087757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 8097757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def topic(self, channel, new_topic=None): 8107757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a TOPIC command.""" 8117757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if new_topic is None: 8127757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("TOPIC " + channel) 8137757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch else: 8147757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("TOPIC %s :%s" % (channel, new_topic)) 8157757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 8167757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def trace(self, target=""): 8177757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a TRACE command.""" 8187757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("TRACE" + (target and (" " + target))) 8197757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 8207757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def user(self, username, realname): 8217757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a USER command.""" 8227757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("USER %s 0 * :%s" % (username, realname)) 8237757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 8247757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def userhost(self, nicks): 8257757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a USERHOST command.""" 8267757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("USERHOST " + ",".join(nicks)) 8277757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 8287757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def users(self, server=""): 8297757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a USERS command.""" 8307757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("USERS" + (server and (" " + server))) 8317757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 8327757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def version(self, server=""): 8337757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a VERSION command.""" 8347757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("VERSION" + (server and (" " + server))) 8357757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 8367757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def wallops(self, text): 8377757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a WALLOPS command.""" 8387757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("WALLOPS :" + text) 8397757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 8407757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def who(self, target="", op=""): 8417757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a WHO command.""" 8427757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("WHO%s%s" % (target and (" " + target), op and (" o"))) 8437757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 8447757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def whois(self, targets): 8457757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a WHOIS command.""" 8467757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("WHOIS " + ",".join(targets)) 8477757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 8487757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def whowas(self, nick, max="", server=""): 8497757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send a WHOWAS command.""" 8507757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.send_raw("WHOWAS %s%s%s" % (nick, 8517757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch max and (" " + max), 8527757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch server and (" " + server))) 8537757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 8547757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochclass DCCConnectionError(IRCError): 8557757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch pass 8567757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 8577757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 8587757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochclass DCCConnection(Connection): 8597757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """This class represents a DCC connection. 8607757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 8617757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch DCCConnection objects are instantiated by calling the dcc 8627757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch method on an IRC object. 8637757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 8647757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def __init__(self, irclibobj, dcctype): 8657757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Connection.__init__(self, irclibobj) 8667757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.connected = 0 8677757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.passive = 0 8687757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.dcctype = dcctype 8697757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.peeraddress = None 8707757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.peerport = None 8717757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 8727757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def connect(self, address, port): 8737757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Connect/reconnect to a DCC peer. 8747757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 8757757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Arguments: 8767757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch address -- Host/IP address of the peer. 8777757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 8787757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch port -- The port number to connect to. 8797757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 8807757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Returns the DCCConnection object. 8817757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 8827757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.peeraddress = socket.gethostbyname(address) 8837757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.peerport = port 8847757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.socket = None 8857757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.previous_buffer = "" 8867757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.handlers = {} 8877757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 8887757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.passive = 0 8897757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch try: 8907757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.socket.connect((self.peeraddress, self.peerport)) 8917757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch except socket.error, x: 8927757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch raise DCCConnectionError, "Couldn't connect to socket: %s" % x 8937757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.connected = 1 8947757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if self.irclibobj.fn_to_add_socket: 8957757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.irclibobj.fn_to_add_socket(self.socket) 8967757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return self 8977757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 8987757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def listen(self): 8997757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Wait for a connection/reconnection from a DCC peer. 9007757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 9017757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Returns the DCCConnection object. 9027757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 9037757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch The local IP address and port are available as 9047757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.localaddress and self.localport. After connection from a 9057757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch peer, the peer address and port are available as 9067757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.peeraddress and self.peerport. 9077757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 9087757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.previous_buffer = "" 9097757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.handlers = {} 9107757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 9117757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.passive = 1 9127757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch try: 9137757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.socket.bind((socket.gethostbyname(socket.gethostname()), 0)) 9147757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.localaddress, self.localport = self.socket.getsockname() 9157757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.socket.listen(10) 9167757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch except socket.error, x: 9177757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch raise DCCConnectionError, "Couldn't bind socket: %s" % x 9187757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return self 9197757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 9207757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def disconnect(self, message=""): 9217757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Hang up the connection and close the object. 9227757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 9237757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Arguments: 9247757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 9257757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch message -- Quit message. 9267757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 9277757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if not self.connected: 9287757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return 9297757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 9307757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.connected = 0 9317757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch try: 9327757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.socket.close() 9337757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch except socket.error, x: 9347757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch pass 9357757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.socket = None 9367757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.irclibobj._handle_event( 9377757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self, 9387757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Event("dcc_disconnect", self.peeraddress, "", [message])) 9397757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.irclibobj._remove_connection(self) 9407757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 9417757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def process_data(self): 9427757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """[Internal]""" 9437757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 9447757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if self.passive and not self.connected: 9457757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch conn, (self.peeraddress, self.peerport) = self.socket.accept() 9467757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.socket.close() 9477757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.socket = conn 9487757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.connected = 1 9497757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if DEBUG: 9507757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch print "DCC connection from %s:%d" % ( 9517757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.peeraddress, self.peerport) 9527757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.irclibobj._handle_event( 9537757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self, 9547757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Event("dcc_connect", self.peeraddress, None, None)) 9557757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return 9567757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 9577757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch try: 9587757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch new_data = self.socket.recv(2**14) 9597757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch except socket.error, x: 9607757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # The server hung up. 9617757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.disconnect("Connection reset by peer") 9627757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return 9637757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if not new_data: 9647757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # Read nothing: connection must be down. 9657757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.disconnect("Connection reset by peer") 9667757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return 9677757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 9687757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if self.dcctype == "chat": 9697757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # The specification says lines are terminated with LF, but 9707757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # it seems safer to handle CR LF terminations too. 9717757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch chunks = _linesep_regexp.split(self.previous_buffer + new_data) 9727757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 9737757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # Save the last, unfinished line. 9747757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.previous_buffer = chunks[-1] 9757757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if len(self.previous_buffer) > 2**14: 9767757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # Bad peer! Naughty peer! 9777757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.disconnect() 9787757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return 9797757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch chunks = chunks[:-1] 9807757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch else: 9817757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch chunks = [new_data] 9827757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 9837757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch command = "dccmsg" 9847757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch prefix = self.peeraddress 9857757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch target = None 9867757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch for chunk in chunks: 9877757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if DEBUG: 9887757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch print "FROM PEER:", chunk 9897757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch arguments = [chunk] 9907757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if DEBUG: 9917757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch print "command: %s, source: %s, target: %s, arguments: %s" % ( 9927757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch command, prefix, target, arguments) 9937757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.irclibobj._handle_event( 9947757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self, 9957757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Event(command, prefix, target, arguments)) 9967757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 9977757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def _get_socket(self): 9987757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """[Internal]""" 9997757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return self.socket 10007757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 10017757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def privmsg(self, string): 10027757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Send data to DCC peer. 10037757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 10047757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch The string will be padded with appropriate LF if it's a DCC 10057757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch CHAT session. 10067757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 10077757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch try: 10087757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.socket.send(string) 10097757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if self.dcctype == "chat": 10107757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.socket.send("\n") 10117757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if DEBUG: 10127757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch print "TO PEER: %s\n" % string 10137757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch except socket.error, x: 10147757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # Ouch! 10157757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.disconnect("Connection reset by peer.") 10167757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 10177757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochclass SimpleIRCClient: 10187757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """A simple single-server IRC client class. 10197757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 10207757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch This is an example of an object-oriented wrapper of the IRC 10217757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch framework. A real IRC client can be made by subclassing this 10227757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch class and adding appropriate methods. 10237757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 10247757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch The method on_join will be called when a "join" event is created 10257757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch (which is done when the server sends a JOIN messsage/command), 10267757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch on_privmsg will be called for "privmsg" events, and so on. The 10277757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch handler methods get two arguments: the connection object (same as 10287757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.connection) and the event object. 10297757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 10307757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Instance attributes that can be used by sub classes: 10317757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 10327757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch ircobj -- The IRC instance. 10337757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 10347757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch connection -- The ServerConnection instance. 10357757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 10367757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch dcc_connections -- A list of DCCConnection instances. 10377757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 10387757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def __init__(self): 10397757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.ircobj = IRC() 10407757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.connection = self.ircobj.server() 10417757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.dcc_connections = [] 10427757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.ircobj.add_global_handler("all_events", self._dispatcher, -10) 10437757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.ircobj.add_global_handler("dcc_disconnect", self._dcc_disconnect, -10) 10447757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 10457757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def _dispatcher(self, c, e): 10467757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """[Internal]""" 10477757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch m = "on_" + e.eventtype() 10487757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if hasattr(self, m): 10497757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch getattr(self, m)(c, e) 10507757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 10517757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def _dcc_disconnect(self, c, e): 10527757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.dcc_connections.remove(c) 10537757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 10547757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def connect(self, server, port, nickname, password=None, username=None, 10557757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch ircname=None, localaddress="", localport=0, ssl=False, ipv6=False): 10567757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Connect/reconnect to a server. 10577757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 10587757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Arguments: 10597757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 10607757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch server -- Server name. 10617757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 10627757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch port -- Port number. 10637757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 10647757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch nickname -- The nickname. 10657757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 10667757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch password -- Password (if any). 10677757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 10687757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch username -- The username. 10697757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 10707757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch ircname -- The IRC name. 10717757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 10727757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch localaddress -- Bind the connection to a specific local IP address. 10737757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 10747757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch localport -- Bind the connection to a specific local port. 10757757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 10767757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch ssl -- Enable support for ssl. 10777757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 10787757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch ipv6 -- Enable support for ipv6. 10797757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 10807757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch This function can be called to reconnect a closed connection. 10817757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 10827757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.connection.connect(server, port, nickname, 10837757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch password, username, ircname, 10847757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch localaddress, localport, ssl, ipv6) 10857757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 10867757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def dcc_connect(self, address, port, dcctype="chat"): 10877757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Connect to a DCC peer. 10887757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 10897757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Arguments: 10907757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 10917757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch address -- IP address of the peer. 10927757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 10937757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch port -- Port to connect to. 10947757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 10957757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Returns a DCCConnection instance. 10967757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 10977757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch dcc = self.ircobj.dcc(dcctype) 10987757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.dcc_connections.append(dcc) 10997757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch dcc.connect(address, port) 11007757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return dcc 11017757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 11027757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def dcc_listen(self, dcctype="chat"): 11037757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Listen for connections from a DCC peer. 11047757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 11057757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Returns a DCCConnection instance. 11067757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 11077757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch dcc = self.ircobj.dcc(dcctype) 11087757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.dcc_connections.append(dcc) 11097757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch dcc.listen() 11107757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return dcc 11117757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 11127757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def start(self): 11137757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Start the IRC client.""" 11147757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.ircobj.process_forever() 11157757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 11167757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 11177757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochclass Event: 11187757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Class representing an IRC event.""" 11197757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def __init__(self, eventtype, source, target, arguments=None): 11207757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Constructor of Event objects. 11217757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 11227757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Arguments: 11237757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 11247757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch eventtype -- A string describing the event. 11257757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 11267757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch source -- The originator of the event (a nick mask or a server). 11277757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 11287757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch target -- The target of the event (a nick or a channel). 11297757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 11307757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch arguments -- Any event specific arguments. 11317757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 11327757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self._eventtype = eventtype 11337757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self._source = source 11347757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self._target = target 11357757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if arguments: 11367757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self._arguments = arguments 11377757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch else: 11387757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self._arguments = [] 11397757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 11407757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def eventtype(self): 11417757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Get the event type.""" 11427757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return self._eventtype 11437757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 11447757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def source(self): 11457757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Get the event source.""" 11467757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return self._source 11477757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 11487757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def target(self): 11497757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Get the event target.""" 11507757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return self._target 11517757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 11527757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def arguments(self): 11537757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Get the event arguments.""" 11547757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return self._arguments 11557757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 11567757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch_LOW_LEVEL_QUOTE = "\020" 11577757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch_CTCP_LEVEL_QUOTE = "\134" 11587757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch_CTCP_DELIMITER = "\001" 11597757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 11607757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch_low_level_mapping = { 11617757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "0": "\000", 11627757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "n": "\n", 11637757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "r": "\r", 11647757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch _LOW_LEVEL_QUOTE: _LOW_LEVEL_QUOTE 11657757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch} 11667757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 11677757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch_low_level_regexp = re.compile(_LOW_LEVEL_QUOTE + "(.)") 11687757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 11697757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochdef mask_matches(nick, mask): 11707757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Check if a nick matches a mask. 11717757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 11727757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Returns true if the nick matches, otherwise false. 11737757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 11747757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch nick = irc_lower(nick) 11757757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch mask = irc_lower(mask) 11767757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch mask = mask.replace("\\", "\\\\") 11777757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch for ch in ".$|[](){}+": 11787757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch mask = mask.replace(ch, "\\" + ch) 11797757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch mask = mask.replace("?", ".") 11807757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch mask = mask.replace("*", ".*") 11817757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch r = re.compile(mask, re.IGNORECASE) 11827757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return r.match(nick) 11837757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 11847757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch_special = "-[]\\`^{}" 11857757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochnick_characters = string.ascii_letters + string.digits + _special 11867757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch_ircstring_translation = string.maketrans(string.ascii_uppercase + "[]\\^", 11877757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch string.ascii_lowercase + "{}|~") 11887757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 11897757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochdef irc_lower(s): 11907757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Returns a lowercased string. 11917757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 11927757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch The definition of lowercased comes from the IRC specification (RFC 11937757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 1459). 11947757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 11957757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return s.translate(_ircstring_translation) 11967757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 11977757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochdef _ctcp_dequote(message): 11987757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """[Internal] Dequote a message according to CTCP specifications. 11997757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 12007757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch The function returns a list where each element can be either a 12017757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch string (normal message) or a tuple of one or two strings (tagged 12027757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch messages). If a tuple has only one element (ie is a singleton), 12037757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch that element is the tag; otherwise the tuple has two elements: the 12047757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch tag and the data. 12057757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 12067757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Arguments: 12077757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 12087757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch message -- The message to be decoded. 12097757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 12107757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 12117757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def _low_level_replace(match_obj): 12127757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch ch = match_obj.group(1) 12137757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 12147757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # If low_level_mapping doesn't have the character as key, we 12157757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # should just return the character. 12167757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return _low_level_mapping.get(ch, ch) 12177757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 12187757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if _LOW_LEVEL_QUOTE in message: 12197757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # Yup, there was a quote. Release the dequoter, man! 12207757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch message = _low_level_regexp.sub(_low_level_replace, message) 12217757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 12227757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if _CTCP_DELIMITER not in message: 12237757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return [message] 12247757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch else: 12257757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # Split it into parts. (Does any IRC client actually *use* 12267757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # CTCP stacking like this?) 12277757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch chunks = message.split(_CTCP_DELIMITER) 12287757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 12297757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch messages = [] 12307757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch i = 0 12317757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch while i < len(chunks)-1: 12327757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # Add message if it's non-empty. 12337757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if len(chunks[i]) > 0: 12347757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch messages.append(chunks[i]) 12357757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 12367757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if i < len(chunks)-2: 12377757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # Aye! CTCP tagged data ahead! 12387757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch messages.append(tuple(chunks[i+1].split(" ", 1))) 12397757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 12407757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch i = i + 2 12417757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 12427757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if len(chunks) % 2 == 0: 12437757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # Hey, a lonely _CTCP_DELIMITER at the end! This means 12447757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # that the last chunk, including the delimiter, is a 12457757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # normal message! (This is according to the CTCP 12467757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # specification.) 12477757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch messages.append(_CTCP_DELIMITER + chunks[-1]) 12487757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 12497757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return messages 12507757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 12517757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochdef is_channel(string): 12527757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Check if a string is a channel name. 12537757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 12547757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Returns true if the argument is a channel name, otherwise false. 12557757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 12567757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return string and string[0] in "#&+!" 12577757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 12587757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochdef ip_numstr_to_quad(num): 12597757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Convert an IP number as an integer given in ASCII 12607757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch representation (e.g. '3232235521') to an IP address string 12617757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch (e.g. '192.168.0.1').""" 12627757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch n = long(num) 12637757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch p = map(str, map(int, [n >> 24 & 0xFF, n >> 16 & 0xFF, 12647757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch n >> 8 & 0xFF, n & 0xFF])) 12657757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return ".".join(p) 12667757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 12677757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochdef ip_quad_to_numstr(quad): 12687757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Convert an IP address string (e.g. '192.168.0.1') to an IP 12697757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch number as an integer given in ASCII representation 12707757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch (e.g. '3232235521').""" 12717757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch p = map(long, quad.split(".")) 12727757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch s = str((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]) 12737757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if s[-1] == "L": 12747757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch s = s[:-1] 12757757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return s 12767757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 12777757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochdef nm_to_n(s): 12787757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Get the nick part of a nickmask. 12797757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 12807757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch (The source of an Event is a nickmask.) 12817757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 12827757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return s.split("!")[0] 12837757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 12847757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochdef nm_to_uh(s): 12857757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Get the userhost part of a nickmask. 12867757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 12877757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch (The source of an Event is a nickmask.) 12887757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 12897757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return s.split("!")[1] 12907757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 12917757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochdef nm_to_h(s): 12927757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Get the host part of a nickmask. 12937757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 12947757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch (The source of an Event is a nickmask.) 12957757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 12967757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return s.split("@")[1] 12977757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 12987757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochdef nm_to_u(s): 12997757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Get the user part of a nickmask. 13007757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 13017757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch (The source of an Event is a nickmask.) 13027757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 13037757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch s = s.split("!")[1] 13047757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return s.split("@")[0] 13057757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 13067757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochdef parse_nick_modes(mode_string): 13077757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Parse a nick mode string. 13087757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 13097757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch The function returns a list of lists with three members: sign, 13107757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch mode and argument. The sign is \"+\" or \"-\". The argument is 13117757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch always None. 13127757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 13137757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Example: 13147757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 13157757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch >>> irclib.parse_nick_modes(\"+ab-c\") 13167757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch [['+', 'a', None], ['+', 'b', None], ['-', 'c', None]] 13177757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 13187757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 13197757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return _parse_modes(mode_string, "") 13207757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 13217757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochdef parse_channel_modes(mode_string): 13227757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Parse a channel mode string. 13237757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 13247757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch The function returns a list of lists with three members: sign, 13257757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch mode and argument. The sign is \"+\" or \"-\". The argument is 13267757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch None if mode isn't one of \"b\", \"k\", \"l\", \"v\" or \"o\". 13277757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 13287757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch Example: 13297757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 13307757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch >>> irclib.parse_channel_modes(\"+ab-c foo\") 13317757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch [['+', 'a', None], ['+', 'b', 'foo'], ['-', 'c', None]] 13327757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 13337757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 13347757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return _parse_modes(mode_string, "bklvo") 13357757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 13367757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochdef _parse_modes(mode_string, unary_modes=""): 13377757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """[Internal]""" 13387757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch modes = [] 13397757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch arg_count = 0 13407757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 13417757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # State variable. 13427757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch sign = "" 13437757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 13447757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch a = mode_string.split() 13457757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if len(a) == 0: 13467757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return [] 13477757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch else: 13487757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch mode_part, args = a[0], a[1:] 13497757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 13507757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if mode_part[0] not in "+-": 13517757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return [] 13527757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch for ch in mode_part: 13537757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if ch in "+-": 13547757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch sign = ch 13557757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch elif ch == " ": 13567757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch collecting_arguments = 1 13577757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch elif ch in unary_modes: 13587757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if len(args) >= arg_count + 1: 13597757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch modes.append([sign, ch, args[arg_count]]) 13607757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch arg_count = arg_count + 1 13617757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch else: 13627757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch modes.append([sign, ch, None]) 13637757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch else: 13647757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch modes.append([sign, ch, None]) 13657757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return modes 13667757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 13677757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochdef _ping_ponger(connection, event): 13687757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """[Internal]""" 13697757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch connection.pong(event.target()) 13707757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 13717757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# Numeric table mostly stolen from the Perl IRC module (Net::IRC). 13727757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochnumeric_events = { 13737757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "001": "welcome", 13747757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "002": "yourhost", 13757757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "003": "created", 13767757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "004": "myinfo", 13777757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "005": "featurelist", # XXX 13787757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "200": "tracelink", 13797757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "201": "traceconnecting", 13807757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "202": "tracehandshake", 13817757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "203": "traceunknown", 13827757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "204": "traceoperator", 13837757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "205": "traceuser", 13847757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "206": "traceserver", 13857757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "207": "traceservice", 13867757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "208": "tracenewtype", 13877757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "209": "traceclass", 13887757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "210": "tracereconnect", 13897757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "211": "statslinkinfo", 13907757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "212": "statscommands", 13917757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "213": "statscline", 13927757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "214": "statsnline", 13937757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "215": "statsiline", 13947757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "216": "statskline", 13957757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "217": "statsqline", 13967757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "218": "statsyline", 13977757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "219": "endofstats", 13987757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "221": "umodeis", 13997757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "231": "serviceinfo", 14007757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "232": "endofservices", 14017757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "233": "service", 14027757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "234": "servlist", 14037757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "235": "servlistend", 14047757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "241": "statslline", 14057757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "242": "statsuptime", 14067757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "243": "statsoline", 14077757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "244": "statshline", 14087757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "250": "luserconns", 14097757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "251": "luserclient", 14107757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "252": "luserop", 14117757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "253": "luserunknown", 14127757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "254": "luserchannels", 14137757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "255": "luserme", 14147757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "256": "adminme", 14157757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "257": "adminloc1", 14167757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "258": "adminloc2", 14177757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "259": "adminemail", 14187757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "261": "tracelog", 14197757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "262": "endoftrace", 14207757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "263": "tryagain", 14217757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "265": "n_local", 14227757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "266": "n_global", 14237757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "300": "none", 14247757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "301": "away", 14257757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "302": "userhost", 14267757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "303": "ison", 14277757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "305": "unaway", 14287757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "306": "nowaway", 14297757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "311": "whoisuser", 14307757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "312": "whoisserver", 14317757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "313": "whoisoperator", 14327757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "314": "whowasuser", 14337757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "315": "endofwho", 14347757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "316": "whoischanop", 14357757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "317": "whoisidle", 14367757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "318": "endofwhois", 14377757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "319": "whoischannels", 14387757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "321": "liststart", 14397757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "322": "list", 14407757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "323": "listend", 14417757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "324": "channelmodeis", 14427757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "329": "channelcreate", 14437757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "331": "notopic", 14447757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "332": "currenttopic", 14457757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "333": "topicinfo", 14467757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "341": "inviting", 14477757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "342": "summoning", 14487757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "346": "invitelist", 14497757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "347": "endofinvitelist", 14507757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "348": "exceptlist", 14517757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "349": "endofexceptlist", 14527757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "351": "version", 14537757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "352": "whoreply", 14547757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "353": "namreply", 14557757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "361": "killdone", 14567757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "362": "closing", 14577757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "363": "closeend", 14587757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "364": "links", 14597757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "365": "endoflinks", 14607757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "366": "endofnames", 14617757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "367": "banlist", 14627757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "368": "endofbanlist", 14637757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "369": "endofwhowas", 14647757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "371": "info", 14657757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "372": "motd", 14667757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "373": "infostart", 14677757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "374": "endofinfo", 14687757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "375": "motdstart", 14697757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "376": "endofmotd", 14707757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "377": "motd2", # 1997-10-16 -- tkil 14717757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "381": "youreoper", 14727757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "382": "rehashing", 14737757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "384": "myportis", 14747757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "391": "time", 14757757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "392": "usersstart", 14767757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "393": "users", 14777757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "394": "endofusers", 14787757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "395": "nousers", 14797757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "401": "nosuchnick", 14807757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "402": "nosuchserver", 14817757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "403": "nosuchchannel", 14827757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "404": "cannotsendtochan", 14837757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "405": "toomanychannels", 14847757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "406": "wasnosuchnick", 14857757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "407": "toomanytargets", 14867757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "409": "noorigin", 14877757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "411": "norecipient", 14887757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "412": "notexttosend", 14897757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "413": "notoplevel", 14907757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "414": "wildtoplevel", 14917757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "421": "unknowncommand", 14927757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "422": "nomotd", 14937757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "423": "noadmininfo", 14947757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "424": "fileerror", 14957757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "431": "nonicknamegiven", 14967757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "432": "erroneusnickname", # Thiss iz how its speld in thee RFC. 14977757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "433": "nicknameinuse", 14987757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "436": "nickcollision", 14997757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "437": "unavailresource", # "Nick temporally unavailable" 15007757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "441": "usernotinchannel", 15017757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "442": "notonchannel", 15027757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "443": "useronchannel", 15037757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "444": "nologin", 15047757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "445": "summondisabled", 15057757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "446": "usersdisabled", 15067757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "451": "notregistered", 15077757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "461": "needmoreparams", 15087757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "462": "alreadyregistered", 15097757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "463": "nopermforhost", 15107757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "464": "passwdmismatch", 15117757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "465": "yourebannedcreep", # I love this one... 15127757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "466": "youwillbebanned", 15137757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "467": "keyset", 15147757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "471": "channelisfull", 15157757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "472": "unknownmode", 15167757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "473": "inviteonlychan", 15177757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "474": "bannedfromchan", 15187757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "475": "badchannelkey", 15197757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "476": "badchanmask", 15207757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "477": "nochanmodes", # "Channel doesn't support modes" 15217757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "478": "banlistfull", 15227757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "481": "noprivileges", 15237757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "482": "chanoprivsneeded", 15247757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "483": "cantkillserver", 15257757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "484": "restricted", # Connection is restricted 15267757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "485": "uniqopprivsneeded", 15277757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "491": "nooperhost", 15287757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "492": "noservicehost", 15297757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "501": "umodeunknownflag", 15307757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "502": "usersdontmatch", 15317757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch} 15327757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 15337757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochgenerated_events = [ 15347757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # Generated events 15357757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "dcc_connect", 15367757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "dcc_disconnect", 15377757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "dccmsg", 15387757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "disconnect", 15397757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "ctcp", 15407757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "ctcpreply", 15417757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch] 15427757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 15437757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochprotocol_events = [ 15447757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # IRC protocol events 15457757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "error", 15467757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "join", 15477757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "kick", 15487757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "mode", 15497757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "part", 15507757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "ping", 15517757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "privmsg", 15527757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "privnotice", 15537757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "pubmsg", 15547757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "pubnotice", 15557757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "quit", 15567757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "invite", 15577757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "pong", 15587757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch] 15597757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 15607757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochall_events = generated_events + protocol_events + numeric_events.values() 1561