|
@@ -1,32 +1,36 @@ |
|
|
# -*- coding: utf-8 -*- |
|
|
# -*- coding: utf-8 -*- |
|
|
|
|
|
|
|
|
# A class to interface with IRC. |
|
|
|
|
|
|
|
|
|
|
|
import socket |
|
|
import socket |
|
|
import threading |
|
|
import threading |
|
|
|
|
|
|
|
|
class BrokenSocketException(Exception): |
|
|
class BrokenSocketException(Exception): |
|
|
"""A socket has broken, because it is not sending data.""" |
|
|
|
|
|
|
|
|
"""A socket has broken, because it is not sending data. Raised by |
|
|
|
|
|
Connection.get().""" |
|
|
pass |
|
|
pass |
|
|
|
|
|
|
|
|
class Connection(object): |
|
|
class Connection(object): |
|
|
def __init__(self, host=None, port=None, nick=None, ident=None, realname=None): |
|
|
|
|
|
"""a class to interface with IRC""" |
|
|
|
|
|
|
|
|
"""A class to interface with IRC.""" |
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, host=None, port=None, nick=None, ident=None, |
|
|
|
|
|
realname=None): |
|
|
self.host = host |
|
|
self.host = host |
|
|
self.port = port |
|
|
self.port = port |
|
|
self.nick = nick |
|
|
self.nick = nick |
|
|
self.ident = ident |
|
|
self.ident = ident |
|
|
self.realname = realname |
|
|
self.realname = realname |
|
|
|
|
|
|
|
|
|
|
|
# A lock to prevent us from sending two messages at once. |
|
|
|
|
|
self.lock = threading.Lock() |
|
|
|
|
|
|
|
|
def connect(self): |
|
|
def connect(self): |
|
|
"""connect to IRC""" |
|
|
|
|
|
|
|
|
"""Connect to our IRC server.""" |
|
|
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
|
|
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
|
|
self.sock.connect((self.host, self.port)) |
|
|
self.sock.connect((self.host, self.port)) |
|
|
self.send("NICK %s" % self.nick) |
|
|
self.send("NICK %s" % self.nick) |
|
|
self.send("USER %s %s * :%s" % (self.ident, self.host, self.realname)) |
|
|
self.send("USER %s %s * :%s" % (self.ident, self.host, self.realname)) |
|
|
|
|
|
|
|
|
def close(self): |
|
|
def close(self): |
|
|
"""close our connection with IRC""" |
|
|
|
|
|
|
|
|
"""Close our connection with the IRC server.""" |
|
|
try: |
|
|
try: |
|
|
self.sock.shutdown(socket.SHUT_RDWR) # shut down connection first |
|
|
self.sock.shutdown(socket.SHUT_RDWR) # shut down connection first |
|
|
except socket.error: |
|
|
except socket.error: |
|
@@ -34,42 +38,52 @@ class Connection(object): |
|
|
self.sock.close() |
|
|
self.sock.close() |
|
|
|
|
|
|
|
|
def get(self, size=4096): |
|
|
def get(self, size=4096): |
|
|
"""receive (get) data from the server""" |
|
|
|
|
|
|
|
|
"""Receive (i.e. get) data from the server.""" |
|
|
data = self.sock.recv(4096) |
|
|
data = self.sock.recv(4096) |
|
|
if not data: # socket giving us no data, so it is dead/broken |
|
|
|
|
|
|
|
|
if not data: |
|
|
|
|
|
# Socket isn't giving us any data, so it is dead or broken: |
|
|
raise BrokenSocketException() |
|
|
raise BrokenSocketException() |
|
|
return data |
|
|
return data |
|
|
|
|
|
|
|
|
def send(self, msg): |
|
|
def send(self, msg): |
|
|
"""send data to the server""" |
|
|
|
|
|
lock = threading.Lock() |
|
|
|
|
|
lock.acquire() # ensure that we only send one message at a time (blocking) |
|
|
|
|
|
try: |
|
|
|
|
|
|
|
|
"""Send data to the server.""" |
|
|
|
|
|
# Ensure that we only send one message at a time with a blocking lock: |
|
|
|
|
|
with self.lock: |
|
|
self.sock.sendall(msg + "\r\n") |
|
|
self.sock.sendall(msg + "\r\n") |
|
|
print " %s" % msg |
|
|
print " %s" % msg |
|
|
finally: |
|
|
|
|
|
lock.release() |
|
|
|
|
|
|
|
|
|
|
|
def say(self, target, msg): |
|
|
def say(self, target, msg): |
|
|
"""send a message""" |
|
|
|
|
|
self.send("PRIVMSG %s :%s" % (target, msg)) |
|
|
|
|
|
|
|
|
"""Send a private message to a target on the server.""" |
|
|
|
|
|
message = "".join(("PRIVMSG ", target, " :", msg)) |
|
|
|
|
|
self.send(message) |
|
|
|
|
|
|
|
|
def reply(self, data, msg): |
|
|
def reply(self, data, msg): |
|
|
"""send a message as a reply""" |
|
|
|
|
|
self.say(data.chan, "%s%s%s: %s" % (chr(2), data.nick, chr(0x0f), msg)) |
|
|
|
|
|
|
|
|
"""Send a private message as a reply to a user on the server. `data` is |
|
|
|
|
|
a Data object (or anything with chan and nick attributes).""" |
|
|
|
|
|
message = "".join((chr(2), data.nick, chr(0x0f), ": ", msg)) |
|
|
|
|
|
self.say(data.chan, message) |
|
|
|
|
|
|
|
|
def action(self, target, msg): |
|
|
def action(self, target, msg): |
|
|
"""send a message as an action""" |
|
|
|
|
|
self.say(target,"%sACTION %s%s" % (chr(1), msg, chr(1))) |
|
|
|
|
|
|
|
|
"""Send a private message to a target on the server as an action.""" |
|
|
|
|
|
message = "".join((chr(1), "ACTION ", msg, chr(1))) |
|
|
|
|
|
self.say(target, message) |
|
|
|
|
|
|
|
|
def notice(self, target, msg): |
|
|
def notice(self, target, msg): |
|
|
"""send a notice""" |
|
|
|
|
|
self.send("NOTICE %s :%s" % (target, msg)) |
|
|
|
|
|
|
|
|
"""Send a notice to a target on the server.""" |
|
|
|
|
|
message = "".join(("NOTICE ", target, " :", msg)) |
|
|
|
|
|
self.send(message) |
|
|
|
|
|
|
|
|
def join(self, chan): |
|
|
def join(self, chan): |
|
|
"""join a channel""" |
|
|
|
|
|
self.send("JOIN %s" % chan) |
|
|
|
|
|
|
|
|
"""Join a channel on the server.""" |
|
|
|
|
|
message = " ".join(("JOIN", chan)) |
|
|
|
|
|
self.send(message) |
|
|
|
|
|
|
|
|
|
|
|
def part(self, chan): |
|
|
|
|
|
"""Part from a channel on the server.""" |
|
|
|
|
|
message = " ".join(("PART", chan)) |
|
|
|
|
|
self.send(message) |
|
|
|
|
|
|
|
|
def mode(self, chan, level, msg): |
|
|
def mode(self, chan, level, msg): |
|
|
"""send a mode message""" |
|
|
|
|
|
self.send("MODE %s %s %s" % (chan, level, msg)) |
|
|
|
|
|
|
|
|
"""Send a mode message to the server.""" |
|
|
|
|
|
message = " ".join(("MODE", chan, level, msg)) |
|
|
|
|
|
self.send(message) |