diff options
| author | 2026-05-01 14:56:53 +0000 | |
|---|---|---|
| committer | 2026-05-01 14:56:53 +0000 | |
| commit | a0ce214430138ddd93dc36c56e4bec533c92d195 (patch) | |
| tree | 11dd3ee1bcb724f7f473bab61f334d2ba2ab9963 | |
| parent | Allow having certain irc config changes without reconnect (diff) | |
| signature | ||
Allow setting a global message ratelimit
This allows configuring a http2irc instance to have a higher global
message sending rate limit different from the default hard-coded rate of
1 message / second, whilst keeping backwards compatibility by keeping
default value being the hard-coded one.
In addition if the rate limit is set to 0, which can be done live, the
bot will stop sending queued messages whilst keeping the IRC connection
alive.
| -rw-r--r-- | config.example.toml | 2 | ||||
| -rw-r--r-- | http2irc.py | 18 |
2 files changed, 16 insertions, 4 deletions
diff --git a/config.example.toml b/config.example.toml index 59a9f09..3c0c6a0 100644 --- a/config.example.toml +++ b/config.example.toml @@ -11,6 +11,8 @@ #family = #nick = 'h2ibot' #real = 'I am an http2irc bot.' + #sendratelimit = 1 + # Set to the number of lines per second you want the bot to be able to send globally. # Certificate and key for SASL EXTERNAL authentication with NickServ; certfile is a string containing the path to a .pem file which has the certificate and the key, certkeyfile similarly for one containing only the key; default values are empty (None in Python) to disable authentication; the connection is terminated if authentication fails #certfile = #certkeyfile = diff --git a/http2irc.py b/http2irc.py index a73f23d..57c66ac 100644 --- a/http2irc.py +++ b/http2irc.py @@ -104,7 +104,7 @@ class Config(dict): except (ValueError, AssertionError) as e: raise InvalidConfig('Invalid log format: parsing failed') from e if 'irc' in obj: - if any(x not in ('host', 'port', 'ssl', 'family', 'nick', 'real', 'certfile', 'certkeyfile') for x in obj['irc']): + if any(x not in ('host', 'port', 'ssl', 'family', 'nick', 'real', 'certfile', 'certkeyfile', 'sendratelimit') for x in obj['irc']): raise InvalidConfig('Unknown key found in irc section') if 'host' in obj['irc'] and not isinstance(obj['irc']['host'], str): #TODO: Check whether it's a valid hostname raise InvalidConfig('Invalid IRC host') @@ -124,6 +124,11 @@ class Config(dict): raise InvalidConfig('Invalid IRC nick: contains illegal characters') if len(IRCClientProtocol.nick_command(obj['irc']['nick'])) > 510: raise InvalidConfig('Invalid IRC nick: NICK command too long') + if 'sendratelimit' in obj['irc']: + if not type(obj['irc']['sendratelimit']) in [float, int]: + raise InvalidConfig('Invalid ratelimit, must be a float or a integer') + if obj['irc']['sendratelimit'] < 0: + raise InvalidConfig('Ratelimit must be positive') if 'real' in obj['irc'] and not isinstance(obj['irc']['real'], str): raise InvalidConfig('Invalid IRC realname') if len(IRCClientProtocol.user_command(obj['irc']['nick'], obj['irc']['real'])) > 510: @@ -225,7 +230,7 @@ class Config(dict): # Default values finalObj = { 'logging': {'level': 'INFO', 'format': '{asctime} {levelname} {name} {message}'}, - 'irc': {'host': 'irc.hackint.org', 'port': 6697, 'ssl': 'yes', 'family': 0, 'nick': 'h2ibot', 'real': 'I am an http2irc bot.', 'certfile': None, 'certkeyfile': None}, + 'irc': {'host': 'irc.hackint.org', 'port': 6697, 'ssl': 'yes', 'family': 0, 'nick': 'h2ibot', 'real': 'I am an http2irc bot.', 'certfile': None, 'certkeyfile': None, 'sendratelimit': 1 }, 'web': {'host': '127.0.0.1', 'port': 8080, 'maxrequestsize': 1048576}, 'maps': {} } @@ -484,11 +489,16 @@ class IRCClientProtocol(asyncio.Protocol): data = t.result() self.logger.debug(f'Got {data!r} from send queue') now = time.time() - if self.lastSentTime is not None and now - self.lastSentTime < 1: - self.logger.debug(f'Rate limited') + if self.config['irc']['sendratelimit'] == 0: + self.logger.debug(f'Rate limited, not sending messages for now.') await wait_cancel_pending({asyncio.create_task(self.connectionClosedEvent.wait())}, timeout = self.lastSentTime + 1 - now) if self.connectionClosedEvent.is_set(): break + if (self.lastSentTime is not None and now - self.lastSentTime < 1/self.config['irc']['sendratelimit']) or self.config['irc']['sendratelimit'] == 0: + self.logger.debug(f'Rate limited') + await wait_cancel_pending({asyncio.create_task(self.connectionClosedEvent.wait())}, timeout = self.lastSentTime + 1/self.config['irc']['sendratelimit'] - now) + if self.connectionClosedEvent.is_set(): + break time_ = self._direct_send(data) if self.lastSentTime is not None: self.lastSentTime = time_ |
