From: John Runyon Date: Wed, 20 Dec 2023 03:52:44 +0000 (-0700) Subject: sockets - now working X-Git-Url: https://jfr.im/git/erebus.git/commitdiff_plain/b5e5c4470d993b41113ae43b7f5030d412585430 sockets - now working --- diff --git a/modlib.py b/modlib.py index 0627618..cc1c47c 100644 --- a/modlib.py +++ b/modlib.py @@ -170,7 +170,7 @@ class modlib(object): return func return realhook - def bind(self, bindto): + def bind(self, bindto, data=None): """Used as a decorator on a class which implements getdata and parse methods. See modules/sockets.py for an example. Takes an arg like: @@ -191,39 +191,36 @@ class modlib(object): 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)) + return self._hooksocket(af, ty, (host, port), data) - def bind_tcp(self, host, port): - return self._hooksocket(socket.AF_INET, socket.SOCK_STREAM, (host, port)) - def bind_udp(self, host, port): - return self._hooksocket(socket.AF_INET, socket.SOCK_DGRAM, (host, port)) - def bind_unix(self, path): - return self._hooksocket(socket.AF_UNIX, socket.SOCK_STREAM, path) - def _hooksocket(self, af, ty, address): + def bind_tcp(self, host, port, data=None): + return self._hooksocket(socket.AF_INET, socket.SOCK_STREAM, (host, port), data) + def bind_udp(self, host, port, data=None): + return self._hooksocket(socket.AF_INET, socket.SOCK_DGRAM, (host, port), data) + def bind_unix(self, path, data=None): + return self._hooksocket(socket.AF_UNIX, socket.SOCK_STREAM, path, data) + def _hooksocket(self, af, ty, address, data): def realhook(cls): if not (hasattr(cls, 'getdata') and callable(cls.getdata)): # Check early that the object implements getdata. # If getdata ever returns a non-empty list, then a parse method must also exist, but we don't check that. raise Exception('Attempted to hook a socket without a class to process data') - self.sockhooks.append((af, ty, address, cls)) + self.sockhooks.append((af, ty, address, cls, data)) if self.parent is not None: - self._create_socket(af, ty, address, cls) + self._create_socket(af, ty, address, cls, data) return cls return realhook - def _create_socket(self, af, ty, address, cls): + def _create_socket(self, af, ty, address, cls, data): ty = ty | socket.SOCK_NONBLOCK sock = socket.socket(af, ty) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(address) - obj = _ListenSocket(self, sock, cls) + obj = _ListenSocket(self, sock, cls, data) self.sockets.append((sock,obj)) sock.listen(5) self.parent.newfd(obj, sock.fileno()) @@ -281,11 +278,12 @@ class modlib(object): return realhook class _ListenSocket(object): - def __init__(self, lib, sock, cls): + def __init__(self, lib, sock, cls, data): self.clients = [] self.lib = lib self.sock = sock self.cls = cls + self.data = data def _make_closer(self, obj, client): def close(): @@ -302,7 +300,7 @@ class _ListenSocket(object): def getdata(self): client, addr = self.sock.accept() - obj = self.cls(client) + obj = self.cls(client, self.data) obj.close = self._make_closer(obj, client) self.lib.parent.log(repr(self), '?', 'New connection #%d from %s' % (client.fileno(), addr)) self.clients.append((client,obj)) diff --git a/modules/example_socket.py b/modules/example_socket.py index 1f6563d..dc90767 100644 --- a/modules/example_socket.py +++ b/modules/example_socket.py @@ -25,7 +25,7 @@ modstop = lib.modstop # - listen on the socket `sock.listen()` # - accept `sock.accept()` # -# Once a connection is accepted, your class is instantiated with the client socket. +# Once a connection is accepted, your class is instantiated with the client socket. (And the optional third argument you passed to bind_*, or None) # - When data comes in on the client socket, your `getdata` method will be called. It should return a list of strings. # - For each element in the list returned by `getdata`, `parse` will be called. # - When the socket is being closed by the bot (f.e. your module is unloaded), the optional method `closing` will be called. @@ -36,7 +36,7 @@ modstop = lib.modstop @lib.bind_tcp('0.0.0.0', 12543) class BasicServer(object): - def __init__(self, sock): + def __init__(self, sock, data): self.chan = lib.parent.cfg.get('basic_socket', 'channel', '#') self.buffer = b'' self.sock = sock diff --git a/modules/sockets.py b/modules/sockets.py index 0e96e04..060057b 100644 --- a/modules/sockets.py +++ b/modules/sockets.py @@ -28,9 +28,9 @@ def gotParent(parent): @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 + # 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 - print(repr(self.chan)) self.buffer = b'' self.sock = sock