diff options
| author | tv <tv@xso> | 2011-09-05 20:45:56 +0200 | 
|---|---|---|
| committer | tv <tv@xso> | 2011-09-05 20:45:56 +0200 | 
| commit | 2f5655ced03243ef03eb3417c9f090117568a885 (patch) | |
| tree | 968bb8da4ae7508bd30371c13a239edfc7728d9d /Synapse | |
| parent | 34b55bc3cf29cdfbaa40c494e3d2011855f15cc8 (diff) | |
//Synapse: emergency commit^_^
Diffstat (limited to 'Synapse')
| -rwxr-xr-x | Synapse/bot.py | 34 | ||||
| -rwxr-xr-x | Synapse/bot2.py | 73 | ||||
| -rw-r--r-- | Synapse/content | 1 | ||||
| -rwxr-xr-x | Synapse/index | 9 | ||||
| -rwxr-xr-x | Synapse/install | 27 | ||||
| -rw-r--r-- | Synapse/irclib.py | 1550 | 
6 files changed, 1694 insertions, 0 deletions
diff --git a/Synapse/bot.py b/Synapse/bot.py new file mode 100755 index 00000000..af974f4e --- /dev/null +++ b/Synapse/bot.py @@ -0,0 +1,34 @@ +#! /usr/bin/env python2 + +from irclib import IRC, ServerConnectionError, is_channel +from sys import exit +from os import environ as env + +host = str(env.get('host', 'irc.freenode.org')) +port = int(env.get('port', 6667)) +nick = str(env.get('nick', 'crabspasm')) +channel = str(env.get('channel', '#tincspasm')) +print '====> irc://%s@%s:%s/%s' % (nick, host, port, channel) + +irc = IRC() +try: +  client = irc.server().connect(host, port, nick) +except ServerConnectionError, error: +  print error +  exit + +def on_connect(connection, event): +  connection.join(channel) +  print 'Es passiert...' + +def on_join(connection, event): +  connection.privmsg(channel, 'lol') + +def on_disconnect(connection, event): +  exit + +client.add_global_handler('welcome', on_connect) +client.add_global_handler('join', on_join) +client.add_global_handler('disconnect', on_disconnect) + +irc.process_forever() diff --git a/Synapse/bot2.py b/Synapse/bot2.py new file mode 100755 index 00000000..63db57cb --- /dev/null +++ b/Synapse/bot2.py @@ -0,0 +1,73 @@ +#! /usr/bin/env python + +from irclib import SimpleIRCClient, ServerConnectionError, is_channel +from sys import exit +from os import environ as env +import tokenize +import cStringIO +import pprint +import re +pp = pprint.PrettyPrinter(indent=2) + +class IRCBot(SimpleIRCClient): +  def __init__(self, target): +    SimpleIRCClient.__init__(self) +    self.target = target + +  def on_welcome(self, connection, event): +    print 'I\'m welcome! :D joining to %s now...' % (self.target) +    if is_channel(self.target): +      connection.join(self.target) +    else: +      self.connection.privmsg(self.target, 'lol') +      self.connection.quit('Pong timeout: 423 seconds') + +  def on_join(self, connection, event): +    print 'Es passiert in %s' % (self.target) + +  def on_disconnect(self, connection, event): +    exit(0) + +  def on_pubmsg(self, connection, event): +    arguments = ''.join(event.arguments()) +    nickname = connection.get_nickname() +    server_name = connection.get_server_name() +    is_connected = connection.is_connected() +    #readline = cStringIO.StringIO(''.join(event.arguments())).readline +    #self.connection.privmsg(self.target, 'nickname: ' + nickname) +    #self.connection.privmsg(self.target, 'server_name: ' + server_name) + +    arguments = re.split(':\s+', arguments, 1) +    if len(arguments) != 2: +      return + +    target, arguments = arguments + +    arguments = re.split('\s+', arguments, 1) +    if len(arguments) != 2: +      return + +    command, arguments = arguments + +    self.connection.privmsg(self.target, '- target: ' + target) +    self.connection.privmsg(self.target, '- command: ' + command) +    self.connection.privmsg(self.target, '- arguments: ' + pp.pformat(arguments)) +     + +def main(): +  host = str(env.get('host', 'irc.freenode.org')) +  port = int(env.get('port', 6667)) +  nick = str(env.get('nick', 'crabspasm')) +  target = str(env.get('target', '#tincspasm')) +  print '====> irc://%s@%s:%s/%s' % (nick, host, port, target) + +  client = IRCBot(target) +  try: +    client.connect(host, port, nick) +  except ServerConnectionError, error: +    print error +    exit(1) +  client.start() + +if __name__ == "__main__": +    main() diff --git a/Synapse/content b/Synapse/content new file mode 100644 index 00000000..e0292376 --- /dev/null +++ b/Synapse/content @@ -0,0 +1 @@ +python-irclib-0.4.6/ircbot.py diff --git a/Synapse/index b/Synapse/index new file mode 100755 index 00000000..c8b8bcfb --- /dev/null +++ b/Synapse/index @@ -0,0 +1,9 @@ +#! /bin/sh +set -xeuf + +cd $(dirname $(readlink -f $0)) + +./install + +export PYTHONPATH="$PWD" +exec python2 bot2.py "$@" diff --git a/Synapse/install b/Synapse/install new file mode 100755 index 00000000..95e05199 --- /dev/null +++ b/Synapse/install @@ -0,0 +1,27 @@ +#! /bin/sh +set -xeuf + +cd $(dirname $(readlink -f $0)) + +# install irclib.py +{ +  PV=0.4.6 +  PN=python-irclib +  P=$PN-$PV +  tarball=$P.tar.gz +  URL=http://downloads.sourceforge.net/$PN/$tarball +  SHA1SUM=c6271e44293ed51c21af0f44ce106667d3006e6f + +  file=irclib.py + +  if ! echo "$SHA1SUM  $file" | sha1sum -c; then +    temp=`mktemp` +    trap "rm -f $temp" EXIT INT + +    echo $P/$file > $temp +    curl -LfsS $URL | tar --strip-components=1 -zxT $temp +  fi +  echo "$SHA1SUM  $file" | sha1sum -c +} + + diff --git a/Synapse/irclib.py b/Synapse/irclib.py new file mode 100644 index 00000000..c072ecc7 --- /dev/null +++ b/Synapse/irclib.py @@ -0,0 +1,1550 @@ +# Copyright (C) 1999--2002  Joel Rosdahl +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA +# +# keltus <keltus@users.sourceforge.net> +# +# $Id: irclib.py,v 1.43 2005/12/24 22:12:40 keltus Exp $ + +"""irclib -- Internet Relay Chat (IRC) protocol client library. + +This library is intended to encapsulate the IRC protocol at a quite +low level.  It provides an event-driven IRC client framework.  It has +a fairly thorough support for the basic IRC protocol, CTCP, DCC chat, +but DCC file transfers is not yet supported. + +In order to understand how to make an IRC client, I'm afraid you more +or less must understand the IRC specifications.  They are available +here: [IRC specifications]. + +The main features of the IRC client framework are: + +  * Abstraction of the IRC protocol. +  * Handles multiple simultaneous IRC server connections. +  * Handles server PONGing transparently. +  * Messages to the IRC server are done by calling methods on an IRC +    connection object. +  * Messages from an IRC server triggers events, which can be caught +    by event handlers. +  * Reading from and writing to IRC server sockets are normally done +    by an internal select() loop, but the select()ing may be done by +    an external main loop. +  * Functions can be registered to execute at specified times by the +    event-loop. +  * Decodes CTCP tagging correctly (hopefully); I haven't seen any +    other IRC client implementation that handles the CTCP +    specification subtilties. +  * A kind of simple, single-server, object-oriented IRC client class +    that dispatches events to instance methods is included. + +Current limitations: + +  * The IRC protocol shines through the abstraction a bit too much. +  * Data is not written asynchronously to the server, i.e. the write() +    may block if the TCP buffers are stuffed. +  * There are no support for DCC file transfers. +  * The author haven't even read RFC 2810, 2811, 2812 and 2813. +  * Like most projects, documentation is lacking... + +.. [IRC specifications] http://www.irchelp.org/irchelp/rfc/ +""" + +import bisect +import re +import select +import socket +import string +import sys +import time +import types + +VERSION = 0, 4, 6 +DEBUG = 0 + +# TODO +# ---- +# (maybe) thread safety +# (maybe) color parser convenience functions +# documentation (including all event types) +# (maybe) add awareness of different types of ircds +# send data asynchronously to the server (and DCC connections) +# (maybe) automatically close unused, passive DCC connections after a while + +# NOTES +# ----- +# connection.quit() only sends QUIT to the server. +# ERROR from the server triggers the error event and the disconnect event. +# dropping of the connection triggers the disconnect event. + +class IRCError(Exception): +    """Represents an IRC exception.""" +    pass + + +class IRC: +    """Class that handles one or several IRC server connections. + +    When an IRC object has been instantiated, it can be used to create +    Connection objects that represent the IRC connections.  The +    responsibility of the IRC object is to provide an event-driven +    framework for the connections and to keep the connections alive. +    It runs a select loop to poll each connection's TCP socket and +    hands over the sockets with incoming data for processing by the +    corresponding connection. + +    The methods of most interest for an IRC client writer are server, +    add_global_handler, remove_global_handler, execute_at, +    execute_delayed, process_once and process_forever. + +    Here is an example: + +        irc = irclib.IRC() +        server = irc.server() +        server.connect(\"irc.some.where\", 6667, \"my_nickname\") +        server.privmsg(\"a_nickname\", \"Hi there!\") +        irc.process_forever() + +    This will connect to the IRC server irc.some.where on port 6667 +    using the nickname my_nickname and send the message \"Hi there!\" +    to the nickname a_nickname. +    """ + +    def __init__(self, fn_to_add_socket=None, +                 fn_to_remove_socket=None, +                 fn_to_add_timeout=None): +        """Constructor for IRC objects. + +        Optional arguments are fn_to_add_socket, fn_to_remove_socket +        and fn_to_add_timeout.  The first two specify functions that +        will be called with a socket object as argument when the IRC +        object wants to be notified (or stop being notified) of data +        coming on a new socket.  When new data arrives, the method +        process_data should be called.  Similarly, fn_to_add_timeout +        is called with a number of seconds (a floating point number) +        as first argument when the IRC object wants to receive a +        notification (by calling the process_timeout method).  So, if +        e.g. the argument is 42.17, the object wants the +        process_timeout method to be called after 42 seconds and 170 +        milliseconds. + +        The three arguments mainly exist to be able to use an external +        main loop (for example Tkinter's or PyGTK's main app loop) +        instead of calling the process_forever method. + +        An alternative is to just call ServerConnection.process_once() +        once in a while. +        """ + +        if fn_to_add_socket and fn_to_remove_socket: +            self.fn_to_add_socket = fn_to_add_socket +            self.fn_to_remove_socket = fn_to_remove_socket +        else: +            self.fn_to_add_socket = None +            self.fn_to_remove_socket = None + +        self.fn_to_add_timeout = fn_to_add_timeout +        self.connections = [] +        self.handlers = {} +        self.delayed_commands = [] # list of tuples in the format (time, function, arguments) + +        self.add_global_handler("ping", _ping_ponger, -42) + +    def server(self): +        """Creates and returns a ServerConnection object.""" + +        c = ServerConnection(self) +        self.connections.append(c) +        return c + +    def process_data(self, sockets): +        """Called when there is more data to read on connection sockets. + +        Arguments: + +            sockets -- A list of socket objects. + +        See documentation for IRC.__init__. +        """ +        for s in sockets: +            for c in self.connections: +                if s == c._get_socket(): +                    c.process_data() + +    def process_timeout(self): +        """Called when a timeout notification is due. + +        See documentation for IRC.__init__. +        """ +        t = time.time() +        while self.delayed_commands: +            if t >= self.delayed_commands[0][0]: +                self.delayed_commands[0][1](*self.delayed_commands[0][2]) +                del self.delayed_commands[0] +            else: +                break + +    def process_once(self, timeout=0): +        """Process data from connections once. + +        Arguments: + +            timeout -- How long the select() call should wait if no +                       data is available. + +        This method should be called periodically to check and process +        incoming data, if there are any.  If that seems boring, look +        at the process_forever method. +        """ +        sockets = map(lambda x: x._get_socket(), self.connections) +        sockets = filter(lambda x: x != None, sockets) +        if sockets: +            (i, o, e) = select.select(sockets, [], [], timeout) +            self.process_data(i) +        else: +            time.sleep(timeout) +        self.process_timeout() + +    def process_forever(self, timeout=0.2): +        """Run an infinite loop, processing data from connections. + +        This method repeatedly calls process_once. + +        Arguments: + +            timeout -- Parameter to pass to process_once. +        """ +        while 1: +            self.process_once(timeout) + +    def disconnect_all(self, message=""): +        """Disconnects all connections.""" +        for c in self.connections: +            c.disconnect(message) + +    def add_global_handler(self, event, handler, priority=0): +        """Adds a global handler function for a specific event type. + +        Arguments: + +            event -- Event type (a string).  Check the values of the +            numeric_events dictionary in irclib.py for possible event +            types. + +            handler -- Callback function. + +            priority -- A number (the lower number, the higher priority). + +        The handler function is called whenever the specified event is +        triggered in any of the connections.  See documentation for +        the Event class. + +        The handler functions are called in priority order (lowest +        number is highest priority).  If a handler function returns +        \"NO MORE\", no more handlers will be called. +        """ + +        if not event in self.handlers: +            self.handlers[event] = [] +        bisect.insort(self.handlers[event], ((priority, handler))) + +    def remove_global_handler(self, event, handler): +        """Removes a global handler function. + +        Arguments: + +            event -- Event type (a string). + +            handler -- Callback function. + +        Returns 1 on success, otherwise 0. +        """ +        if not event in self.handlers: +            return 0 +        for h in self.handlers[event]: +            if handler == h[1]: +                self.handlers[event].remove(h) +        return 1 + +    def execute_at(self, at, function, arguments=()): +        """Execute a function at a specified time. + +        Arguments: + +            at -- Execute at this time (standard \"time_t\" time). + +            function -- Function to call. + +            arguments -- Arguments to give the function. +        """ +        self.execute_delayed(at-time.time(), function, arguments) + +    def execute_delayed(self, delay, function, arguments=()): +        """Execute a function after a specified time. + +        Arguments: + +            delay -- How many seconds to wait. + +            function -- Function to call. + +            arguments -- Arguments to give the function. +        """ +        bisect.insort(self.delayed_commands, (delay+time.time(), function, arguments)) +        if self.fn_to_add_timeout: +            self.fn_to_add_timeout(delay) + +    def dcc(self, dcctype="chat"): +        """Creates and returns a DCCConnection object. + +        Arguments: + +            dcctype -- "chat" for DCC CHAT connections or "raw" for +                       DCC SEND (or other DCC types). If "chat", +                       incoming data will be split in newline-separated +                       chunks. If "raw", incoming data is not touched. +        """ +        c = DCCConnection(self, dcctype) +        self.connections.append(c) +        return c + +    def _handle_event(self, connection, event): +        """[Internal]""" +        h = self.handlers +        for handler in h.get("all_events", []) + h.get(event.eventtype(), []): +            if handler[1](connection, event) == "NO MORE": +                return + +    def _remove_connection(self, connection): +        """[Internal]""" +        self.connections.remove(connection) +        if self.fn_to_remove_socket: +            self.fn_to_remove_socket(connection._get_socket()) + +_rfc_1459_command_regexp = re.compile("^(:(?P<prefix>[^ ]+) +)?(?P<command>[^ ]+)( *(?P<argument> .+))?") + + +class Connection: +    """Base class for IRC connections. + +    Must be overridden. +    """ +    def __init__(self, irclibobj): +        self.irclibobj = irclibobj + +    def _get_socket(): +        raise IRCError, "Not overridden" + +    ############################## +    ### Convenience wrappers. + +    def execute_at(self, at, function, arguments=()): +        self.irclibobj.execute_at(at, function, arguments) + +    def execute_delayed(self, delay, function, arguments=()): +        self.irclibobj.execute_delayed(delay, function, arguments) + + +class ServerConnectionError(IRCError): +    pass + +class ServerNotConnectedError(ServerConnectionError): +    pass + + +# Huh!?  Crrrrazy EFNet doesn't follow the RFC: their ircd seems to +# use \n as message separator!  :P +_linesep_regexp = re.compile("\r?\n") + +class ServerConnection(Connection): +    """This class represents an IRC server connection. + +    ServerConnection objects are instantiated by calling the server +    method on an IRC object. +    """ + +    def __init__(self, irclibobj): +        Connection.__init__(self, irclibobj) +        self.connected = 0  # Not connected yet. +        self.socket = None + +    def connect(self, server, port, nickname, password=None, username=None, +                ircname=None, localaddress="", localport=0): +        """Connect/reconnect to a server. + +        Arguments: + +            server -- Server name. + +            port -- Port number. + +            nickname -- The nickname. + +            password -- Password (if any). + +            username -- The username. + +            ircname -- The IRC name ("realname"). + +            localaddress -- Bind the connection to a specific local IP address. + +            localport -- Bind the connection to a specific local port. + +        This function can be called to reconnect a closed connection. + +        Returns the ServerConnection object. +        """ +        if self.connected: +            self.disconnect("Changing servers") + +        self.previous_buffer = "" +        self.handlers = {} +        self.real_server_name = "" +        self.real_nickname = nickname +        self.server = server +        self.port = port +        self.nickname = nickname +        self.username = username or nickname +        self.ircname = ircname or nickname +        self.password = password +        self.localaddress = localaddress +        self.localport = localport +        self.localhost = socket.gethostname() +        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +        try: +            self.socket.bind((self.localaddress, self.localport)) +            self.socket.connect((self.server, self.port)) +        except socket.error, x: +            self.socket.close() +            self.socket = None +            raise ServerConnectionError, "Couldn't connect to socket: %s" % x +        self.connected = 1 +        if self.irclibobj.fn_to_add_socket: +            self.irclibobj.fn_to_add_socket(self.socket) + +        # Log on... +        if self.password: +            self.pass_(self.password) +        self.nick(self.nickname) +        self.user(self.username, self.ircname) +        return self + +    def close(self): +        """Close the connection. + +        This method closes the connection permanently; after it has +        been called, the object is unusable. +        """ + +        self.disconnect("Closing object") +        self.irclibobj._remove_connection(self) + +    def _get_socket(self): +        """[Internal]""" +        return self.socket + +    def get_server_name(self): +        """Get the (real) server name. + +        This method returns the (real) server name, or, more +        specifically, what the server calls itself. +        """ + +        if self.real_server_name: +            return self.real_server_name +        else: +            return "" + +    def get_nickname(self): +        """Get the (real) nick name. + +        This method returns the (real) nickname.  The library keeps +        track of nick changes, so it might not be the nick name that +        was passed to the connect() method.  """ + +        return self.real_nickname + +    def process_data(self): +        """[Internal]""" + +        try: +            new_data = self.socket.recv(2**14) +        except socket.error, x: +            # The server hung up. +            self.disconnect("Connection reset by peer") +            return +        if not new_data: +            # Read nothing: connection must be down. +            self.disconnect("Connection reset by peer") +            return + +        lines = _linesep_regexp.split(self.previous_buffer + new_data) + +        # Save the last, unfinished line. +        self.previous_buffer = lines[-1] +        lines = lines[:-1] + +        for line in lines: +            if DEBUG: +                print "FROM SERVER:", line + +            if not line: +                continue + +            prefix = None +            command = None +            arguments = None +            self._handle_event(Event("all_raw_messages", +                                     self.get_server_name(), +                                     None, +                                     [line])) + +            m = _rfc_1459_command_regexp.match(line) +            if m.group("prefix"): +                prefix = m.group("prefix") +                if not self.real_server_name: +                    self.real_server_name = prefix + +            if m.group("command"): +                command = m.group("command").lower() + +            if m.group("argument"): +                a = m.group("argument").split(" :", 1) +                arguments = a[0].split() +                if len(a) == 2: +                    arguments.append(a[1]) + +            # Translate numerics into more readable strings. +            if command in numeric_events: +                command = numeric_events[command] + +            if command == "nick": +                if nm_to_n(prefix) == self.real_nickname: +                    self.real_nickname = arguments[0] +            elif command == "welcome": +                # Record the nickname in case the client changed nick +                # in a nicknameinuse callback. +                self.real_nickname = arguments[0] + +            if command in ["privmsg", "notice"]: +                target, message = arguments[0], arguments[1] +                messages = _ctcp_dequote(message) + +                if command == "privmsg": +                    if is_channel(target): +                        command = "pubmsg" +                else: +                    if is_channel(target): +                        command = "pubnotice" +                    else: +                        command = "privnotice" + +                for m in messages: +                    if type(m) is types.TupleType: +                        if command in ["privmsg", "pubmsg"]: +                            command = "ctcp" +                        else: +                            command = "ctcpreply" + +                        m = list(m) +                        if DEBUG: +                            print "command: %s, source: %s, target: %s, arguments: %s" % ( +                                command, prefix, target, m) +                        self._handle_event(Event(command, prefix, target, m)) +                        if command == "ctcp" and m[0] == "ACTION": +                            self._handle_event(Event("action", prefix, target, m[1:])) +                    else: +                        if DEBUG: +                            print "command: %s, source: %s, target: %s, arguments: %s" % ( +                                command, prefix, target, [m]) +                        self._handle_event(Event(command, prefix, target, [m])) +            else: +                target = None + +                if command == "quit": +                    arguments = [arguments[0]] +                elif command == "ping": +                    target = arguments[0] +                else: +                    target = arguments[0] +                    arguments = arguments[1:] + +                if command == "mode": +                    if not is_channel(target): +                        command = "umode" + +                if DEBUG: +                    print "command: %s, source: %s, target: %s, arguments: %s" % ( +                        command, prefix, target, arguments) +                self._handle_event(Event(command, prefix, target, arguments)) + +    def _handle_event(self, event): +        """[Internal]""" +        self.irclibobj._handle_event(self, event) +        if event.eventtype() in self.handlers: +            for fn in self.handlers[event.eventtype()]: +                fn(self, event) + +    def is_connected(self): +        """Return connection status. + +        Returns true if connected, otherwise false. +        """ +        return self.connected + +    def add_global_handler(self, *args): +        """Add global handler. + +        See documentation for IRC.add_global_handler. +        """ +        self.irclibobj.add_global_handler(*args) + +    def remove_global_handler(self, *args): +        """Remove global handler. + +        See documentation for IRC.remove_global_handler. +        """ +        self.irclibobj.remove_global_handler(*args) + +    def action(self, target, action): +        """Send a CTCP ACTION command.""" +        self.ctcp("ACTION", target, action) + +    def admin(self, server=""): +        """Send an ADMIN command.""" +        self.send_raw(" ".join(["ADMIN", server]).strip()) + +    def ctcp(self, ctcptype, target, parameter=""): +        """Send a CTCP command.""" +        ctcptype = ctcptype.upper() +        self.privmsg(target, "\001%s%s\001" % (ctcptype, parameter and (" " + parameter) or "")) + +    def ctcp_reply(self, target, parameter): +        """Send a CTCP REPLY command.""" +        self.notice(target, "\001%s\001" % parameter) + +    def disconnect(self, message=""): +        """Hang up the connection. + +        Arguments: + +            message -- Quit message. +        """ +        if not self.connected: +            return + +        self.connected = 0 + +        self.quit(message) + +        try: +            self.socket.close() +        except socket.error, x: +            pass +        self.socket = None +        self._handle_event(Event("disconnect", self.server, "", [message])) + +    def globops(self, text): +        """Send a GLOBOPS command.""" +        self.send_raw("GLOBOPS :" + text) + +    def info(self, server=""): +        """Send an INFO command.""" +        self.send_raw(" ".join(["INFO", server]).strip()) + +    def invite(self, nick, channel): +        """Send an INVITE command.""" +        self.send_raw(" ".join(["INVITE", nick, channel]).strip()) + +    def ison(self, nicks): +        """Send an ISON command. + +        Arguments: + +            nicks -- List of nicks. +        """ +        self.send_raw("ISON " + " ".join(nicks)) + +    def join(self, channel, key=""): +        """Send a JOIN command.""" +        self.send_raw("JOIN %s%s" % (channel, (key and (" " + key)))) + +    def kick(self, channel, nick, comment=""): +        """Send a KICK command.""" +        self.send_raw("KICK %s %s%s" % (channel, nick, (comment and (" :" + comment)))) + +    def links(self, remote_server="", server_mask=""): +        """Send a LINKS command.""" +        command = "LINKS" +        if remote_server: +            command = command + " " + remote_server +        if server_mask: +            command = command + " " + server_mask +        self.send_raw(command) + +    def list(self, channels=None, server=""): +        """Send a LIST command.""" +        command = "LIST" +        if channels: +            command = command + " " + ",".join(channels) +        if server: +            command = command + " " + server +        self.send_raw(command) + +    def lusers(self, server=""): +        """Send a LUSERS command.""" +        self.send_raw("LUSERS" + (server and (" " + server))) + +    def mode(self, target, command): +        """Send a MODE command.""" +        self.send_raw("MODE %s %s" % (target, command)) + +    def motd(self, server=""): +        """Send an MOTD command.""" +        self.send_raw("MOTD" + (server and (" " + server))) + +    def names(self, channels=None): +        """Send a NAMES command.""" +        self.send_raw("NAMES" + (channels and (" " + ",".join(channels)) or "")) + +    def nick(self, newnick): +        """Send a NICK command.""" +        self.send_raw("NICK " + newnick) + +    def notice(self, target, text): +        """Send a NOTICE command.""" +        # Should limit len(text) here! +        self.send_raw("NOTICE %s :%s" % (target, text)) + +    def oper(self, nick, password): +        """Send an OPER command.""" +        self.send_raw("OPER %s %s" % (nick, password)) + +    def part(self, channels, message=""): +        """Send a PART command.""" +        if type(channels) == types.StringType: +            self.send_raw("PART " + channels + (message and (" " + message))) +        else: +            self.send_raw("PART " + ",".join(channels) + (message and (" " + message))) + +    def pass_(self, password): +        """Send a PASS command.""" +        self.send_raw("PASS " + password) + +    def ping(self, target, target2=""): +        """Send a PING command.""" +        self.send_raw("PING %s%s" % (target, target2 and (" " + target2))) + +    def pong(self, target, target2=""): +        """Send a PONG command.""" +        self.send_raw("PONG %s%s" % (target, target2 and (" " + target2))) + +    def privmsg(self, target, text): +        """Send a PRIVMSG command.""" +        # Should limit len(text) here! +        self.send_raw("PRIVMSG %s :%s" % (target, text)) + +    def privmsg_many(self, targets, text): +        """Send a PRIVMSG command to multiple targets.""" +        # Should limit len(text) here! +        self.send_raw("PRIVMSG %s :%s" % (",".join(targets), text)) + +    def quit(self, message=""): +        """Send a QUIT command.""" +        # Note that many IRC servers don't use your QUIT message +        # unless you've been connected for at least 5 minutes! +        self.send_raw("QUIT" + (message and (" :" + message))) + +    def sconnect(self, target, port="", server=""): +        """Send an SCONNECT command.""" +        self.send_raw("CONNECT %s%s%s" % (target, +                                          port and (" " + port), +                                          server and (" " + server))) + +    def send_raw(self, string): +        """Send raw string to the server. + +        The string will be padded with appropriate CR LF. +        """ +        if self.socket is None: +            raise ServerNotConnectedError, "Not connected." +        try: +            self.socket.send(string + "\r\n") +            if DEBUG: +                print "TO SERVER:", string +        except socket.error, x: +            # Ouch! +            self.disconnect("Connection reset by peer.") + +    def squit(self, server, comment=""): +        """Send an SQUIT command.""" +        self.send_raw("SQUIT %s%s" % (server, comment and (" :" + comment))) + +    def stats(self, statstype, server=""): +        """Send a STATS command.""" +        self.send_raw("STATS %s%s" % (statstype, server and (" " + server))) + +    def time(self, server=""): +        """Send a TIME command.""" +        self.send_raw("TIME" + (server and (" " + server))) + +    def topic(self, channel, new_topic=None): +        """Send a TOPIC command.""" +        if new_topic is None: +            self.send_raw("TOPIC " + channel) +        else: +            self.send_raw("TOPIC %s :%s" % (channel, new_topic)) + +    def trace(self, target=""): +        """Send a TRACE command.""" +        self.send_raw("TRACE" + (target and (" " + target))) + +    def user(self, username, realname): +        """Send a USER command.""" +        self.send_raw("USER %s 0 * :%s" % (username, realname)) + +    def userhost(self, nicks): +        """Send a USERHOST command.""" +        self.send_raw("USERHOST " + ",".join(nicks)) + +    def users(self, server=""): +        """Send a USERS command.""" +        self.send_raw("USERS" + (server and (" " + server))) + +    def version(self, server=""): +        """Send a VERSION command.""" +        self.send_raw("VERSION" + (server and (" " + server))) + +    def wallops(self, text): +        """Send a WALLOPS command.""" +        self.send_raw("WALLOPS :" + text) + +    def who(self, target="", op=""): +        """Send a WHO command.""" +        self.send_raw("WHO%s%s" % (target and (" " + target), op and (" o"))) + +    def whois(self, targets): +        """Send a WHOIS command.""" +        self.send_raw("WHOIS " + ",".join(targets)) + +    def whowas(self, nick, max="", server=""): +        """Send a WHOWAS command.""" +        self.send_raw("WHOWAS %s%s%s" % (nick, +                                         max and (" " + max), +                                         server and (" " + server))) + + +class DCCConnectionError(IRCError): +    pass + + +class DCCConnection(Connection): +    """This class represents a DCC connection. + +    DCCConnection objects are instantiated by calling the dcc +    method on an IRC object. +    """ +    def __init__(self, irc  | 
