return func
return realhook
+ def bind(self, bindto):
+ """Used as a decorator on a class which implements getdata and parse methods.
+ See modules/sockets.py for an example.
+ Takes an arg like:
+ [unix:]/foo/bar
+ [udp|tcp:][ip:]port
+ """
+ if len(bindto) == 0:
+ raise Exception('bindto must have a value')
+ if bindto[0] == '/':
+ return self._hooksocket(socket.AF_UNIX, socket.SOCK_STREAM, bindto)
+ if len(bindto) > 5 and bindto[0:5] == 'unix:':
+ return self._hooksocket(socket.AF_UNIX, socket.SOCK_STREAM, bindto[5:])
+ af = socket.AF_INET
+ ty = socket.SOCK_STREAM
+ host = '0.0.0.0'
+ if len(bindto) > 4 and bindto[0:4] == 'udp:':
+ ty = socket.SOCK_DGRAM
+ bindto = bindto[4:]
+ if len(bindto) > 4 and bindto[0:4] == 'tcp:':
+ bindto = bindto[4:]
+ print(repr(bindto), ':' in bindto)
+ if ':' in bindto:
+ print(bindto)
+ pieces = bindto.rsplit(':', 1)
+ host = pieces[0]
+ bindto = pieces[1]
+ print(pieces,host,bindto)
+ port = int(bindto)
+ return self._hooksocket(af, ty, (host, port))
+
def bind_tcp(self, host, port):
return self._hooksocket(socket.AF_INET, socket.SOCK_STREAM, (host, port))
def bind_udp(self, host, port):
self.sockets.append((sock,obj))
sock.listen(5)
self.parent.newfd(obj, sock.fileno())
- self.parent.log(repr(obj), '?', 'Socket ready to accept new connections')
+ self.parent.log(repr(obj), '?', 'Socket ready to accept new connections (%r, %r, %r, %r)' % (af, ty, address, cls))
def _destroy_socket(self, sock, obj):
obj.close()
--- /dev/null
+# Erebus IRC bot - Author: Erebus Team
+# vim: fileencoding=utf-8
+# This file is released into the public domain; see http://unlicense.org/
+
+# Note: this module doesn't do any kind of authentication, so anyone who can connect to the bound port can spam you.
+
+# module info
+modinfo = {
+ 'author': 'Erebus Team',
+ 'license': 'public domain',
+ 'compatible': [0],
+ 'depends': [],
+ 'softdeps': [],
+}
+
+# preamble
+import modlib
+lib = modlib.modlib(__name__)
+def modstart(parent, *args, **kwargs):
+ gotParent(parent)
+ return lib.modstart(parent, *args, **kwargs)
+modstop = lib.modstop
+
+# module code
+
+def gotParent(parent):
+ for bindto, channel in parent.cfg.items('sockets'):
+ @lib.bind(bindto, data=channel)
+ class BasicServer(object):
+ def __init__(self, sock, data):
+ # The lambda bit is needed to make this copy the value-at-definition instead of using the closure value-at-runtime
+ self.chan = data
+ print(repr(self.chan))
+ self.buffer = b''
+ self.sock = sock
+
+ def getdata(self):
+ recvd = self.sock.recv(8192)
+ if recvd == b"": # EOF
+ if len(self.buffer) != 0:
+ # Process what's left in the buffer. We'll get called again after.
+ remaining_buf = self.buffer.decode('utf-8', 'backslashreplace')
+ self.buffer = b""
+ return [remaining_buf]
+ else:
+ # Nothing left in the buffer. Return None to signal the core to close this socket.
+ return None
+ self.buffer += recvd
+ lines = []
+
+ while b"\n" in self.buffer:
+ pieces = self.buffer.split(b"\n", 1)
+ s = pieces[0].decode('utf-8', 'backslashreplace').rstrip("\r")
+ lines.append(pieces[0].decode('utf-8', 'backslashreplace'))
+ self.buffer = pieces[1]
+
+ return lines
+
+ def parse(self, line):
+ bot = lib.parent.randbot()
+ maxlen = bot.maxmsglen() - len("PRIVMSG :") - len(self.chan)
+ while len(line) > maxlen:
+ cutat = line.rfind(' ', 0, maxlen)
+ if cutat == -1:
+ cutat = maxlen
+ bot.msg(self.chan, line[0:cutat])
+ line = line[cutat:].strip()
+ bot.msg(self.chan, line)
+
+ def send(self, line):
+ if lib.parent.parent.cfg.getboolean('debug', 'io'):
+ lib.parent.log(str(self), 'O', line)
+ self.sock.sendall(line.encode('utf-8', 'backslashreplace')+b"\r\n")
+
+ def _getsockerr(self):
+ try: # SO_ERROR might not exist on all platforms
+ return self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
+ except:
+ return None
+
+ def __str__(self):
+ return '%s#%d' % (__name__, self.sock.fileno())
+ def __repr__(self):
+ return '<%s #%d %s:%d>' % ((__name__, self.sock.fileno())+self.sock.getpeername())