diff options
| author | 2020-05-13 15:00:54 +0000 | |
|---|---|---|
| committer | 2020-05-13 15:00:54 +0000 | |
| commit | 98f8821fda0488685d6e36d1b70510444487abec (patch) | |
| tree | 4fe67a2fc16d692c80055db76658f7b178a2b158 /http2irc.py | |
| parent | Add Grafana module (diff) | |
Add option to truncate overlong messages instead of splitting them
Diffstat (limited to 'http2irc.py')
| -rw-r--r-- | http2irc.py | 32 |
1 files changed, 23 insertions, 9 deletions
diff --git a/http2irc.py b/http2irc.py index 60c0a5c..16e02e3 100644 --- a/http2irc.py +++ b/http2irc.py @@ -121,7 +121,7 @@ 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', 'moduleargs') for x in map_): + if any(x not in ('webpath', 'ircchannel', 'auth', 'module', 'moduleargs', 'overlongmode') for x in map_): raise InvalidConfig(f'Unknown key(s) found in map {key!r}') if 'webpath' not in map_: @@ -158,6 +158,11 @@ class Config(dict): 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}') + if 'overlongmode' in map_: + if not isinstance(map_['overlongmode'], str): + raise InvalidConfig(f'Invalid map {key!r} overlongmode: not a string') + if map_['overlongmode'] not in ('split', 'truncate'): + raise InvalidConfig(f'Invalid map {key!r} overlongmode: unsupported value') # Default values finalObj = {'logging': {'level': 'INFO', 'format': '{asctime} {levelname} {name} {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': {}} @@ -172,6 +177,8 @@ class Config(dict): map_['module'] = None if 'moduleargs' not in map_: map_['moduleargs'] = [] + if 'overlongmode' not in map_: + map_['overlongmode'] = 'split' # Load modules modulePaths = {} # path: str -> (extraargs: int, key: str) @@ -386,7 +393,7 @@ class IRCClientProtocol(asyncio.Protocol): async def send_messages(self): while self.connected: self.logger.debug(f'Trying to get a message') - channel, message = await self._get_message() + channel, message, overlongmode = await self._get_message() self.logger.debug(f'Got message: {message!r}') if message is None: break @@ -394,13 +401,17 @@ class IRCClientProtocol(asyncio.Protocol): messageB = message.encode('utf-8') usermaskPrefixLength = 1 + (len(self.usermask) if self.usermask else 100) + 1 if usermaskPrefixLength + len(b'PRIVMSG ' + channelB + b' :' + messageB) > 510: - self.logger.debug(f'Splitting up into smaller messages') - # Message too long, need to split. First try to split on spaces, then on codepoints. Ideally, would use graphemes between, but that's too complicated. + # Message too long, need to split or truncate. First try to split on spaces, then on codepoints. Ideally, would use graphemes between, but that's too complicated. + self.logger.debug(f'Message too long, overlongmode = {overlongmode}') prefix = b'PRIVMSG ' + channelB + b' :' prefixLength = usermaskPrefixLength + len(prefix) # Need to account for the origin prefix included by the ircd when sending to others maxMessageLength = 510 - prefixLength # maximum length of the message part within each line + if overlongmode == 'truncate': + maxMessageLength -= 3 # Make room for an ellipsis at the end messages = [] while message: + if overlongmode == 'truncate' and messages: + break # Only need the first message on truncation if len(messageB) <= maxMessageLength: messages.append(message) break @@ -425,8 +436,11 @@ class IRCClientProtocol(asyncio.Protocol): messages.append(message[:cutoffIndex]) message = message[cutoffIndex:] messageB = message.encode('utf-8') - for msg in reversed(messages): - self.messageQueue.putleft_nowait((channel, msg)) + if overlongmode == 'split': + for msg in reversed(messages): + self.messageQueue.putleft_nowait((channel, msg, overlongmode)) + elif overlongmode == 'truncate': + self.messageQueue.putleft_nowait((channel, messages[0] + '…', overlongmode)) else: self.logger.info(f'Sending {message!r} to {channel!r}') self.unconfirmedMessages.append((channel, message)) @@ -633,7 +647,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'], map_['moduleargs']) 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'], map_['overlongmode']) for map_ in config['maps'].values()} needRebind = self.config['web'] != config['web'] self.config = config if needRebind: @@ -654,7 +668,7 @@ class WebServer: async def post(self, request): self.logger.info(f'Received request {id(request)} from {request.remote!r} for {request.path!r} with body {(await request.read())!r}') try: - channel, auth, module, moduleargs = self._paths[request.path] + channel, auth, module, moduleargs, overlongmode = self._paths[request.path] except KeyError: self.logger.info(f'Bad request {id(request)}: no path {request.path!r}') raise aiohttp.web.HTTPNotFound() @@ -679,7 +693,7 @@ class WebServer: self.logger.debug(f'Processing request {id(request)} using default processor') message = await self._default_process(request) self.logger.info(f'Accepted request {id(request)}, putting message {message!r} for {channel} into message queue') - self.messageQueue.put_nowait((channel, message)) + self.messageQueue.put_nowait((channel, message, overlongmode)) raise aiohttp.web.HTTPOk() async def _default_process(self, request): |
