User privileges#

IRC users can have privileges in a channel, given by MODE messages such as:

MODE #example +ov Nickname Nickname

This will give both OP and Voice privileges to the user named “Nickname” in the “#example” channel (and only in this channel). When Sopel receives a MODE message it registers and updates its knowledge of a user’s privileges in a channel, which can be used by plugins in various way.

Access rights#

Privileged users#

A plugin can limit who can trigger its callables using the require_privilege() decorator:

from sopel import plugin, privileges

@plugin.require_privilege(privileges.OP)
@plugin.require_chanmsg
@plugin.command('chanopcommand')
def chanop_command(bot, trigger):
    # only a channel operator can use this command

This way, only users with OP privileges or above in a channel can use the command chanopcommand in that channel: other users will be ignored by the bot. It is possible to tell these users why with the message parameter:

@plugin.require_privilege(privileges.OP, 'You need +o privileges.')

Important

A command that requires channel privileges will always execute if called from a private message to the bot. You can use the sopel.plugin.require_chanmsg() decorator to ignore the command if it’s called in PMs.

The bot is a user too#

Sometimes, you may want the bot to be a privileged user in a channel to allow a command. For that, there is the require_bot_privilege() decorator:

@plugin.require_bot_privilege(privileges.OP)
@plugin.require_chanmsg
@plugin.command('opbotcommand')
def change_topic(bot, trigger):
    # only if the bot has OP privileges

This way, this command cannot be used if the bot doesn’t have the right privileges in the channel where it is used, independent from the privileges of the user who invokes the command.

As with require_privilege, you can provide an error message:

@plugin.require_bot_privilege(
    privileges.OP, 'The bot needs +o privileges.')

And you can use both require_privilege and require_bot_privilege on the same plugin callable:

@plugin.require_privilege(privileges.VOICE)
@plugin.require_bot_privilege(privileges.OP)
@plugin.require_chanmsg
@plugin.command('special')
def special_command(bot, trigger):
    # only if the user has +v and the bot has +o (or above)

This way, you can allow a less privileged user to access a command for a more privileged bot (this works for any combination of privileges).

Important

A command that requires channel privileges will always execute if called from a private message to the bot. You can use the sopel.plugin.require_chanmsg() decorator to ignore the command if it’s called in PMs.

Restrict to user account#

Sometimes, a command should be used only by users who are authenticated via IRC services. On IRC networks that provide such information to IRC clients, this is possible with the require_account() decorator:

@plugin.require_privilege(privileges.VOICE)
@plugin.require_account
@plugin.require_chanmsg
@plugin.command('danger')
def dangerous_command(bot, trigger):
    # only if the user has +v and has a registered account

This has two consequences:

  1. this command cannot be used by users who are not authenticated

  2. this command cannot be used on an IRC network that doesn’t allow authentication or doesn’t expose that information

It makes your plugin safer to use and prevents the possibility to use it on insecure IRC networks.

Getting user privileges in a channel#

Within a plugin callable, you can get access to a user’s privileges in a channel to check privileges manually. For example, you could adapt the level of information your callable provides based on said privileges.

First you need a user’s nick and a channel (e.g. from the trigger parameter), then you can get that user’s privileges through the channel’s privileges attribute:

user_privileges = channel.privileges['Nickname']
user_privileges = channel.privileges[trigger.nick]

You can check the user’s privileges manually using bitwise operators. Here for example, we check if the user is voiced (+v) or above:

from sopel import privileges

if user_privileges & privileges.VOICE:
    # user is voiced
elif user_privileges > privileges.VOICE:
    # not voiced, but higher privileges
    # like privileges.HALFOP or privileges.OP
else:
    # no privilege

Another option is to use dedicated methods from the channel object:

if channel.is_voiced('Nickname'):
    # user is voiced
elif channel.has_privilege('Nickname', privileges.VOICE):
    # not voiced, but higher privileges
    # like privileges.HALFOP or privileges.OP
else:
    # no privilege

You can also iterate over the list of users and filter them by privileges:

# get users with the OP privilege
op_users = [
    user
    for nick, user in channel.users
    if channel.is_op(nick, privileges.OP)
]

# get users with OP privilege or above
op_or_higher_users = [
    user
    for nick, user in channel.users
    if channel.has_privileges(nick, privileges.OP)
]

See also

Read about the Channel and User classes for more details.

sopel.privileges#

Constants for user privileges in channels.

Privilege levels#

Historically, there were two user privileges in channels:

  • OP: channel operator, or chanop, set and unset by +o and -o

  • VOICE: the privilege to send messages to a channel with the +m mode, set and unset by +v and -v

Since then, other privileges have been adopted by IRC servers and clients:

  • HALFOP: intermediate level between Voiced and OP, set and unset by +h and -h

  • ADMIN: channel admin, above OP and below OWNER, set and unset by +a and -a

  • OWNER: channel owner, above ADMIN and OP, set and unset by +q and -q

Important

Not all IRC networks support these added privilege modes. If you are writing a plugin for public distribution, ensure your code behaves sensibly if only +v (voice) and +o (op) modes exist.

Compare privileges#

This module represents privileges as powers of two, with higher values assigned to higher-level privileges:

>>> from sopel.privileges import VOICE, HALFOP, OP, ADMIN, OWNER
>>> VOICE < HALFOP < OP < ADMIN < OWNER
True

Then a user’s privileges are represented as a sum of privilege levels:

>>> VOICE
1
>>> OP
4
>>> priv = VOICE | OP
>>> priv
5

This allows to use comparators and bitwise operators to compare privileges:

>>> priv >= OP
True
>>> bool(priv & HALFOP)
False

In that case, priv contains both VOICE and OP privileges, but not HALFOP.

sopel.privileges.VOICE = 1#

Privilege level for the +v channel permission

New in version 4.1.

Changed in version 8.0: Moved into sopel.privileges.

sopel.privileges.HALFOP = 2#

Privilege level for the +h channel permission

New in version 4.1.

Changed in version 8.0: Moved into sopel.privileges.

Important

Not all IRC networks support this privilege mode. If you are writing a plugin for public distribution, ensure your code behaves sensibly if only +v (voice) and +o (op) modes exist.

sopel.privileges.OP = 4#

Privilege level for the +o channel permission

New in version 4.1.

Changed in version 8.0: Moved into sopel.privileges.

sopel.privileges.ADMIN = 8#

Privilege level for the +a channel permission

New in version 4.1.

Changed in version 8.0: Moved into sopel.privileges.

Important

Not all IRC networks support this privilege mode. If you are writing a plugin for public distribution, ensure your code behaves sensibly if only +v (voice) and +o (op) modes exist.

sopel.privileges.OWNER = 16#

Privilege level for the +q channel permission

New in version 4.1.

Changed in version 8.0: Moved into sopel.privileges.

Important

Not all IRC networks support this privilege mode. If you are writing a plugin for public distribution, ensure your code behaves sensibly if only +v (voice) and +o (op) modes exist.

sopel.privileges.OPER = 32#

Privilege level for the +y/+Y channel permissions

Note: Except for these (non-standard) channel modes, Sopel does not monitor or store any user’s OPER status.

New in version 7.0.

Changed in version 8.0: Moved into sopel.privileges.

Important

Not all IRC networks support this privilege mode. If you are writing a plugin for public distribution, ensure your code behaves sensibly if only +v (voice) and +o (op) modes exist.