aboutsummaryrefslogtreecommitdiff
path: root/http2irc.py
diff options
context:
space:
mode:
authorGravatar JustAnotherArchivist2019-12-22 07:27:27 +0000
committerGravatar JustAnotherArchivist2019-12-22 07:27:27 +0000
commitd00c59e6bc48b66cf0daa077d3ba3ce382e60ce1 (patch)
treea2ee8ec901a5e35aaf6a25ffc2918c04a990393b /http2irc.py
parentAdd support for transformation/translation modules that do arbitrary request ... (diff)
signature
Add a way to pass additional arguments into the module
Diffstat (limited to 'http2irc.py')
-rw-r--r--http2irc.py32
1 files changed, 23 insertions, 9 deletions
diff --git a/http2irc.py b/http2irc.py
index 7a09642..bdeffe4 100644
--- a/http2irc.py
+++ b/http2irc.py
@@ -116,11 +116,16 @@ class Config(dict):
raise InvalidConfig(f'Invalid map key {key!r}')
if not isinstance(map_, collections.abc.Mapping):
raise InvalidConfig(f'Invalid map for {key!r}')
- if any(x not in ('webpath', 'ircchannel', 'auth', 'module') for x in map_):
+ if any(x not in ('webpath', 'ircchannel', 'auth', 'module', 'moduleargs') for x in map_):
raise InvalidConfig(f'Unknown key(s) found in map {key!r}')
#TODO: Check values
if 'module' in map_ and not os.path.isfile(map_['module']):
raise InvalidConfig(f'Module {map_["module"]!r} in map {key!r} is not a file')
+ if 'moduleargs' in map_:
+ if not isinstance(map_['moduleargs'], list):
+ raise InvalidConfig(f'Invalid module args for {key!r}: not an array')
+ if 'module' not in map_:
+ raise InvalidConfig(f'Module args cannot be specified without a module for {key!r}')
# Default values
finalObj = {'logging': {'level': 'INFO', 'format': '{asctime} {levelname} {message}'}, 'irc': {'host': 'irc.hackint.org', 'port': 6697, 'ssl': 'yes', 'nick': 'h2ibot', 'real': 'I am an http2irc bot.', 'certfile': None, 'certkeyfile': None}, 'web': {'host': '127.0.0.1', 'port': 8080}, 'maps': {}}
@@ -135,11 +140,19 @@ class Config(dict):
map_['auth'] = False
if 'module' not in map_:
map_['module'] = None
+ if 'moduleargs' not in map_:
+ map_['moduleargs'] = []
# Load modules
- modulePaths = {map_['module'] for map_ in obj['maps'].values() if 'module' in map_ and map_['module'] is not None}
+ modulePaths = {} # path: str -> (extraargs: int, key: str)
+ for key, map_ in obj['maps'].items():
+ if map_['module'] is not None:
+ if map_['module'] not in modulePaths:
+ modulePaths[map_['module']] = (len(map_['moduleargs']), key)
+ elif modulePaths[map_['module']][0] != len(map_['moduleargs']):
+ raise InvalidConfig(f'Module {map_["module"]!r} process function extra argument inconsistency between {key!r} and {modulePaths[map_["module"]][1]!r}')
modules = {} # path: str -> module: module
- for i, path in enumerate(modulePaths):
+ for i, (path, (extraargs, _)) in enumerate(modulePaths.items()):
try:
# Build a name that is virtually guaranteed to be unique across a process.
# Although importlib does not seem to perform any caching as of CPython 3.8, this is not guaranteed by spec.
@@ -152,8 +165,9 @@ class Config(dict):
raise InvalidConfig(f'Module {path!r} does not have a process function')
if not inspect.iscoroutinefunction(module.process):
raise InvalidConfig(f'Module {path!r} process attribute is not a coroutine function')
- if len(inspect.signature(module.process).parameters) != 1:
- raise InvalidConfig(f'Module {path!r} process function does not take exactly 1 parameter')
+ nargs = len(inspect.signature(module.process).parameters)
+ if nargs != 1 + extraargs:
+ raise InvalidConfig(f'Module {path!r} process function takes {nargs} parameter{"s" if nargs > 1 else ""}, not {1 + extraargs}')
modules[path] = module
# Replace module value in maps
@@ -401,7 +415,7 @@ class WebServer:
self.messageQueue = messageQueue
self.config = config
- self._paths = {} # '/path' => ('#channel', auth, module) where auth is either False (no authentication) or the HTTP header value for basic auth
+ self._paths = {} # '/path' => ('#channel', auth, module, moduleargs) where auth is either False (no authentication) or the HTTP header value for basic auth
self._app = aiohttp.web.Application()
self._app.add_routes([aiohttp.web.post('/{path:.+}', self.post)])
@@ -410,7 +424,7 @@ class WebServer:
self._configChanged = asyncio.Event()
def update_config(self, config):
- self._paths = {map_['webpath']: (map_['ircchannel'], f'Basic {base64.b64encode(map_["auth"].encode("utf-8")).decode("utf-8")}' if map_['auth'] else False, map_['module']) for map_ in config['maps'].values()}
+ self._paths = {map_['webpath']: (map_['ircchannel'], f'Basic {base64.b64encode(map_["auth"].encode("utf-8")).decode("utf-8")}' if map_['auth'] else False, map_['module'], map_['moduleargs']) for map_ in config['maps'].values()}
needRebind = self.config['web'] != config['web']
self.config = config
if needRebind:
@@ -431,7 +445,7 @@ class WebServer:
async def post(self, request):
logging.info(f'Received request for {request.path!r}')
try:
- channel, auth, module = self._paths[request.path]
+ channel, auth, module, moduleargs = self._paths[request.path]
except KeyError:
logging.info(f'Bad request: no path {request.path!r}')
raise aiohttp.web.HTTPNotFound()
@@ -442,7 +456,7 @@ class WebServer:
raise aiohttp.web.HTTPForbidden()
if module is not None:
try:
- message = await module.process(request)
+ message = await module.process(request, *moduleargs)
except aiohttp.web.HTTPException as e:
raise e
except Exception as e: