Skip to content

Commit

Permalink
Merge pull request #2441 from Exirel/sopel-wrapper-statusmsg-reply
Browse files Browse the repository at this point in the history
bot, trigger: use status prefix in SopelWrapper
  • Loading branch information
dgw authored Apr 20, 2023
2 parents be7092c + 59b5bb6 commit e5bcd1f
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 5 deletions.
52 changes: 47 additions & 5 deletions sopel/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -1256,6 +1256,35 @@ def __getattr__(self, attr):
def __setattr__(self, attr, value):
return setattr(self._bot, attr, value)

@property
def default_destination(self) -> Optional[str]:
"""Default say/reply destination for the associated Trigger.
:return: the channel (with status prefix) or nick to send messages to
This property returns the :class:`str` version of the destination that
will be used by default by these methods:
* :meth:`say`
* :meth:`reply`
* :meth:`action`
* :meth:`notice`
For a channel, it also ensures that the status-specific prefix is added
to the result, so the bot replies with the same status.
"""
if not self._trigger.sender:
return None

# ensure str and not Identifier
destination = str(self._trigger.sender)

# prepend status prefix if it exists
if self._trigger.status_prefix:
destination = self._trigger.status_prefix + destination

return destination

def say(self, message, destination=None, max_messages=1, truncation='', trailing=''):
"""Override ``Sopel.say`` to use trigger source by default.
Expand All @@ -1280,8 +1309,15 @@ def say(self, message, destination=None, max_messages=1, truncation='', trailing
"""
if destination is None:
destination = self._trigger.sender
self._bot.say(self._out_pfx + message, destination, max_messages, truncation, trailing)
destination = self.default_destination

self._bot.say(
self._out_pfx + message,
destination,
max_messages,
truncation,
trailing,
)

def action(self, message, destination=None):
"""Override ``Sopel.action`` to use trigger source by default.
Expand All @@ -1298,7 +1334,8 @@ def action(self, message, destination=None):
:meth:`sopel.bot.Sopel.action`
"""
if destination is None:
destination = self._trigger.sender
destination = self.default_destination

self._bot.action(message, destination)

def notice(self, message, destination=None):
Expand All @@ -1316,7 +1353,8 @@ def notice(self, message, destination=None):
:meth:`sopel.bot.Sopel.notice`
"""
if destination is None:
destination = self._trigger.sender
destination = self.default_destination

self._bot.notice(self._out_pfx + message, destination)

def reply(self, message, destination=None, reply_to=None, notice=False):
Expand All @@ -1339,9 +1377,11 @@ def reply(self, message, destination=None, reply_to=None, notice=False):
:meth:`sopel.bot.Sopel.reply`
"""
if destination is None:
destination = self._trigger.sender
destination = self.default_destination

if reply_to is None:
reply_to = self._trigger.nick

self._bot.reply(message, destination, reply_to, notice)

def kick(self, nick, channel=None, message=None):
Expand All @@ -1364,6 +1404,8 @@ def kick(self, nick, channel=None, message=None):
raise RuntimeError('Error: KICK requires a channel.')
else:
channel = self._trigger.sender

if nick is None:
raise RuntimeError('Error: KICK requires a nick.')

self._bot.kick(nick, channel, message)
13 changes: 13 additions & 0 deletions sopel/trigger.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,18 @@ class Trigger(str):
else:
# message sent from a channel
.. important::
If the message was sent to a `specific status prefix`__, the ``sender``
does not include the status prefix. Be sure to use the
:attr:`status_prefix` when replying.
Note that the ``bot`` argument passed to plugin callables is a
:class:`~sopel.bot.SopelWrapper` that handles this for the default
``destination`` of the methods it overrides (most importantly,
:meth:`~sopel.bot.SopelWrapper.say` &
:meth:`~sopel.bot.SopelWrapper.reply`).
.. warning::
The ``sender`` Will be ``None`` for commands that have no implicit
Expand All @@ -337,6 +349,7 @@ class Trigger(str):
The :attr:`COMMANDS_WITH_CONTEXT` attribute lists IRC commands for
which ``sender`` can be relied upon.
.. __: https://modern.ircdocs.horse/#statusmsg-parameter
"""
status_prefix = property(lambda self: self._pretrigger.status_prefix)
"""The prefix used for the :attr:`sender` for status-specific messages.
Expand Down
82 changes: 82 additions & 0 deletions test/test_bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,32 @@ def mockplugin(tmpdir):
# -----------------------------------------------------------------------------
# sopel.bot.SopelWrapper

def test_wrapper_default_destination(mockbot, triggerfactory):
wrapper = triggerfactory.wrapper(
mockbot, ':[email protected] PRIVMSG #channel :test message')

assert wrapper.default_destination == '#channel'


def test_wrapper_default_destination_none(mockbot, triggerfactory):
wrapper = triggerfactory.wrapper(
mockbot, ':irc.example.com 301 Sopel :I am away.')

assert wrapper.default_destination is None


def test_wrapper_default_destination_statusmsg(mockbot, triggerfactory):
mockbot._isupport = mockbot.isupport.apply(
STATUSMSG=tuple('+'),
)

wrapper = triggerfactory.wrapper(
mockbot, ':[email protected] PRIVMSG +#channel :test message')

assert wrapper._trigger.sender == '#channel'
assert wrapper.default_destination == '+#channel'


def test_wrapper_say(mockbot, triggerfactory):
wrapper = triggerfactory.wrapper(
mockbot, ':[email protected] PRIVMSG #channel :test message')
Expand All @@ -112,6 +138,20 @@ def test_wrapper_say(mockbot, triggerfactory):
)


def test_wrapper_say_statusmsg(mockbot, triggerfactory):
mockbot._isupport = mockbot.isupport.apply(
STATUSMSG=tuple('+'),
)

wrapper: bot.SopelWrapper = triggerfactory.wrapper(
mockbot, ':[email protected] PRIVMSG +#channel :test message')
wrapper.say('Hi!')

assert mockbot.backend.message_sent == rawlist(
'PRIVMSG +#channel :Hi!'
)


def test_wrapper_say_override_destination(mockbot, triggerfactory):
wrapper = triggerfactory.wrapper(
mockbot, ':[email protected] PRIVMSG #channel :test message')
Expand All @@ -132,6 +172,20 @@ def test_wrapper_notice(mockbot, triggerfactory):
)


def test_wrapper_notice_statusmsg(mockbot, triggerfactory):
mockbot._isupport = mockbot.isupport.apply(
STATUSMSG=tuple('+'),
)

wrapper: bot.SopelWrapper = triggerfactory.wrapper(
mockbot, ':[email protected] PRIVMSG +#channel :test message')
wrapper.notice('Hi!')

assert mockbot.backend.message_sent == rawlist(
'NOTICE +#channel :Hi!'
)


def test_wrapper_notice_override_destination(mockbot, triggerfactory):
wrapper = triggerfactory.wrapper(
mockbot, ':[email protected] PRIVMSG #channel :test message')
Expand All @@ -152,6 +206,20 @@ def test_wrapper_action(mockbot, triggerfactory):
)


def test_wrapper_action_statusmsg(mockbot, triggerfactory):
mockbot._isupport = mockbot.isupport.apply(
STATUSMSG=tuple('+'),
)

wrapper: bot.SopelWrapper = triggerfactory.wrapper(
mockbot, ':[email protected] PRIVMSG +#channel :test message')
wrapper.action('Hi!')

assert mockbot.backend.message_sent == rawlist(
'PRIVMSG +#channel :\x01ACTION Hi!\x01'
)


def test_wrapper_action_override_destination(mockbot, triggerfactory):
wrapper = triggerfactory.wrapper(
mockbot, ':[email protected] PRIVMSG #channel :test message')
Expand All @@ -172,6 +240,20 @@ def test_wrapper_reply(mockbot, triggerfactory):
)


def test_wrapper_reply_statusmsg(mockbot, triggerfactory):
mockbot._isupport = mockbot.isupport.apply(
STATUSMSG=tuple('+'),
)

wrapper: bot.SopelWrapper = triggerfactory.wrapper(
mockbot, ':[email protected] PRIVMSG +#channel :test message')
wrapper.reply('Hi!')

assert mockbot.backend.message_sent == rawlist(
'PRIVMSG +#channel :Test: Hi!'
)


def test_wrapper_reply_override_destination(mockbot, triggerfactory):
wrapper = triggerfactory.wrapper(
mockbot, ':[email protected] PRIVMSG #channel :test message')
Expand Down

0 comments on commit e5bcd1f

Please sign in to comment.