aboutsummaryrefslogtreecommitdiff
path: root/http2irc.py
diff options
context:
space:
mode:
authorGravatar JustAnotherArchivist2020-05-13 15:00:54 +0000
committerGravatar JustAnotherArchivist2020-05-13 15:00:54 +0000
commit98f8821fda0488685d6e36d1b70510444487abec (patch)
tree4fe67a2fc16d692c80055db76658f7b178a2b158 /http2irc.py
parentAdd Grafana module (diff)
Add option to truncate overlong messages instead of splitting them
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 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):