Authorization module to handle roles

This commit is contained in:
Davte 2019-07-19 17:08:53 +02:00
parent 3c12b00839
commit 4dd88a0b2c

224
davtelepot/authorization.py Normal file
View File

@ -0,0 +1,224 @@
"""Provide authorization levels to bot functions."""
# Standard library modules
from collections import OrderedDict
# Project modules
from .utilities import make_button
DEFAULT_ROLES = OrderedDict()
DEFAULT_ROLES[0] = {
'name': 'banned',
'symbol': '🚫',
'singular': 'banned',
'plural': 'banned',
'can_appoint': [],
'can_be_appointed_by': [1, 2, 3]
}
DEFAULT_ROLES[1] = {
'name': 'founder',
'symbol': '👑',
'singular': 'founder',
'plural': 'founders',
'can_appoint': [0, 1, 2, 3, 4, 5, 7, 100],
'can_be_appointed_by': []
}
DEFAULT_ROLES[2] = {
'name': 'admin',
'symbol': '⚜️',
'singular': 'administrator',
'plural': 'administrators',
'can_appoint': [0, 3, 4, 5, 7, 100],
'can_be_appointed_by': [1]
}
DEFAULT_ROLES[3] = {
'name': 'moderator',
'symbol': '🔰',
'singular': 'moderator',
'plural': 'moderators',
'can_appoint': [0, 5, 7],
'can_be_appointed_by': [1, 2]
}
DEFAULT_ROLES[5] = {
'name': 'user',
'symbol': '🎫',
'singular': 'registered user',
'plural': 'registered users',
'can_appoint': [],
'can_be_appointed_by': [1, 2, 3]
}
DEFAULT_ROLES[100] = {
'name': 'everybody',
'symbol': '👤',
'singular': 'common user',
'plural': 'common users',
'can_appoint': [],
'can_be_appointed_by': [1, 2, 3]
}
class Role():
"""Authorization level for users of a bot."""
roles = OrderedDict()
def __init__(self, code, name, symbol, singular, plural,
can_appoint, can_be_appointed_by):
"""Instantiate Role object.
code : int
The higher the code, the less privileges are connected to that
role.
Use 0 for banned users.
name : str
Short name for role.
symbol : str
Emoji used to represent role.
singular : str
Singular full name of role.
plural : str
Plural full name of role.
can_appoint : lsit of int
List of role codes that this role can appoint.
can_be_appointed_by : list of int
List of role codes this role can be appointed by.
"""
self._code = code
self._name = name
self._symbol = symbol
self._singular = singular
self._plural = plural
self._can_appoint = can_appoint
self._can_be_appointed_by = can_be_appointed_by
self.__class__.roles[self.code] = self
@property
def code(self):
"""Return code."""
return self._code
@property
def name(self):
"""Return name."""
return self._name
@property
def symbol(self):
"""Return symbol."""
return self._symbol
@property
def singular(self):
"""Return singular."""
return self._singular
@property
def plural(self):
"""Return plural."""
return self._plural
@property
def can_appoint(self):
"""Return can_appoint."""
return self._can_appoint
@property
def can_be_appointed_by(self):
"""Return roles whom this role can be appointed by."""
return self._can_be_appointed_by
@classmethod
def get_by_role_id(cls, role_id=100):
"""Give a `role_id`, return the corresponding `Role` instance."""
for code, role in cls.roles.items():
if code == role_id:
return role
raise IndexError(f"Unknown role id: {role_id}")
@classmethod
def get_user_role(cls, user_record=None, role_id=None):
"""Given a `user_record`, return its `Role`.
`role_id` may be passed as keyword argument or as user_record.
"""
if isinstance(user_record, dict) and 'privileges' in user_record:
user_role_id = user_record['privileges']
elif type(user_record) is int:
user_role_id = user_record
if type(user_role_id) is not int:
user_role_id = 100
return cls.get_by_role_id(role_id=role_id)
@classmethod
def get_user_role_panel(cls, user_record):
"""Get text and buttons for user role panel."""
user_role = cls.get_user_role(user_record)
text = (
"""👤 <a href="tg://user?id={u[telegram_id]}">{u[username]}</a>\n"""
f"🔑 <i>{user_role.singular.capitalize()}</i> {user_role.symbol}"
).format(
u=user_record,
)
buttons = [
make_button(
f"{role.symbol} {role.singular.capitalize()}",
f"auth:///set|{user_record['id']}_{code}"
)
for code, role in cls.roles.items()
]
return text, buttons
def __eq__(self, other):
"""Return True if self is equal to other."""
return self.code == other.code
def __gt__(self, other):
"""Return True if self can appoint other."""
return (
self.code < other.code
and self.code in other.can_be_appointed_by
)
def __ge__(self, other):
"""Return True if self >= other."""
return self.__gt__(other) or self.__eq__(other)
def __lt__(self, other):
"""Return True if self can not appoint other."""
return not self.__ge__(other)
def __le__(self, other):
"""Return True if self is superior or equal to other."""
return not self.__gt__(other)
def __ne__(self, other):
"""Return True if self is not equal to other."""
return not self.__eq__(other)
def __str__(self):
"""Return human-readable description of role."""
return f"<Role object: {self.symbol} {self.singular.capitalize()}>"
def init(bot, roles=None):
"""Set bot roles and assign role-related commands.
Pass an OrderedDict of `roles` to get them set.
"""
class _Role(Role):
roles = OrderedDict()
if roles is None:
roles = DEFAULT_ROLES
# Cast roles to OrderedDict
if isinstance(roles, list):
roles = OrderedDict(
(i, element)
for i, element in enumerate(list)
)
if not isinstance(roles, OrderedDict):
raise TypeError("`roles` shall be a OrderedDict!")
for id, role in roles.items():
if 'code' not in role:
role['code'] = id
Role(**role)