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

module: add require account feature #1733

Merged
merged 4 commits into from
Nov 18, 2019
Merged
Show file tree
Hide file tree
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
62 changes: 52 additions & 10 deletions sopel/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
'nickname_commands',
'priority',
'rate',
'require_account',
'require_admin',
'require_chanmsg',
'require_owner',
Expand Down Expand Up @@ -384,18 +385,16 @@ def require_privmsg(message=None, reply=False):
"""
def actual_decorator(function):
@functools.wraps(function)
def _nop(*args, **kwargs):
# Assign trigger and bot for easy access later
bot, trigger = args[0:2]
def guarded(bot, trigger, *args, **kwargs):
if trigger.is_privmsg:
return function(*args, **kwargs)
return function(bot, trigger, *args, **kwargs)
else:
if message and not callable(message):
if reply:
bot.reply(message)
else:
bot.say(message)
return _nop
return guarded

# Hack to allow decorator without parens
if callable(message):
Expand All @@ -421,25 +420,68 @@ def require_chanmsg(message=None, reply=False):
"""
def actual_decorator(function):
@functools.wraps(function)
def _nop(*args, **kwargs):
# Assign trigger and bot for easy access later
bot, trigger = args[0:2]
def guarded(bot, trigger, *args, **kwargs):
if not trigger.is_privmsg:
return function(*args, **kwargs)
return function(bot, trigger, *args, **kwargs)
else:
if message and not callable(message):
if reply:
bot.reply(message)
else:
bot.say(message)
return _nop
return guarded

# Hack to allow decorator without parens
if callable(message):
return actual_decorator(message)
return actual_decorator


def require_account(message=None, reply=False):
"""Decorate a function to require services/NickServ authentication.

:param str message: optional message to say if a user without
authentication tries to trigger this function
:param bool reply: use :meth:`~.bot.Sopel.reply` instead of
:meth:`~.bot.Sopel.say` when ``True``; defaults to
``False``

.. versionadded:: 7.0.0
.. note::

Only some networks support services authentication, and not all of
those implement the standards required for clients like Sopel to
determine authentication status. This decorator will block *all* use
of functions it decorates on networks that lack the relevant features.

.. seealso::

The value of the :class:`trigger<.trigger.Trigger>`'s
:meth:`account<.trigger.Trigger.account>` property determines whether
this requirement is satisfied, and the property's documentation
includes up-to-date details on what features a network must
support to allow Sopel to fetch account information.
"""
def actual_decorator(function):
@functools.wraps(function)
def guarded(bot, trigger, *args, **kwargs):
if not trigger.account:
if message and not callable(message):
if reply:
bot.reply(message)
else:
bot.say(message)
else:
return function(bot, trigger, *args, **kwargs)
return guarded

# Hack to allow decorator without parens
if callable(message):
return actual_decorator(message)

return actual_decorator


def require_privilege(level, message=None, reply=False):
"""Decorate a function to require at least the given channel permission.

Expand Down
24 changes: 22 additions & 2 deletions test/test_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ def trigger_owner(bot):
return Trigger(bot.config, PreTrigger(Identifier('Bar'), line), None)


@pytest.fixture
def trigger_account(bot):
line = '@account=egg :egg!egg@eg.gs PRIVMSG #Sopel :Hello, world'
return Trigger(bot.config, PreTrigger(Identifier('egg'), line), None, 'egg')


@pytest.fixture
def trigger(bot, pretrigger):
return Trigger(bot.config, pretrigger, None)
Expand Down Expand Up @@ -323,8 +329,22 @@ def mock(bot, trigger, match=None):
@module.require_chanmsg
def mock_(bot, trigger, match=None):
return True
assert mock(bot, trigger) is True
assert mock(bot, trigger_pm) is not True
assert mock_(bot, trigger) is True
assert mock_(bot, trigger_pm) is not True


def test_require_account(bot, trigger, trigger_account):
@module.require_account('You need to authenticate to services first.')
def mock(bot, trigger, match=None):
return True
assert mock(bot, trigger) is not True
assert mock(bot, trigger_account) is True

@module.require_account
def mock_(bot, trigger, match=None):
return True
assert mock_(bot, trigger) is not True
assert mock_(bot, trigger_account) is True


def test_require_privilege(bot, trigger):
Expand Down