Skip to content

Commit

Permalink
Merge pull request #1470 from dgw/echo-message
Browse files Browse the repository at this point in the history
irc/bot: implement echo-message
  • Loading branch information
dgw authored Apr 5, 2019
2 parents cf0177e + 443c303 commit 95812b5
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 7 deletions.
3 changes: 3 additions & 0 deletions sopel/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,9 @@ def dispatch(self, pretrigger):
match = True
if not match:
continue
if (trigger.nick.lower() == self.nick.lower() and
not func.echo):
continue
if func.thread:
targs = (func, wrapper, trigger)
t = threading.Thread(target=self.call, args=targs)
Expand Down
18 changes: 12 additions & 6 deletions sopel/coretasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,11 +423,11 @@ def track_quit(bot, trigger):
@sopel.module.thread(False)
@sopel.module.priority('high')
@sopel.module.unblockable
def recieve_cap_list(bot, trigger):
def receive_cap_list(bot, trigger):
cap = trigger.strip('-=~')
# Server is listing capabilites
if trigger.args[1] == 'LS':
recieve_cap_ls_reply(bot, trigger)
receive_cap_ls_reply(bot, trigger)
# Server denied CAP REQ
elif trigger.args[1] == 'NAK':
entry = bot._cap_reqs.get(cap, None)
Expand Down Expand Up @@ -471,10 +471,10 @@ def recieve_cap_list(bot, trigger):
if req.success:
req.success(bot, req.prefix + trigger)
if cap == 'sasl': # TODO why is this not done with bot.cap_req?
recieve_cap_ack_sasl(bot)
receive_cap_ack_sasl(bot)


def recieve_cap_ls_reply(bot, trigger):
def receive_cap_ls_reply(bot, trigger):
if bot.server_capabilities:
# We've already seen the results, so someone sent CAP LS from a module.
# We're too late to do SASL, and we don't want to send CAP END before
Expand All @@ -496,7 +496,13 @@ def recieve_cap_ls_reply(bot, trigger):

# If some other module requests it, we don't need to add another request.
# If some other module prohibits it, we shouldn't request it.
core_caps = ['multi-prefix', 'away-notify', 'cap-notify', 'server-time']
core_caps = [
'echo-message',
'multi-prefix',
'away-notify',
'cap-notify',
'server-time',
]
for cap in core_caps:
if cap not in bot._cap_reqs:
bot._cap_reqs[cap] = [_CapReq('', 'coretasks')]
Expand Down Expand Up @@ -545,7 +551,7 @@ def acct_warn(bot, cap):
bot.write(('CAP', 'END'))


def recieve_cap_ack_sasl(bot):
def receive_cap_ack_sasl(bot):
# Presumably we're only here if we said we actually *want* sasl, but still
# check anyway.
password = bot.config.core.auth_password
Expand Down
18 changes: 18 additions & 0 deletions sopel/irc.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,24 @@ def write(self, args, text=None):
finally:
self.writing_lock.release()

# Simulate echo-message
if ('echo-message' not in self.enabled_capabilities and
args[0].upper() in ['PRIVMSG', 'NOTICE']):
# Use the hostmask we think the IRC server is using for us,
# or something reasonable if that's not available
host = 'localhost'
if self.config.core.bind_host:
host = self.config.core.bind_host
else:
try:
host = self.hostmask
except KeyError:
pass # we tried, and that's good enough

pretrigger = PreTrigger(self.nick, ':{0}!{1}@{2} {3}'
.format(self.nick, self.user, host, temp))
self.dispatch(pretrigger)

def run(self, host, port=6667):
try:
self.initiate_connect(host, port)
Expand Down
1 change: 1 addition & 0 deletions sopel/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ def clean_callable(func, config):
example = None

func.unblockable = getattr(func, 'unblockable', False)
func.echo = getattr(func, 'echo', False)
func.priority = getattr(func, 'priority', 'medium')
func.thread = getattr(func, 'thread', True)
func.rate = getattr(func, 'rate', 0)
Expand Down
16 changes: 16 additions & 0 deletions sopel/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,22 @@ def add_attribute(function):
return add_attribute


def echo(function=None):
"""Decorate a function to specify if it should receive echo messages.
This decorator can be used to listen in on the messages that Sopel is
sending and react accordingly.
"""
def add_attribute(function):
function.echo = True
return function

# hack to allow both @echo and @echo() to work
if callable(function):
return add_attribute(function)
return add_attribute


def commands(*command_list):
"""Decorate a function to set one or more commands to trigger it.
Expand Down
3 changes: 2 additions & 1 deletion sopel/modules/find.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@

import re
from sopel.tools import Identifier, SopelMemory
from sopel.module import rule, priority
from sopel.module import rule, priority, echo
from sopel.formatting import bold


def setup(bot):
bot.memory['find_lines'] = SopelMemory()


@echo
@rule('.*')
@priority('low')
def collectlines(bot, trigger):
Expand Down
21 changes: 21 additions & 0 deletions test/test_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,27 @@ def mock(bot, trigger, match):
assert mock.thread is True


def test_echo():
# test decorator with parentheses
@module.echo()
def mock(bot, trigger, match):
return True
assert mock.echo is True

# test decorator without parentheses
@module.echo
def mock(bot, trigger, match):
return True
assert mock.echo is True

# test without decorator
def mock(bot, trigger, match):
return True
# on undecorated callables, the attr only exists after the loader loads them
# so this cannot `assert mock.echo is False` here
assert not hasattr(mock, 'echo')


def test_commands():
@module.commands('sopel')
def mock(bot, trigger, match):
Expand Down

0 comments on commit 95812b5

Please sign in to comment.