]> jfr.im git - erebus.git/blobdiff - modules/sockets.py
admin_config - add !getconfig, remove some unused functions
[erebus.git] / modules / sockets.py
index cf79e70d36a912fc8e100e6936062cef8ec9e1a0..af650ed5f3a5a68e9e7dc7dc4ac748a5e77d7cda 100644 (file)
@@ -1,9 +1,34 @@
 # Erebus IRC bot - Author: Erebus Team
 # vim: fileencoding=utf-8
+# Configurable sockets module. DO NOT USE without understanding the risks
 # 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.
 
+"""
+To use - add in bot.config something like:
+
+[sockets]
+127.0.0.1:1337 = #example
+
+The left side is the address to listen on and the right side is the channel to send to.
+The exmaple will send incoming lines/packets on localhost, port 1337 to channel #example
+
+The full syntax for the address is:
+[unix:]</path/to/socket>
+[udp|tcp:][<ip>:]<port>
+
+
+Address examples:
+
+Unix domain socket:          /path
+Unix domain socket:          unix:/path
+TCP socket (all interfaces): 1337
+TCP socket (one interface):  127.0.0.1:1337
+UDP socket (all interfaces): udp:1337
+UDP socket (one interface):  udp:127.0.0.1:1337
+"""
+
 # module info
 modinfo = {
        'author': 'Erebus Team',
@@ -22,66 +47,32 @@ def modstart(parent, *args, **kwargs):
 modstop = lib.modstop
 
 # module code
+class BasicServer(lib.Socketlike):
+       def __init__(self, sock, data):
+               super(BasicServer, self).__init__(sock, data)
+               # NB neither directly referencing `channel`, nor trying to pass it through a default-arg-to-a-lambda like the python docs suggest, works here.
+               # Yay python. At least passing it via bind works.
+               self.chan = data
 
-def gotParent(parent):
-       for bindto, channel in parent.cfg.items('sockets'):
-               @lib.bind(bindto, data=channel)
-               class BasicServer(object):
-                       def __init__(self, sock, data):
-                               # NB neither directly referencing `channel`, nor trying to pass it through a default-arg-to-a-lambda like the python docs suggest, works here.
-                               # Yay python. At least passing it via bind works.
-                               self.chan = data
-                               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
+       # default getdata() and send() methods are defined by lib.Socketlike
+       # suitable for line-based protocols like IRC
 
-                       def parse(self, line):
-                               try:
-                                       bot = lib.parent.channel(self.chan).bot
-                               except AttributeError: # <class 'AttributeError'> 'NoneType' object has no attribute 'bot'
-                                       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 parse(self, line):
+               try:
+                       bot = lib.parent.channel(self.chan).bot
+               except AttributeError: # <class 'AttributeError'> 'NoneType' object has no attribute 'bot'
+                       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")
+       # default __str__() and __repr__() methods are defined by lib.Socketlike
 
-                       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())
+def gotParent(parent):
+       for bindto, channel in parent.cfg.items('sockets'):
+               lib.bind(bindto, data=channel)(BasicServer)