summaryrefslogtreecommitdiffstats
path: root/Synapse
diff options
context:
space:
mode:
authortv <tv@xso>2011-09-05 20:45:56 +0200
committertv <tv@xso>2011-09-05 20:45:56 +0200
commit2f5655ced03243ef03eb3417c9f090117568a885 (patch)
tree968bb8da4ae7508bd30371c13a239edfc7728d9d /Synapse
parent34b55bc3cf29cdfbaa40c494e3d2011855f15cc8 (diff)
//Synapse: emergency commit^_^
Diffstat (limited to 'Synapse')
-rwxr-xr-xSynapse/bot.py34
-rwxr-xr-xSynapse/bot2.py73
-rw-r--r--Synapse/content1
-rwxr-xr-xSynapse/index9
-rwxr-xr-xSynapse/install27
-rw-r--r--Synapse/irclib.py1550
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.
+<