Skip to content

Commit

Permalink
Allow bot to utilize multiple destinations at one time
Browse files Browse the repository at this point in the history
Implement maxtargets = 4
  • Loading branch information
deathbybandaid committed Apr 2, 2019
1 parent 5197450 commit 080e50a
Showing 1 changed file with 76 additions and 52 deletions.
128 changes: 76 additions & 52 deletions sopel/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -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.
Expand All @@ -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):
Expand Down

0 comments on commit 080e50a

Please sign in to comment.