From 080e50a539dd83d5121ec607bb41ed09e6b02cde Mon Sep 17 00:00:00 2001 From: deathbybandaid Date: Thu, 28 Mar 2019 12:29:50 -0400 Subject: [PATCH] Allow bot to utilize multiple destinations at one time Implement maxtargets = 4 --- sopel/bot.py | 128 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 76 insertions(+), 52 deletions(-) diff --git a/sopel/bot.py b/sopel/bot.py index 7e0fefc866..2799b44584 100644 --- a/sopel/bot.py +++ b/sopel/bot.py @@ -267,14 +267,14 @@ def join(self, channel, password=None): else: self.write(['JOIN', channel, password]) - def msg(self, recipient, text, max_messages=1): + def msg(self, recipients, text, max_messages=1): # Deprecated, but way too much of a pain to remove. - self.say(text, recipient, max_messages) + self.say(text, recipients, max_messages) - def say(self, text, recipient, max_messages=1): - """Send ``text`` as a PRIVMSG to ``recipient``. + def say(self, text, recipients, max_messages=1): + """Send ``text`` as a PRIVMSG to ``recipients``. - In the context of a triggered callable, the ``recipient`` defaults to + In the context of a triggered callable, the ``recipients`` defaults to the channel (or nickname, if a private message) from which the message was received. @@ -297,55 +297,80 @@ def say(self, text, recipient, max_messages=1): # Manage multi-line only when needed text, excess = tools.get_sendable_message(text) - try: - self.sending.acquire() - - # No messages within the last 3 seconds? Go ahead! - # Otherwise, wait so it's been at least 0.8 seconds + penalty - - recipient_id = Identifier(recipient) - - if recipient_id not in self.stack: - self.stack[recipient_id] = [] - elif self.stack[recipient_id]: - elapsed = time.time() - self.stack[recipient_id][-1][0] - if elapsed < 3: - penalty = float(max(0, len(text) - 40)) / 70 - wait = 0.8 + penalty - if elapsed < wait: - time.sleep(wait - elapsed) - - # Loop detection - messages = [m[1] for m in self.stack[recipient_id][-8:]] - - # If what we about to send repeated at least 5 times in the - # last 2 minutes, replace with '...' - if messages.count(text) >= 5 and elapsed < 120: - text = '...' - if messages.count('...') >= 3: - # If we said '...' 3 times, discard message - return - - self.write(('PRIVMSG', recipient), text) - self.stack[recipient_id].append((time.time(), self.safe(text))) - self.stack[recipient_id] = self.stack[recipient_id][-10:] - finally: - self.sending.release() - # Now that we've sent the first part, we need to send the rest. Doing - # this recursively seems easier to me than iteratively - if excess: - self.msg(recipient, excess, max_messages - 1) - - def notice(self, text, dest): + if not isinstance(recipients, list): + recipients = recipients.split(",") + + maxtargets = 4 + # if server.capabilities.maxtargets # TODO + recipientgroups = [] + while len(recipients): + dests_part = ','.join(str(x) for x in recipients[-maxtargets:]) + recipientgroups.append(dests_part) + del recipients[-maxtargets:] + + for recipientgroup in recipientgroups: + + try: + self.sending.acquire() + + # No messages within the last 3 seconds? Go ahead! + # Otherwise, wait so it's been at least 0.8 seconds + penalty + + recipient_id = Identifier(recipientgroup) + + if recipient_id not in self.stack: + self.stack[recipient_id] = [] + elif self.stack[recipient_id]: + elapsed = time.time() - self.stack[recipient_id][-1][0] + if elapsed < 3: + penalty = float(max(0, len(text) - 40)) / 70 + wait = 0.8 + penalty + if elapsed < wait: + time.sleep(wait - elapsed) + + # Loop detection + messages = [m[1] for m in self.stack[recipient_id][-8:]] + + # If what we about to send repeated at least 5 times in the + # last 2 minutes, replace with '...' + if messages.count(text) >= 5 and elapsed < 120: + text = '...' + if messages.count('...') >= 3: + # If we said '...' 3 times, discard message + return + + self.write(('PRIVMSG', recipientgroup), text) + self.stack[recipient_id].append((time.time(), self.safe(text))) + self.stack[recipient_id] = self.stack[recipient_id][-10:] + finally: + self.sending.release() + # Now that we've sent the first part, we need to send the rest. Doing + # this recursively seems easier to me than iteratively + if excess: + self.msg(recipientgroup, excess, max_messages - 1) + + def notice(self, text, dests): """Send an IRC NOTICE to a user or a channel. Within the context of a triggered callable, ``dest`` will default to the channel (or nickname, if a private message), in which the trigger happened. """ - self.write(('NOTICE', dest), text) + if not isinstance(dests, list): + dests = dests.split(",") + + maxtargets = 4 + # if server.capabilities.maxtargets # TODO + destgroups = [] + while len(dests): + dests_part = ','.join(str(x) for x in dests[-maxtargets:]) + destgroups.append(dests_part) + del dests[-maxtargets:] + + for destgroup in destgroups: + self.write(('NOTICE', destgroup), text) - def action(self, text, dest): + def action(self, text, dests): """Send ``text`` as a CTCP ACTION PRIVMSG to ``dest``. The same loop detection and length restrictions apply as with @@ -355,9 +380,9 @@ def action(self, text, dest): the channel (or nickname, if a private message), in which the trigger happened. """ - self.say('\001ACTION {}\001'.format(text), dest) + self.say('\001ACTION {}\001'.format(text), dests) - def reply(self, text, dest, reply_to, notice=False): + def reply(self, text, dests, reply_to, notice=False): """Prepend ``reply_to`` to ``text``, and send as a PRIVMSG to ``dest``. If ``notice`` is ``True``, send a NOTICE rather than a PRIVMSG. @@ -370,11 +395,10 @@ def reply(self, text, dest, reply_to, notice=False): channel (or nickname, if a private message), in which the trigger happened. """ - text = '%s: %s' % (reply_to, text) if notice: - self.notice(text, dest) + self.notice(text, dests) else: - self.say(text, dest) + self.say(text, dests) class SopelWrapper(object): def __init__(self, sopel, trigger):