]> jfr.im git - erebus.git/commitdiff
start work on sockets module, not ready to use yet
authorJohn Runyon <redacted>
Wed, 20 Dec 2023 03:44:57 +0000 (20:44 -0700)
committerJohn Runyon <redacted>
Wed, 20 Dec 2023 03:44:57 +0000 (20:44 -0700)
config.py
modlib.py
modules/example_socket.py [moved from modules/basic_socket.py with 100% similarity]
modules/sockets.py [new file with mode: 0644]

index 5538dc78c9f68db4b27d6d9c2c1995fa2bb26e4c..9755f84644d25ac076c05d521973bf04f4c79328 100644 (file)
--- a/config.py
+++ b/config.py
@@ -12,7 +12,7 @@ else:
 
 class Config(object):
        def __init__(self, filename, writeout=True):
-               self.__dict__['config'] = ConfigParser.RawConfigParser()
+               self.__dict__['config'] = ConfigParser.RawConfigParser(delimiters=('=',))
                self.__dict__['filename'] = filename
                self.__dict__['writeout'] = writeout
                self.config.read(filename)
index 91e05843dd306a49adc15596806d6d2ed3aa5d51..06276184ef27876a3c338adb94a0d3e2f473db8f 100644 (file)
--- a/modlib.py
+++ b/modlib.py
@@ -170,6 +170,37 @@ class modlib(object):
                        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):
@@ -196,7 +227,7 @@ class modlib(object):
                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()
 
diff --git a/modules/sockets.py b/modules/sockets.py
new file mode 100644 (file)
index 0000000..0e96e04
--- /dev/null
@@ -0,0 +1,84 @@
+# 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())