aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar klea2026-06-27 03:40:13 +0000
committerGravatar klea2026-06-27 03:40:13 +0000
commitdd466410791a947e2acfb2858b4e7e45f63c1580 (patch)
treec291b1d7501567cf4093aad2c6379f1b9f3a3200
parentTrim spaces from CAP responses (diff)
Allow setting global message sending rate limit settable-global-ratelimit
-rw-r--r--config.example.toml2
-rw-r--r--http2irc.py19
2 files changed, 16 insertions, 5 deletions
diff --git a/config.example.toml b/config.example.toml
index 59a9f09..219c1bd 100644
--- a/config.example.toml
+++ b/config.example.toml
@@ -11,6 +11,8 @@
#family =
#nick = 'h2ibot'
#real = 'I am an http2irc bot.'
+ #sendratelimit = 10
+ # 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 da47dc2..5294923 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')
@@ -126,6 +126,9 @@ class Config(dict):
raise InvalidConfig('Invalid IRC nick: NICK command too long')
if 'real' in obj['irc'] and not isinstance(obj['irc']['real'], str):
raise InvalidConfig('Invalid IRC realname')
+ if 'sendratelimit' in obj['irc']:
+ if not isinstance(obj['irc']['sendratelimit'], (int, float)):
+ raise InvalidConfig('Invalid ratelimit: must be numeric')
if len(IRCClientProtocol.user_command(obj['irc']['nick'], obj['irc']['real'])) > 510:
raise InvalidConfig('Invalid IRC nick/realname combination: USER command too long')
if ('certfile' in obj['irc']) != ('certkeyfile' in obj['irc']):
@@ -225,7 +228,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,9 +487,10 @@ 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:
+ sendTimeLimit = 1 / self.config['irc']['sendratelimit']
+ if self.lastSentTime is not None and now - self.lastSentTime < sendTimeLimit:
self.logger.debug(f'Rate limited')
- await wait_cancel_pending({asyncio.create_task(self.connectionClosedEvent.wait())}, timeout = self.lastSentTime + 1 - now)
+ await wait_cancel_pending({asyncio.create_task(self.connectionClosedEvent.wait())}, timeout = self.lastSentTime + sendTimeLimit - now)
if self.connectionClosedEvent.is_set():
break
time_ = self._direct_send(data)
@@ -871,7 +875,11 @@ class IRCClient:
self._protocol = None
def update_config(self, config):
- needReconnect = self.config['irc'] != config['irc']
+ ircReconnectNeededKeys = [
+ 'host', 'port', 'ssl', 'family',
+ 'nick', 'real', 'certfile', 'certkeyfile',
+ ]
+ needReconnect = True in [self.config['irc'][key] != config['irc'][key] for key in ircReconnectNeededKeys]
self.config = config
if self._transport: # if currently connected:
if needReconnect:
@@ -879,6 +887,7 @@ class IRCClient:
else:
self.channels = {map_['ircchannel'] for map_ in config['maps'].values()}
self._protocol.update_channels(self.channels)
+ self._protocol.config['irc'] = config['irc']
def _get_ssl_context(self):
ctx = SSL_CONTEXTS[self.config['irc']['ssl']]