diff --git a/config.example.toml b/config.example.toml index d97ea97..ace7e68 100644 --- a/config.example.toml +++ b/config.example.toml @@ -28,6 +28,8 @@ #[channels.spam] # If ircchannel isn't specified, it corresponds to '#' followed by the map key. #ircchannel = '#spam' + # If path isn't specified, it corresponds to the map key. + #path = 'spam' # auth can be either 'user:pass' for basic authentication or false to disable auth #auth = false # Whether this channel should still be actively logged. Set this to false to stop logging the channel but keep serving the previous logs. diff --git a/irclog.py b/irclog.py index 954d969..702a05b 100644 --- a/irclog.py +++ b/irclog.py @@ -140,12 +140,13 @@ class Config(dict): raise InvalidConfig('Invalid web port') if 'channels' in obj: seenChannels = {} + seenPaths = {} for key, channel in obj['channels'].items(): if not isinstance(key, str) or not key: raise InvalidConfig(f'Invalid channel key {key!r}') if not isinstance(channel, collections.abc.Mapping): raise InvalidConfig(f'Invalid channel for {key!r}') - if any(x not in ('ircchannel', 'auth', 'active') for x in channel): + if any(x not in ('ircchannel', 'path', 'auth', 'active') for x in channel): raise InvalidConfig(f'Unknown key(s) found in channel {key!r}') if 'ircchannel' not in channel: @@ -162,6 +163,18 @@ class Config(dict): raise InvalidConfig(f'Invalid channel {key!r} IRC channel: collides with channel {seenWebPaths[channel["ircchannel"]]!r}') seenChannels[channel['ircchannel']] = key + if 'path' not in channel: + channel['path'] = key + if not isinstance(channel['path'], str): + raise InvalidConfig(f'Invalid channel {key!r} path: not a string') + if '/' in channel['path'] or '\\' in channel['path']: #TODO Anything else? + raise InvalidConfig(f'Invalid channel {key!r} path: contains forward or backward slashes') + if channel['path'] == 'general': + raise InvalidConfig(f'Invalid channel {key!r} path: cannot be "general"') + if channel['path'] in seenPaths: + raise InvalidConfig(f'Invalid channel {key!r} path: collides with channel {seenPaths[channel["path"]]!r}') + seenPaths[channel['path']] = key + if 'auth' in channel: if channel['auth'] is not False and not isinstance(channel['auth'], str): raise InvalidConfig(f'Invalid channel {key!r} auth: must be false or a string') @@ -582,6 +595,7 @@ class Storage: def __init__(self, messageQueue, config): self.messageQueue = messageQueue self.config = config + self.paths = {} # channel -> path from channels config self.files = {} # channel|None -> (filename, fileobj); None = general raw log def update_config(self, config): @@ -589,6 +603,7 @@ class Storage: channelsNew = {channel['ircchannel'] for channel in config['channels'].values()} channelsRemoved = channelsOld - channelsNew self.config = config + self.paths = {channel['ircchannel']: channel['path'] for channel in self.config['channels'].values()} for channel in channelsRemoved: if channel in self.files: @@ -601,7 +616,7 @@ class Storage: return if channel in self.files: self.files[channel][1].close() - dn = channel if channel is not None else 'general' + dn = self.paths[channel] if channel is not None else 'general' mode = 'a' if channel is not None else 'ab' os.makedirs(os.path.join(self.config['storage']['path'], dn), exist_ok = True) self.files[channel] = (fn, open(os.path.join(self.config['storage']['path'], dn, fn), mode))