From 258ca9f6efadb7d63c277735cb6a0179e2d4bc1d Mon Sep 17 00:00:00 2001 From: Davte Date: Wed, 13 May 2020 15:31:20 +0200 Subject: [PATCH] /ban command implemented --- davtelepot/authorization.py | 265 +++++++++++++----------------------- 1 file changed, 94 insertions(+), 171 deletions(-) diff --git a/davtelepot/authorization.py b/davtelepot/authorization.py index 03322b6..4c30cd7 100644 --- a/davtelepot/authorization.py +++ b/davtelepot/authorization.py @@ -2,7 +2,7 @@ # Standard library modules from collections import OrderedDict -from typing import Callable, Union, List +from typing import Callable, List, Union # Project modules from .bot import Bot @@ -68,14 +68,14 @@ class Role: roles = OrderedDict() default_role_code = 100 - def __init__(self, code: int, name: str, symbol: str, - singular: str, plural: str, - can_appoint: List[int], can_be_appointed_by: List[int]): + 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. + role. + Use 0 for banned users. name : str Short name for role. symbol : str @@ -84,7 +84,7 @@ class Role: Singular full name of role. plural : str Plural full name of role. - can_appoint : list of int + 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. @@ -99,42 +99,42 @@ class Role: self.__class__.roles[self.code] = self @property - def code(self) -> int: + def code(self): """Return code.""" return self._code @property - def name(self) -> str: + def name(self): """Return name.""" return self._name @property - def symbol(self) -> str: + def symbol(self): """Return symbol.""" return self._symbol @property - def singular(self) -> str: + def singular(self): """Return singular.""" return self._singular @property - def plural(self) -> str: + def plural(self): """Return plural.""" return self._plural @property - def can_appoint(self) -> List[int]: + def can_appoint(self): """Return can_appoint.""" return self._can_appoint @property - def can_be_appointed_by(self) -> List[int]: + 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) -> 'Role': + def get_by_role_id(cls, role_id=100): """Given a `role_id`, return the corresponding `Role` instance.""" for code, role in cls.roles.items(): if code == role_id: @@ -142,7 +142,7 @@ class Role: raise IndexError(f"Unknown role id: {role_id}") @classmethod - def get_role_by_name(cls, name='everybody') -> 'Role': + def get_role_by_name(cls, name='everybody'): """Given a `name`, return the corresponding `Role` instance.""" for role in cls.roles.values(): if role.name == name: @@ -150,7 +150,9 @@ class Role: raise IndexError(f"Unknown role name: {name}") @classmethod - def get_user_role(cls, user_record=None, user_role_id=None) -> 'Role': + def get_user_role(cls, + user_record: OrderedDict = None, + user_role_id: int = None) -> 'Role': """Given a `user_record`, return its `Role`. `role_id` may be passed as keyword argument or as user_record. @@ -170,7 +172,7 @@ class Role: return cls.get_by_role_id(role_id=user_role_id) @classmethod - def set_default_role_code(cls, role: int) -> None: + def set_default_role_code(cls, role): """Set class default role code. It will be returned if a specific role code cannot be evaluated. @@ -178,82 +180,30 @@ class Role: cls.default_role_code = role @classmethod - def get_user_role_text(cls, - user_record: OrderedDict, - user_role: 'Role' = None) -> str: - """ - Get a string to describe the role of a user. - - @param user_record: record of table `users` about the user; it must - contain at least a [username | last_name | first_name] and a - telegram identifier. - @param user_role: Role instance about user permissions. - @return: String to describe the role of a user, like this: - ``` - 👤 LinkedUsername - 🔑 Admin ⚜️ - ``` - """ - if user_role is None: - user_role = cls.get_user_role(user_record=user_record) - return ( - f"""👤 {get_user(record=user_record)}\n""" + def get_user_role_panel(cls, user_record): + """Get text and buttons for user role panel.""" + user_role = cls.get_user_role(user_record=user_record) + text = ( + """👤 {u[username]}\n""" f"🔑 {user_role.singular.capitalize()} {user_role.symbol}" + ).format( + u=user_record, ) - - @classmethod - def get_user_role_buttons(cls, - user_record: OrderedDict, - admin_record: OrderedDict, - user_role: 'Role' = None, - admin_role: 'Role' = None) -> List[dict]: - """ Return buttons to edit user permissions. - @param user_record: record of table `users` about the user; it must - contain at least a [username | last_name | first_name] and a - telegram identifier. - @param admin_record: record of table `users` about the admin; it must - contain at least a [username | last_name | first_name] and a - telegram identifier. - @param user_role: Role instance about user permissions. - @param admin_role: Role instance about admin permissions. - @return: list of `InlineKeyboardButton`s. - """ - if admin_role is None: - admin_role = cls.get_user_role(user_record=admin_record) - if user_role is None: - user_role = cls.get_user_role(user_record=user_record) - return [ + buttons = [ make_button( f"{role.symbol} {role.singular.capitalize()}", prefix='auth:///', data=['set', user_record['id'], code] ) for code, role in cls.roles.items() - if (admin_role > user_role - and code in admin_role.can_appoint - and code != user_role.code) ] - - @classmethod - def get_user_role_text_and_buttons(cls, - user_record: OrderedDict, - admin_record: OrderedDict): - """Get text and buttons for user role panel.""" - admin_role = cls.get_user_role(user_record=admin_record) - user_role = cls.get_user_role(user_record=user_record) - text = cls.get_user_role_text(user_record=user_record, - user_role=user_role) - buttons = cls.get_user_role_buttons(user_record=user_record, - user_role=user_role, - admin_record=admin_record, - admin_role=admin_role) return text, buttons - def __eq__(self, other: 'Role'): + def __eq__(self, other): """Return True if self is equal to other.""" return self.code == other.code - def __gt__(self, other: 'Role'): + def __gt__(self, other): """Return True if self can appoint other.""" return ( ( @@ -263,19 +213,19 @@ class Role: and self.code in other.can_be_appointed_by ) - def __ge__(self, other: 'Role'): + def __ge__(self, other): """Return True if self >= other.""" return self.__gt__(other) or self.__eq__(other) - def __lt__(self, other: 'Role'): + def __lt__(self, other): """Return True if self can not appoint other.""" return not self.__ge__(other) - def __le__(self, other: 'Role'): + def __le__(self, other): """Return True if self is superior or equal to other.""" return not self.__gt__(other) - def __ne__(self, other: 'Role'): + def __ne__(self, other): """Return True if self is not equal to other.""" return not self.__eq__(other) @@ -284,7 +234,7 @@ class Role: return f"" -def get_authorization_function(bot: Bot): +def get_authorization_function(bot): """Take a `bot` and return its authorization_function.""" def is_authorized(update, user_record=None, authorization_level=2): @@ -310,69 +260,51 @@ def get_authorization_function(bot: Bot): return is_authorized -async def _authorization_command(bot: Bot, - update: dict, - user_record: OrderedDict, - mode: str = 'auth'): - db = bot.db - text = get_cleaned_text(bot=bot, update=update, replace=[mode]) +async def _authorization_command(bot, update, user_record): + text = get_cleaned_text(bot=bot, update=update, replace=['auth']) reply_markup = None - admin_record = user_record.copy() - user_record = None - admin_role = bot.Role.get_user_role(user_record=admin_record) + # noinspection PyUnusedLocal result = bot.get_message( 'authorization', 'auth_command', 'unhandled_case', - update=update, user_record=admin_record + update=update, user_record=user_record ) - if not text: # No text provided: command must be used in reply - if 'reply_to_message' not in update: # No text and not in reply - result = bot.get_message( + if not text: + if 'reply_to_message' not in update: + return bot.get_message( 'authorization', 'auth_command', 'instructions', - update=update, user_record=admin_record, - command=mode + update=update, user_record=user_record ) - else: # No text, command used in reply to another message - update = update['reply_to_message'] - # Forwarded message: get both the user who forwarded and the original author - if ('forward_from' in update - and update['from']['id'] != update['forward_from']['id']): - user_record = list( - db['users'].find( - telegram_id=[update['from']['id'], - update['forward_from']['id']] - ) - ) - else: # Otherwise: get the author of the message + else: + with bot.db as db: user_record = db['users'].find_one( - telegram_id=update['from']['id'] + telegram_id=update['reply_to_message']['from']['id'] + ) + else: + with bot.db as db: + user_record = list( + db.query( + "SELECT * " + "FROM users " + "WHERE COALESCE(" + " first_name || last_name || username," + " last_name || username," + " first_name || username," + " username," + " first_name || last_name," + " last_name," + " first_name" + f") LIKE '%{text}%'" ) - else: # Get users matching the input text - user_record = list( - db.query( - "SELECT * " - "FROM users " - "WHERE COALESCE(" - " first_name || last_name || username," - " last_name || username," - " first_name || username," - " username," - " first_name || last_name," - " last_name," - " first_name" - f") LIKE '%{text}%'" ) - ) - if len(user_record) == 1: - user_record = user_record[0] - if user_record is None: # If query was not provided and user cannot be found + if user_record is None: result = bot.get_message( 'authorization', 'auth_command', 'unknown_user', - update=update, user_record=admin_record + update=update, user_record=user_record ) - elif type(user_record) is list and len(user_record) > 1: # If many users match + elif type(user_record) is list and len(user_record) > 1: result = bot.get_message( 'authorization', 'auth_command', 'choose_user', - update=update, user_record=admin_record, + update=update, user_record=user_record, n=len(user_record) ) reply_markup = make_inline_keyboard( @@ -386,25 +318,15 @@ async def _authorization_command(bot: Bot, ], 3 ) - elif type(user_record) is list and len(user_record) == 0: # If query was provided but no user matches + elif type(user_record) is list and len(user_record) == 0: result = bot.get_message( 'authorization', 'auth_command', 'no_match', - update=update, user_record=admin_record, - ) - elif isinstance(user_record, dict): # If 1 user matches - # Ban user if admin can do it - user_role = bot.Role.get_user_role(user_record=user_record) - if mode == 'ban' and admin_role > user_role: - user_record['privileges'] = 0 - db['users'].update( - user_record, - ['id'] - ) - # Show user panel (text and buttons) to edit user permissions - result, buttons = bot.Role.get_user_role_text_and_buttons( - user_record=user_record, - admin_record=admin_record + update=update, user_record=user_record, ) + else: + if type(user_record) is list: + user_record = user_record[0] + result, buttons = bot.Role.get_user_role_panel(user_record) reply_markup = make_inline_keyboard(buttons, 1) return dict( text=result, @@ -423,13 +345,10 @@ async def _authorization_button(bot, update, user_record, data): else: other_user_id = None result, text, reply_markup = '', '', None - db = bot.db if command in ['show']: - other_user_record = db['users'].find_one(id=other_user_id) - text, buttons = bot.Role.get_user_role_text_and_buttons( - user_record=other_user_record, - admin_record=user_record - ) + with bot.db as db: + other_user_record = db['users'].find_one(id=other_user_id) + text, buttons = bot.Role.get_user_role_panel(other_user_record) reply_markup = make_inline_keyboard(buttons, 1) elif command in ['set'] and len(arguments) > 1: other_user_id, new_privileges, *_ = arguments @@ -441,7 +360,8 @@ async def _authorization_button(bot, update, user_record, data): 'authorization', 'auth_button', 'confirm', update=update, user_record=user_record, ) - other_user_record = db['users'].find_one(id=other_user_id) + with bot.db as db: + other_user_record = db['users'].find_one(id=other_user_id) user_role = bot.Role.get_user_role(user_record=user_record) other_user_role = bot.Role.get_user_role(user_record=other_user_record) if other_user_role.code == new_privileges: @@ -486,22 +406,20 @@ async def _authorization_button(bot, update, user_record, data): 1 ) else: - db['users'].update( - dict( - id=other_user_id, - privileges=new_privileges - ), - ['id'] - ) - other_user_record = db['users'].find_one(id=other_user_id) + with bot.db as db: + db['users'].update( + dict( + id=other_user_id, + privileges=new_privileges + ), + ['id'] + ) + other_user_record = db['users'].find_one(id=other_user_id) result = bot.get_message( 'authorization', 'auth_button', 'appointed', update=update, user_record=user_record ) - text, buttons = bot.Role.get_user_role_text_and_buttons( - user_record=other_user_record, - admin_record=user_record - ) + text, buttons = bot.Role.get_user_role_panel(other_user_record) reply_markup = make_inline_keyboard(buttons, 1) if text: return dict( @@ -515,9 +433,14 @@ async def _authorization_button(bot, update, user_record, data): return result +async def _ban_command(bot, update, user_record): + # TODO define this function! + return + + def default_get_administrators_function(bot: Bot): return list( - bot.db['users'].find(privileges=[1, 2]) + bot.db['users'].find(privileges=[1,2]) ) @@ -577,6 +500,6 @@ def init(telegram_bot: Bot, @telegram_bot.command('/ban', aliases=[], show_in_keyboard=False, description=authorization_messages['ban_command']['description'], - authorization_level='moderator') + authorization_level='admin') async def ban_command(bot, update, user_record): - return await _authorization_command(bot, update, user_record, mode='ban') + return await _ban_command(bot, update, user_record)