diff --git a/bot_modules/mod_table_operate.py b/bot_modules/mod_table_operate.py index aa8fb9c..9fccfb5 100644 --- a/bot_modules/mod_table_operate.py +++ b/bot_modules/mod_table_operate.py @@ -48,23 +48,19 @@ class Messages(Enum): SELECT_TO_DELETE = auto() SUCCESS_DELETE = auto() -class FSMs(Enum): - CREATE = auto() - -create_fsms_cmd = ''' +create_fsm_create_cmd = ''' class FSMCreate{a_ModName}(StatesGroup): - name = State() - desc = State() - photo = State() - +{items} -fsm = { - FSMs.CREATE: FSMCreate{a_ModName}, -} +fsm = FSMCreate{a_ModName} ''' -def MakeFSMs(a_ModName): - cmd = create_fsms_cmd.replace("{a_ModName}", a_ModName) +def MakeFSMForAdd(a_ModName, a_Fields): + cmd = create_fsm_create_cmd.replace("{a_ModName}", a_ModName) + items = "" + for i in range(len(a_Fields)): + items += f"\titem{i} = State()\n" + cmd = cmd.replace("{items}", items) _locals = locals() exec(cmd, globals(), _locals) return _locals['fsm'] @@ -79,7 +75,6 @@ fsm = FSMEdit{a_ModName}_{a_FieldName}_Item def MakeFSMForEdit(a_ModName, a_FieldName): cmd = edit_fsm_cmd.replace("{a_ModName}", a_ModName).replace("{a_FieldName}", a_FieldName) - print ('cmd', cmd) _locals = locals() exec(cmd, globals(), _locals) return _locals['fsm'] @@ -88,7 +83,6 @@ class TableOperateModule(mod_simple_message.SimpleMessageModule): def __init__(self, a_Table, a_Messages, a_Buttons, a_ParentModName, a_ChildModName, a_InitAccess, a_ChildModuleNameList, a_EditModuleNameList, a_Bot, a_ModuleAgregator, a_BotMessages, a_BotButtons, a_Log): super().__init__(a_Messages, a_Buttons, a_InitAccess, a_ChildModuleNameList, a_Bot, a_ModuleAgregator, a_BotMessages, a_BotButtons, a_Log) self.m_Table = a_Table - self.m_FSMs = MakeFSMs(self.GetName()) self.m_EditModuleNameList = a_EditModuleNameList self.m_ChildModName = a_ChildModName self.m_ParentModName = a_ParentModName @@ -114,9 +108,6 @@ class TableOperateModule(mod_simple_message.SimpleMessageModule): return await self.AddBDItemFunc(a_ItemData, a_UserID) self.m_AddBDItemFunc = AddBDItemFunc - def GetFSM(self, a_FSMName): - return self.m_FSMs.get(a_FSMName, None) - def GetInitBDCommands(self): return [ self.m_Table.GetInitTableRequest(), @@ -350,6 +341,58 @@ class TableOperateModule(mod_simple_message.SimpleMessageModule): field_type = a_FieldType\ ) + def GetAddFields(self): + fields = [] + for f in self.m_Table.GetFields(): + if f.m_Destiny in (bd_table.TableFieldDestiny.NAME, bd_table.TableFieldDestiny.DESC, bd_table.TableFieldDestiny.PHOTO, bd_table.TableFieldDestiny.SUBSCRIBE_TYPE, bd_table.TableFieldDestiny.ITEM_ID, ): + fields += [f] + return fields + + def AddBDItem3RegisterHandlers(self, a_StartCheckFunc, a_AddBDItemFunc, a_ParentPrefix, a_ParentTableName : str, a_ParentKeyFieldName, a_GetButtonNameAndKeyValueAndAccessFunc, a_AccessFunc, a_ButtonFunc, access_mode = user_access.AccessMode.ADD): + fields = self.GetAddFields() + if len(fields) == 0: + return + fsm = MakeFSMForAdd(self.GetName(), fields) + + keyboard_cancel = bd_item.GetCancelKeyboardButtonsTemplate(self.m_Bot, a_AccessFunc, access_mode) + keyboard_skip_and_cancel = bd_item.GetSkipAndCancelKeyboardButtonsTemplate(self.m_Bot, a_AccessFunc, access_mode) + reg_func = self.m_Bot.RegisterMessageHandler + + if a_ParentTableName: + reg_func = self.m_Bot.RegisterCallbackHandler + + def GetFieldType(a_Field): + if a_Field.m_Type == bd_table.TableFieldType.PHOTO: + return bd_item.FieldType.photo + return bd_item.FieldType.text + + def GetContentTypes(a_Field): + if a_Field.m_Type == bd_table.TableFieldType.PHOTO: + return ['photo', 'text'] + return ['text'] + + def GetKeyboard(a_Field): + if a_Field.m_Type == bd_table.TableFieldType.PHOTO: + return keyboard_skip_and_cancel + return keyboard_cancel + + print ('fields', fields) + f_id = 0 + f = fields[f_id] + print ('f', f, f.m_Type, f.m_Destiny, GetFieldType(f)) + reg_func(bd_item_add.StartAddBDItemTemplate(self.m_Bot, fsm, getattr(fsm, f'item{f_id}'), self.ShowMessageTemplate(self.GetMessage(CreateMessage(f.m_Destiny))), a_ParentTableName, a_ParentKeyFieldName, a_ParentPrefix, a_AccessFunc, GetKeyboard(f), a_ButtonFunc, access_mode), a_StartCheckFunc) + + for i in range(len(fields) - 1): + f = fields[i] + next_f = fields[i + 1] + print ('f', f, f.m_Type, f.m_Destiny, GetFieldType(f)) + self.m_Bot.RegisterMessageHandler(bd_item_add.NextAddBDItemTemplate(self.m_Bot, fsm, None, a_ParentTableName, a_ParentKeyFieldName, f.m_Name, self.ShowMessageTemplate(self.GetMessage(CreateMessage(next_f.m_Destiny))), None, a_AccessFunc, GetKeyboard(next_f), a_ButtonFunc, access_mode, field_type = GetFieldType(f)), content_types = GetContentTypes(f), state = getattr(fsm, f'item{i}')) + + f_id = len(fields) - 1 + f = fields[f_id] + print ('f', f, f.m_Type, f.m_Destiny, GetFieldType(f)) + self.m_Bot.RegisterMessageHandler(bd_item_add.FinishAddBDItemTemplate(self.m_Bot, fsm, a_AddBDItemFunc, a_ParentTableName, a_ParentKeyFieldName, f.m_Name, self.ShowMessageTemplate(self.GetMessage(Messages.SUCCESS_CREATE)), None, a_AccessFunc, a_ButtonFunc, access_mode, field_type = GetFieldType(f)), content_types = GetContentTypes(f), state = getattr(fsm, f'item{f_id}')) + def RegisterHandlers(self): super().RegisterHandlers() table_name = self.m_Table.GetName() @@ -421,23 +464,12 @@ class TableOperateModule(mod_simple_message.SimpleMessageModule): if a_Prefix: check_func = bd_item.GetCheckForPrefixFunc(a_Prefix) - bd_item_add.AddBDItem3RegisterHandlers(self.m_Bot, \ + self.AddBDItem3RegisterHandlers(\ check_func, \ - self.GetFSM(FSMs.CREATE), \ - self.GetFSM(FSMs.CREATE).name,\ - self.GetFSM(FSMs.CREATE).desc, \ - self.GetFSM(FSMs.CREATE).photo,\ self.m_AddBDItemFunc, \ - self.ShowMessageTemplate(self.GetMessage(CreateMessage(bd_table.TableFieldDestiny.NAME))), \ - self.ShowMessageTemplate(self.GetMessage(CreateMessage(bd_table.TableFieldDestiny.DESC))), \ - self.ShowMessageTemplate(self.GetMessage(CreateMessage(bd_table.TableFieldDestiny.PHOTO))), \ - self.ShowMessageTemplate(self.GetMessage(Messages.SUCCESS_CREATE)), \ a_Prefix,\ parent_table_name, \ parent_key_name, \ - name_field, \ - desc_field, \ - photo_field, \ GetButtonNameAndKeyValueAndAccess, \ GetAccess, \ self.m_GetStartKeyboardButtonsFunc\ diff --git a/bot_modules/subscribes.py b/bot_modules/subscribes.py new file mode 100644 index 0000000..4d7075c --- /dev/null +++ b/bot_modules/subscribes.py @@ -0,0 +1,231 @@ +# -*- coding: utf8 -*- +# Общественное достояние, 2023, Алексей Безбородов (Alexei Bezborodov) + +# Заказы + +from bot_sys import bot_bd, keyboard, user_access, bd_table, bot_subscribes +from bot_modules import mod_table_operate, mod_simple_message +from template import bd_item_select, bd_item_view, bd_item + +from aiogram.dispatcher import FSMContext +from aiogram.dispatcher.filters.state import State, StatesGroup + +class FSMAddSubsType(StatesGroup): + bd_item = State() + +# --------------------------------------------------------- +# БД +module_name = 'subscribes' + +table_name = module_name +mod_name_field = 'modName' +type_field = 'subsType' +item_id_field = 'itemID' +access_field = 'subsAccess' +create_datetime_field = 'subsCreateDateTime' +parent_id_field = 'userID' + +table_mod_name_field = bd_table.TableField(mod_name_field, bd_table.TableFieldDestiny.NAME, bd_table.TableFieldType.STR) +table_type_field = bd_table.TableField(type_field, bd_table.TableFieldDestiny.SUBSCRIBE_TYPE, bd_table.TableFieldType.ENUM, a_Enum = bot_subscribes.SubscribeType) +table_item_id_field = bd_table.TableField(item_id_field, bd_table.TableFieldDestiny.ITEM_ID, bd_table.TableFieldType.STR) +table_user_id_field = bd_table.TableField(parent_id_field, bd_table.TableFieldDestiny.PARENT_ID, bd_table.TableFieldType.STR) + +table = bd_table.Table(table_name, [ + table_mod_name_field, + table_type_field, + table_item_id_field, + bd_table.TableField(access_field, bd_table.TableFieldDestiny.ACCESS, bd_table.TableFieldType.STR), + bd_table.TableField(create_datetime_field, bd_table.TableFieldDestiny.CREATE_DATE, bd_table.TableFieldType.STR), + table_user_id_field, + ], + [ + [table_mod_name_field, table_type_field, table_user_id_field, table_item_id_field], + ] + ) + +init_access = f'{user_access.user_access_group_new}=vea' + +class ButtonNames(Enum): + ADD_SUBS = auto() + +button_names = { + mod_simple_message.ButtonNames.START: "‍🛒 Подписки", + mod_table_operate.ButtonNames.LIST: "📃 Список моих текущих подписок", + mod_table_operate.ButtonNames.ADD_SUBS: "✅ Добавить подписку", + mod_table_operate.ButtonNames.EDIT: "🛠 Редактировать мою подписку", + mod_table_operate.EditButton(bd_table.TableFieldDestiny.NAME): "≂ Изменить название модуля в моей подписке", + mod_table_operate.EditButton(bd_table.TableFieldDestiny.SUBSCRIBE_TYPE): "𝌴 Изменить тип в моей подписке", + mod_table_operate.EditButton(bd_table.TableFieldDestiny.ITEM_ID): "𝌴 Изменить номер элемента в моей подписке", + mod_table_operate.EditButton(bd_table.TableFieldDestiny.ACCESS): "✋ Изменить доступ к моей подписке", + mod_table_operate.ButtonNames.DEL: "❌ Удалить мою подписку", +} + +messages = { + mod_simple_message.Messages.START: f''' +{button_names[mod_simple_message.ButtonNames.START]} + +''', + mod_table_operate.Messages.SELECT: ''' +Пожалуйста, выберите подписку: +''', + mod_table_operate.Messages.ERROR_FIND: ''' +❌ Ошибка, подписку не найден +''', + mod_table_operate.Messages.OPEN: f''' +Подписку: #{name_field} + +Описание и состав подписки: #{desc_field} + +Статус: #{status_field} + +Адрес доставки: #{address_field} + +Время создания: #{create_datetime_field} +''', + mod_table_operate.CreateMessage(bd_table.TableFieldDestiny.NAME): ''' +Создание подписки. Шаг №1 + +Введите название модуля: +''', + mod_table_operate.CreateMessage(bd_table.TableFieldDestiny.SUBSCRIBE_TYPE): ''' +Создание подписки. Шаг №2 + +Введите тип подписки: +''', + mod_table_operate.CreateMessage(bd_table.TableFieldDestiny.ITEM_ID): ''' +Создание подписки. Шаг №3 + +Номер элемента, на который нужно подписаться (-1, если элемента нет): +''', + mod_table_operate.Messages.SUCCESS_CREATE: '''✅ Подписку успешно добавлен!''', + mod_table_operate.Messages.START_EDIT: ''' +Пожалуйста, выберите действие: +''', + mod_table_operate.Messages.SELECT_TO_EDIT: ''' +Выберите подписку, который вы хотите отредактировать. +''', + mod_table_operate.EditMessage(bd_table.TableFieldDestiny.NAME): f''' +Текущее название подписки: +#{name_field} + +Введите новое название подписки: +''', + mod_table_operate.EditMessage(bd_table.TableFieldDestiny.SUBSCRIBE_TYPE): f''' +Текущее описание подписки: +#{desc_field} + +Введите новый тип подписки: +''', + mod_table_operate.EditMessage(bd_table.TableFieldDestiny.ITEM_ID): f''' +Текущий адрес подписки: +#{desc_field} + +Введите новый номер элемента: +''', + mod_table_operate.EditMessage(bd_table.TableFieldDestiny.ACCESS): f''' +Текущий доступ к подпискуу: +#{access_field} + +{user_access.user_access_readme} + +Введите новую строку доступа: +''', + mod_table_operate.Messages.SUCCESS_EDIT: '''✅ Подписку успешно отредактировано!''', + mod_table_operate.Messages.SELECT_TO_DELETE: ''' +Выберите подписку, которую вы хотите удалить. +''', + mod_table_operate.Messages.SUCCESS_DELETE: '''✅ Подписка успешно удалёна!''', +} + +messages_subs_type_status = { + mod_table_operate.EnumMessageForView(bot_subscribes.SubscribeType.ADD): f'''Добавление элемента''', + mod_table_operate.EnumMessageForView(bot_subscribes.SubscribeType.ANY_ITEM_DEL): f'''Удаление элемента''', + mod_table_operate.EnumMessageForView(bot_subscribes.SubscribeType.ANY_ITEM_EDIT): f'''Редактирование элемента''', + mod_table_operate.EnumMessageForView(bot_subscribes.SubscribeType.ITEM_DEL): f'''Удаление конкретного элемента''', + mod_table_operate.EnumMessageForView(bot_subscribes.SubscribeType.ITEM_EDIT): f'''Редактирование конкретного элемента''', +} + +messages.update(messages_subs_type_status) + +def GetCurItemsTemplate(a_Bot, a_TableName, a_UserIDFieldName, a_StatusFieldName): + def GetBDItems(a_Message, a_UserGroups, a_ParentID): + user_id = str(a_Message.from_user.id) + request = f'SELECT * FROM {a_TableName} WHERE {a_UserIDFieldName} = ? AND {a_StatusFieldName} != ?' + return a_Bot.SQLRequest(request, param = ([user_id, str(OrderStatus.FINISH)])) + return GetBDItems + +def GetBDItemsForUserTemplate(a_Bot, a_TableName, a_UserIDFieldName): + def GetBDItems(a_Message, a_UserGroups, a_ParentID): + user_id = str(a_Message.from_user.id) + return bd_item.GetBDItemsTemplate(a_Bot, a_TableName, a_UserIDFieldName)(user_id) + return GetBDItems + +class DBItemForUserSelectSource(bd_item_select.DBItemSelectSource): + def __init__(self, a_Bot, a_TableName, a_ParentIDFieldName, a_PrevPrefix, a_ButtonName): + super().__init__(a_Bot, a_TableName, a_ParentIDFieldName, a_PrevPrefix, a_ButtonName) + + def GetItemsFunc(self): + return GetBDItemsForUserTemplate(self.m_Bot, self.m_TableName, self.m_ParentIDFieldName) + + def IsFirst(self): + return True + +class ModuleSubscribe(mod_table_operate.TableOperateModule): + def __init__(self, a_Table, a_Messages, a_Buttons, a_ParentModName, a_ChildModName, a_InitAccess, a_ChildModuleNameList, a_EditModuleNameList, a_Bot, a_ModuleAgregator, a_BotMessages, a_BotButtons, a_Log): + super().__init__(table, a_Messages, a_Buttons, a_ParentModName, a_ChildModName, a_InitAccess, a_ChildModuleNameList, a_EditModuleNameList, a_Bot, a_ModuleAgregator, a_BotMessages, a_BotButtons, a_Log) + + def SelectSourceTemplate(self, a_PrevPrefix, a_ButtonName): + parent_id_field = self.m_Table.GetFieldNameByDestiny(bd_table.TableFieldDestiny.PARENT_ID) + return DBItemForUserSelectSource(self.m_Bot, self.m_Table.GetName(), parent_id_field, a_PrevPrefix, a_ButtonName) + + def AddBDItemFunc(self, a_ItemData, a_UserID): + parent_id_field = self.m_Table.GetFieldNameByDestiny(bd_table.TableFieldDestiny.PARENT_ID) + a_ItemData[parent_id_field] = a_UserID + return super().AddBDItemFunc(a_ItemData, a_UserID) + + def RegisterHandlers(self): + super().RegisterHandlers() + GetButtonNameAndKeyValueAndAccess = self.m_GetButtonNameAndKeyValueAndAccessFunc + GetAccess = self.m_GetAccessFunc + + defaul_keyboard_func = self.m_GetStartKeyboardButtonsFunc + + parent_table_name = None + parent_key_name = None + if self.m_ParentModName: + parent_mod = self.GetModule(self.m_ParentModName) + parent_table_name = parent_mod.m_Table.GetName() + parent_key_name = parent_mod.m_Table.GetFieldNameByDestiny(bd_table.TableFieldDestiny.KEY) + + # Добавление + a_ButtonName = self.GetButton(ButtonNames.ADD_SUBS) + if a_ButtonName: + a_Prefix = self.RegisterSelect(a_ButtonName, user_access.AccessMode.ADD, only_parent = True) + + check_func = bd_item.GetCheckForTextFunc(a_ButtonName) + if a_Prefix: + check_func = bd_item.GetCheckForPrefixFunc(a_Prefix) + + bd_item_add.AddBDItem1RegisterHandlers(self.m_Bot,\ + check_func,\ + FSMAddSubsType,\ + self.m_AddBDItemFunc,\ + self.ShowMessageTemplate(self.GetMessage(mod_table_operate.CreateMessage(bd_table.TableFieldDestiny.NAME))),\ + self.ShowMessageTemplate(self.GetMessage(mod_table_operate.Messages.SUCCESS_CREATE)),\ + a_Prefix,\ + parent_table_name,\ + parent_key_name,\ + name_field,\ + GetButtonNameAndKeyValueAndAccess,\ + GetAccess,\ + defaul_keyboard_func,\ + bd_item.FieldType.text,\ + access_mode = user_access.AccessMode.ADD\ + ) + +class ModuleUserSubscribe(ModuleOrders): + def __init__(self, a_ParentModName, a_ChildModName, a_ChildModuleNameList, a_EditModuleNameList, a_Bot, a_ModuleAgregator, a_BotMessages, a_BotButtons, a_Log): + super().__init__(table, messages, button_names, a_ParentModName, a_ChildModName, init_access, a_ChildModuleNameList, a_EditModuleNameList, a_Bot, a_ModuleAgregator, a_BotMessages, a_BotButtons, a_Log) + + def GetName(self): + return module_name diff --git a/bot_sys/bd_table.py b/bot_sys/bd_table.py index d9c6bb9..7820abf 100644 --- a/bot_sys/bd_table.py +++ b/bot_sys/bd_table.py @@ -32,6 +32,8 @@ class TableFieldDestiny(Enum): CREATE_DATE = auto() PARENT_ID = auto() STATUS = auto() + SUBSCRIBE_TYPE = auto() + ITEM_ID = auto() ADDRESS = auto() OTHER = auto() diff --git a/bot_sys/bot_subscribes.py b/bot_sys/bot_subscribes.py index 523fe94..3fe19c5 100644 --- a/bot_sys/bot_subscribes.py +++ b/bot_sys/bot_subscribes.py @@ -14,7 +14,7 @@ class SubscribeType(Enum): ITEM_DEL = auto() ITEM_EDIT = auto() -class BotSubscribes +class BotSubscribes: def __init__(self): self.Clear() @@ -24,15 +24,16 @@ class BotSubscribes def Clear(self): self.m_Subscribes = {} - def CheckSubscribe(self, a_UserID, a_ModuleName, a_Type, a_ItemID = -1): + def GetUserIDs(self, a_ModuleName, a_Type, a_ItemID = -1): s = self.GetSubscribes() - su = s.get(a_UserID, None) - if su: + ids = set() + for user_id, su in s.items(): sub_um = su.get(a_ModuleName, None) if sub_um: - t = su.get(a_ItemID, None) - return t - return None + t = sub_um.get(a_ItemID, None) + if t == a_Type: + ids.add(user_id) + return ids def AddSubscribe(self, a_UserID, a_ModuleName, a_Type, a_ItemID = -1): s = self.GetSubscribes() @@ -41,3 +42,32 @@ class BotSubscribes if not s[a_UserID].get(a_ModuleName, None): s[a_UserID][a_ModuleName] = {} s[a_UserID][a_ModuleName][a_ItemID] = a_Type + +def Test(): + a = set() + a.add(1) + a.add(2) + a.add(1) + assert 1 in a + assert not 3 in a + + user_id_1 = '123' + user_id_2 = '34234' + user_id_3 = '4234' + mod_1 = 'proj' + mod_2 = 'backup' + s = BotSubscribes() + s.AddSubscribe(user_id_1, mod_1, SubscribeType.ADD) + s.AddSubscribe(user_id_2, mod_2, SubscribeType.ITEM_DEL) + + assert len(s.GetUserIDs(mod_1, SubscribeType.ADD)) == 1 + assert len(s.GetUserIDs(mod_1, SubscribeType.ANY_ITEM_DEL)) == 0 + assert user_id_1 in s.GetUserIDs(mod_1, SubscribeType.ADD) + assert not user_id_2 in s.GetUserIDs(mod_1, SubscribeType.ADD) + assert not user_id_3 in s.GetUserIDs(mod_1, SubscribeType.ADD) + + assert len(s.GetUserIDs(mod_2, SubscribeType.ITEM_DEL)) == 1 + assert len(s.GetUserIDs(mod_2, SubscribeType.ADD)) == 0 + assert user_id_2 in s.GetUserIDs(mod_2, SubscribeType.ITEM_DEL) + assert not user_id_1 in s.GetUserIDs(mod_2, SubscribeType.ITEM_DEL) + assert not user_id_3 in s.GetUserIDs(mod_2, SubscribeType.ITEM_DEL) diff --git a/main.py b/main.py index e6de562..82bce79 100644 --- a/main.py +++ b/main.py @@ -7,6 +7,7 @@ from bot_sys import config, log, aiogram_bot, bot_messages, bd_table, user_acces from bot_modules import mod_agregator, start, profile, backup, users_groups_agregator, access, projects, tasks, needs, comments from bot_modules import languages, messages, buttons, users, groups, user_in_groups from bot_modules import orders, all_orders +from bot_sys import bot_subscribes log_start_message = 'Бот успешно запущен!' @@ -135,7 +136,7 @@ for m in modules: m.RegisterHandlers() # Юнит тесты модулей и файлов -test_mods = [user_access, bd_table] +test_mods = [user_access, bd_table, bot_subscribes] for m in test_mods: m.Test()