Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow bot to utilize multiple destinations at one time #1524

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 67 additions & 51 deletions sopel/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,14 +267,18 @@ 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)

def say(self, text, recipient, max_messages=1):
"""Send ``text`` as a PRIVMSG to ``recipient``.
if not isinstance(recipients, list):
recipients = [recipients]
for recipient in recipients:
self.say(text, recipient, max_messages)

In the context of a triggered callable, the ``recipient`` defaults to
def say(self, text, recipients, max_messages=1):
"""Send ``text`` as a PRIVMSG to ``recipients``.

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 +301,62 @@ 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]
for recipient in recipients:

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, 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]
for dest in dests:
self.write(('NOTICE', dest), 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 +366,12 @@ 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)
if not isinstance(dests, list):
dests = [dests]
for dest in dests:
self.say('\001ACTION {}\001'.format(text), dest)

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 @@ -371,10 +385,12 @@ def reply(self, text, dest, reply_to, notice=False):
happened.
"""
text = '%s: %s' % (reply_to, text)
if not isinstance(dests, list):
dests = [dests]
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