From 1fe3092b78742848004817b24aa6b7784ce6096e Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Sat, 24 Feb 2024 17:45:31 +0300 Subject: [PATCH 01/71] =?UTF-8?q?refactor:=20=D0=9E=D1=82=D0=BA=D0=BB?= =?UTF-8?q?=D1=8E=D1=87=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=81=D0=BE=D1=80=D1=82?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B8=20=D0=B4=D0=BB=D1=8F=20bot?= =?UTF-8?q?=5Fdata?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utilities/pickle/pickle_to_json.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utilities/pickle/pickle_to_json.py b/src/utilities/pickle/pickle_to_json.py index dd86e07..c471f57 100644 --- a/src/utilities/pickle/pickle_to_json.py +++ b/src/utilities/pickle/pickle_to_json.py @@ -96,6 +96,8 @@ def default(self, obj): for key_3, item_3 in objects[0][key][key_2].items(): obj_to_serialize[key_2][key_3[0]] = item_3 else: + if key == 'bot_data': + sort_keys = False obj_to_serialize = item try: From c953c8aee449eb16f6a7fcaaff1aa7a254d572ab Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Sat, 24 Feb 2024 17:46:02 +0300 Subject: [PATCH 02/71] =?UTF-8?q?feat:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B7=D0=BD=D0=B0=D1=87=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D0=B2=20TicketStatusEnum?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/db/models.py b/src/db/models.py index d377537..b40a5ef 100644 --- a/src/db/models.py +++ b/src/db/models.py @@ -38,6 +38,7 @@ class Child(BaseModel): class TicketStatusEnum(enum.Enum): + QUERIED = 'queried' # Запрошен PAID = 'paid' # Оплачен APPROVED = 'approved' # Подтвержден REJECTED = 'rejected' # Отклонен From bf6fa19e68bd61515c493f103843ad54ce28a464 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Sat, 24 Feb 2024 17:46:27 +0300 Subject: [PATCH 03/71] =?UTF-8?q?feat:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B7=D0=BD=D0=B0=D1=87=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D0=B2=20TypeEvent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/models.py b/src/db/models.py index b40a5ef..398032a 100644 --- a/src/db/models.py +++ b/src/db/models.py @@ -78,7 +78,7 @@ class TypeEvent(BaseModel): id: Mapped[int] = mapped_column(primary_key=True) name: Mapped[str] name_alias: Mapped[str] - price_for_gift: Mapped[int] + base_price_gift: Mapped[int] notes: Mapped[Optional[str]] From 73992142b7e91f2d157a5c3af989cbaf30172bca Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Sat, 24 Feb 2024 17:46:46 +0300 Subject: [PATCH 04/71] =?UTF-8?q?feat:=20=D0=A1=D0=BE=D0=B7=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20PriceTypeEnum?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/models.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/db/models.py b/src/db/models.py index 398032a..b6ef212 100644 --- a/src/db/models.py +++ b/src/db/models.py @@ -83,6 +83,13 @@ class TypeEvent(BaseModel): notes: Mapped[Optional[str]] +class PriceTypeEnum(enum.Enum): + NONE = None + BASE_PRICE = 'Базовая стоимость' + OPTIONS = 'Опции' + INDIVIDUAL = 'Индивидуальная' + + class TheaterEvent(BaseModel): __tablename__ = 'theater_events' From d06f0bc2cdf7f43db0f75dbffd057532df67bfaa Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Sat, 24 Feb 2024 17:47:00 +0300 Subject: [PATCH 05/71] =?UTF-8?q?feat:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B7=D0=BD=D0=B0=D1=87=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D0=B2=20TheaterEvent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/models.py b/src/db/models.py index b6ef212..5fdcb5f 100644 --- a/src/db/models.py +++ b/src/db/models.py @@ -99,12 +99,12 @@ class TheaterEvent(BaseModel): min_age_child: Mapped[int] max_age_child: Mapped[int] show_emoji: Mapped[str] - # full_name: Mapped[bool] flag_active_repertoire: Mapped[bool] flag_active_bd: Mapped[bool] max_num_child_bd: Mapped[int] max_num_adult_bd: Mapped[int] flag_indiv_cost: Mapped[bool] = mapped_column(default=False) + price_type: Mapped[PriceTypeEnum] class ScheduleEvent(BaseModelTimed): From b0c6501117747de2cb4df08a646f1af31d68a10f Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Sat, 24 Feb 2024 17:47:40 +0300 Subject: [PATCH 06/71] =?UTF-8?q?refactor:=20=D0=A3=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D0=BD=D1=8B=20=D0=BB=D0=B8=D1=88=D0=BD=D0=B8=D0=B5=20=D1=81?= =?UTF-8?q?=D1=82=D1=80=D0=BE=D1=87=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index f8134c4..a7fcfcc 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -76,8 +76,6 @@ services: volumes: db: -# data: -# logs: pgadmin: networks: From 448d2a1479b010bce559044b6dc579f2c44ab532 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Sun, 25 Feb 2024 15:36:55 +0300 Subject: [PATCH 07/71] =?UTF-8?q?refactor:=20=D0=92=D1=8B=D0=BD=D0=B5?= =?UTF-8?q?=D1=81=D0=B5=D0=BD=D1=8B=20enum=20types=20=D0=B2=20=D0=BE=D1=82?= =?UTF-8?q?=D0=B4=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20=D0=BC=D0=BE=D0=B4?= =?UTF-8?q?=D1=83=D0=BB=D1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/__init__.py | 15 +++++---------- src/db/enum_types.py | 45 ++++++++++++++++++++++++++++++++++++++++++++ src/db/models.py | 25 ++++-------------------- 3 files changed, 54 insertions(+), 31 deletions(-) create mode 100644 src/db/enum_types.py diff --git a/src/db/__init__.py b/src/db/__init__.py index 8bbf1f7..efb302d 100644 --- a/src/db/__init__.py +++ b/src/db/__init__.py @@ -1,15 +1,10 @@ -from .base import BaseModel +from .base import BaseModel, BaseModelTimed from .models import ( User, Child, + Ticket, + TypeEvent, TheaterEvent, - Ticket + ScheduleEvent, ) - -__all__ = [ - "BaseModel", - "User", - "Child", - "TheaterEvent", - "Ticket", -] +from .enum_types import TicketStatusEnum, TicketPriceTypeEnum, PriceTypeEnum diff --git a/src/db/enum_types.py b/src/db/enum_types.py new file mode 100644 index 0000000..b630c17 --- /dev/null +++ b/src/db/enum_types.py @@ -0,0 +1,45 @@ +import enum + +import sqlalchemy as sa + +from db import BaseModel + + +class TicketStatus(enum.Enum): + CREATED = 'Создан' + PAID = 'Оплачен' + APPROVED = 'Подтвержден' + REJECTED = 'Отклонен' + REFUNDED = 'Возвращен' + TRANSFERRED = 'Передан' + POSTPONED = 'Перенесен' + + +class PriceType(enum.Enum): + NONE = None + BASE_PRICE = 'Базовая стоимость' + OPTIONS = 'Опции' + INDIVIDUAL = 'Индивидуальная' + + +class TicketPriceType(enum.Enum): + NONE = None + weekday = 'будни' + weekend = 'выходные' + + +TicketStatusEnum = sa.Enum( + TicketStatus, + name="TicketStatusEnum", + metadata=BaseModel.metadata +) +PriceTypeEnum = sa.Enum( + PriceType, + name="PriceTypeEnum", + metadata=BaseModel.metadata +) +TicketPriceTypeEnum = sa.Enum( + TicketPriceType, + name="TicketPriceTypeEnum", + metadata=BaseModel.metadata +) diff --git a/src/db/models.py b/src/db/models.py index 5fdcb5f..03616a4 100644 --- a/src/db/models.py +++ b/src/db/models.py @@ -1,11 +1,11 @@ from datetime import datetime, date -import enum from typing import Optional, List from sqlalchemy import ForeignKey, BigInteger from sqlalchemy.orm import Mapped, mapped_column, relationship -from .base import BaseModel, BaseModelTimed +from db import BaseModel, BaseModelTimed +from db.enum_types import TicketStatus, TicketPriceType, PriceType class User(BaseModelTimed): @@ -37,16 +37,6 @@ class Child(BaseModel): users: Mapped['User'] = relationship(back_populates='children') -class TicketStatusEnum(enum.Enum): - QUERIED = 'queried' # Запрошен - PAID = 'paid' # Оплачен - APPROVED = 'approved' # Подтвержден - REJECTED = 'rejected' # Отклонен - REFUNDED = 'refunded' # Возвращен - TRANSFERRED = 'transferred' # Передан - POSTPONED = 'postponed' # Перенесен - - class Ticket(BaseModelTimed): __tablename__ = 'tickets' @@ -55,7 +45,7 @@ class Ticket(BaseModelTimed): base_ticket_id: Mapped[int] price: Mapped[int] exclude: Mapped[bool] - status: Mapped[TicketStatusEnum] + status: Mapped[TicketStatus] user_id: Mapped[int] = mapped_column( ForeignKey('users.id', ondelete='CASCADE') @@ -83,13 +73,6 @@ class TypeEvent(BaseModel): notes: Mapped[Optional[str]] -class PriceTypeEnum(enum.Enum): - NONE = None - BASE_PRICE = 'Базовая стоимость' - OPTIONS = 'Опции' - INDIVIDUAL = 'Индивидуальная' - - class TheaterEvent(BaseModel): __tablename__ = 'theater_events' @@ -104,7 +87,7 @@ class TheaterEvent(BaseModel): max_num_child_bd: Mapped[int] max_num_adult_bd: Mapped[int] flag_indiv_cost: Mapped[bool] = mapped_column(default=False) - price_type: Mapped[PriceTypeEnum] + price_type: Mapped[PriceType] = mapped_column(default=PriceType.NONE) class ScheduleEvent(BaseModelTimed): From eaef6fae1e451cfc7a8646c1f5267268a4c9aaa6 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Sun, 25 Feb 2024 15:40:33 +0300 Subject: [PATCH 08/71] =?UTF-8?q?fix:=20=D0=98=D1=81=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BD=D0=B0=D0=B7=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D0=BF=D0=BE=D0=BB=D0=B5=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/db/models.py b/src/db/models.py index 03616a4..8c22973 100644 --- a/src/db/models.py +++ b/src/db/models.py @@ -32,7 +32,7 @@ class Child(BaseModel): birthdate: Mapped[date] user_id: Mapped[int] = mapped_column( - ForeignKey('users.id', ondelete='CASCADE') + ForeignKey('users.chat_id', ondelete='CASCADE') ) users: Mapped['User'] = relationship(back_populates='children') @@ -48,7 +48,7 @@ class Ticket(BaseModelTimed): status: Mapped[TicketStatus] user_id: Mapped[int] = mapped_column( - ForeignKey('users.id', ondelete='CASCADE') + ForeignKey('users.chat_id', ondelete='CASCADE') ) users: Mapped['User'] = relationship( back_populates='tickets') From 6013c5f54b7b2e6e35833485a31aba95a1974e91 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Sun, 25 Feb 2024 15:42:12 +0300 Subject: [PATCH 09/71] =?UTF-8?q?feat:=20=D0=94=D0=BE=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D0=BA=D0=B0=20=D0=BE=D1=81=D0=BD=D0=BE=D0=B2=D0=BD?= =?UTF-8?q?=D1=8B=D1=85=20=D0=BF=D0=BE=D0=BB=D0=B5=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Перестановка местами для более согласованного чтения с миграцией - Добавление полей в schedule_event - Доработка опциональных полей и значений по умолчанию --- src/db/models.py | 51 ++++++++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/src/db/models.py b/src/db/models.py index 8c22973..f142bf0 100644 --- a/src/db/models.py +++ b/src/db/models.py @@ -17,7 +17,7 @@ class User(BaseModelTimed): callback_name: Mapped[str] callback_phone: Mapped[Optional[str]] - username: Mapped[str] + username: Mapped[Optional[str]] children: Mapped[List['Child']] = relationship(back_populates='users') tickets: Mapped[List['Ticket']] = relationship(back_populates='users') @@ -27,9 +27,9 @@ class Child(BaseModel): __tablename__ = 'children' id: Mapped[int] = mapped_column(primary_key=True) - name: Mapped[Optional[str]] + name: Mapped[str] age: Mapped[float] - birthdate: Mapped[date] + birthdate: Mapped[Optional[date]] user_id: Mapped[int] = mapped_column( ForeignKey('users.chat_id', ondelete='CASCADE') @@ -44,23 +44,23 @@ class Ticket(BaseModelTimed): base_ticket_id: Mapped[int] price: Mapped[int] - exclude: Mapped[bool] + exclude: Mapped[bool] = mapped_column(default=False) status: Mapped[TicketStatus] + child_id: Mapped[List['Child']] = mapped_column(ForeignKey('children.id')) user_id: Mapped[int] = mapped_column( ForeignKey('users.chat_id', ondelete='CASCADE') ) - users: Mapped['User'] = relationship( - back_populates='tickets') - theater_event_id: Mapped[int] = mapped_column( ForeignKey('theater_events.id', ondelete='CASCADE') ) + schedule_event_id: Mapped[int] + notes: Mapped[Optional[str]] + + users: Mapped['User'] = relationship(back_populates='tickets') theater_events: Mapped['TheaterEvent'] = relationship( back_populates='tickets') - notes: Mapped[Optional[str]] - class TypeEvent(BaseModel): __tablename__ = 'type_events' @@ -68,8 +68,7 @@ class TypeEvent(BaseModel): id: Mapped[int] = mapped_column(primary_key=True) name: Mapped[str] name_alias: Mapped[str] - base_price_gift: Mapped[int] - + base_price_gift: Mapped[Optional[int]] notes: Mapped[Optional[str]] @@ -80,12 +79,12 @@ class TheaterEvent(BaseModel): name: Mapped[str] flag_premier: Mapped[bool] = mapped_column(default=False) min_age_child: Mapped[int] - max_age_child: Mapped[int] - show_emoji: Mapped[str] - flag_active_repertoire: Mapped[bool] - flag_active_bd: Mapped[bool] - max_num_child_bd: Mapped[int] - max_num_adult_bd: Mapped[int] + max_age_child: Mapped[Optional[int]] + show_emoji: Mapped[Optional[str]] + flag_active_repertoire: Mapped[bool] = mapped_column(default=False) + flag_active_bd: Mapped[bool] = mapped_column(default=False) + max_num_child_bd: Mapped[int] = mapped_column(default=8) + max_num_adult_bd: Mapped[int] = mapped_column(default=10) flag_indiv_cost: Mapped[bool] = mapped_column(default=False) price_type: Mapped[PriceType] = mapped_column(default=PriceType.NONE) @@ -103,7 +102,21 @@ class ScheduleEvent(BaseModelTimed): ForeignKey('theater_events.id', ondelete='CASCADE') ) flag_turn_in_bot: Mapped[bool] = mapped_column(default=False) - date_event: Mapped[datetime] + datetime_event: Mapped[datetime] + + qty_child: Mapped[int] + qty_child_free_seat: Mapped[int] + qty_child_nonconfirm_seat: Mapped[int] + qty_adult: Mapped[int] + qty_adult_free_seat: Mapped[int] + qty_adult_nonconfirm_seat: Mapped[int] + + flag_gift: Mapped[bool] = mapped_column(default=False) + flag_christmas_tree: Mapped[bool] = mapped_column(default=False) + flag_santa: Mapped[bool] = mapped_column(default=False) + + ticket_price_type: Mapped[TicketPriceType] = mapped_column( + default=TicketPriceType.NONE) tickets: Mapped[List['Ticket']] = relationship( - back_populates='theater_events') + back_populates='schedule_events') From 549b4421e3e4b21237fc78fd8f2b2fdcaf25707d Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Sun, 25 Feb 2024 15:44:29 +0300 Subject: [PATCH 10/71] =?UTF-8?q?refactor:=20=D0=A0=D0=B5=D1=84=D0=B0?= =?UTF-8?q?=D0=BA=D1=82=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20=D0=BE=D1=81=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D0=BD=D1=8B=D1=85=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D1=87=D0=B8=D0=BA=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Добавление фильтров к нескольким командам Добавление conversation_handlers одним списком --- src/bot.py | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/bot.py b/src/bot.py index 1834bb9..dec1163 100644 --- a/src/bot.py +++ b/src/bot.py @@ -71,19 +71,30 @@ def bot(): application.add_handler(CallbackQueryHandler(main_hl.reject_birthday, pattern='^reject-birthday')) - application.add_handler(reserve_conv_hl) - application.add_handler(reserve_admin_conv_hl) - application.add_handler(list_wait_conv_hl) - application.add_handler(birthday_conv_hl) - application.add_handler(birthday_paid_conv_hl) - application.add_handler(afisha_conv_hl) + conversation_handlers = [ + reserve_conv_hl, + reserve_admin_conv_hl, + list_wait_conv_hl, + birthday_conv_hl, + birthday_paid_conv_hl, + afisha_conv_hl, + ] + application.add_handlers(conversation_handlers) - application.add_handler(CommandHandler('echo', echo)) application.add_handler(CommandHandler('reset', main_hl.reset)) - - application.add_handler(CommandHandler('print_ud', print_ud)) - application.add_handler(CommandHandler('clean_ud', clean_ud)) - application.add_handler(CommandHandler('clean_bd', clean_bd)) + application.add_handler(CommandHandler('echo', echo)) + application.add_handler(CommandHandler( + 'print_ud', + print_ud, + filters=filters.User(ADMIN_ID))) + application.add_handler(CommandHandler( + 'clean_ud', + clean_ud, + filters=filters.User(ADMIN_ID))) + application.add_handler(CommandHandler( + 'clean_bd', + clean_bd, + filters=filters.User(ADMIN_ID))) application.add_handler(CommandHandler( COMMAND_DICT['UP_T_DATA'][0], update_ticket_data, From 604399b7d900fe883029df1ed6590bf1354ab56f Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Sun, 25 Feb 2024 15:56:59 +0300 Subject: [PATCH 11/71] =?UTF-8?q?refactor:=20=D0=A0=D0=B5=D1=84=D0=B0?= =?UTF-8?q?=D0=BA=D1=82=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20=D0=BE=D1=81=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D0=BD=D1=8B=D1=85=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D1=87=D0=B8=D0=BA=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Объединение обработчиков для повышения читаемости --- src/bot.py | 78 +++++++++++++++++++----------------------------------- 1 file changed, 27 insertions(+), 51 deletions(-) diff --git a/src/bot.py b/src/bot.py index dec1163..64486c3 100644 --- a/src/bot.py +++ b/src/bot.py @@ -83,57 +83,33 @@ def bot(): application.add_handler(CommandHandler('reset', main_hl.reset)) application.add_handler(CommandHandler('echo', echo)) - application.add_handler(CommandHandler( - 'print_ud', - print_ud, - filters=filters.User(ADMIN_ID))) - application.add_handler(CommandHandler( - 'clean_ud', - clean_ud, - filters=filters.User(ADMIN_ID))) - application.add_handler(CommandHandler( - 'clean_bd', - clean_bd, - filters=filters.User(ADMIN_ID))) - application.add_handler(CommandHandler( - COMMAND_DICT['UP_T_DATA'][0], - update_ticket_data, - filters=filters.User(ADMIN_ID))) - application.add_handler(CommandHandler( - COMMAND_DICT['UP_S_DATA'][0], - update_show_data, - filters=filters.User(ADMIN_ID))) - application.add_handler(CommandHandler( - COMMAND_DICT['UP_BD_PRICE'][0], - update_bd_price, - filters=filters.User(ADMIN_ID))) - application.add_handler(CommandHandler( - COMMAND_DICT['UP_SPEC_PRICE'][0], - update_special_ticket_price, - filters=filters.User(ADMIN_ID))) - application.add_handler(CommandHandler( - COMMAND_DICT['LOG'][0], - send_log, - filters=filters.User(ADMIN_ID))) - application.add_handler(CommandHandler( - COMMAND_DICT['CB_TW'][0], - get_balance, - filters=filters.User(ADMIN_ID))) - application.add_handler(CommandHandler( - COMMAND_DICT['TOPIC_START'][0], - create_or_connect_topic, - filters=filters.User(ADMIN_ID))) - application.add_handler(CommandHandler( - COMMAND_DICT['TOPIC_DEL'][0], - del_topic, - filters=filters.User(ADMIN_ID))) - application.add_handler(CommandHandler( - COMMAND_DICT['ADM_INFO'][0], - update_admin_info, - filters=filters.User(ADMIN_ID))) - - application.add_handler(CommandHandler('rcl', - request_contact_location)) + + filter_admin = filters.User(ADMIN_ID) + application.add_handlers([ + CommandHandler('clean_ud', clean_ud, filter_admin), + CommandHandler('print_ud', print_ud, filter_admin), + CommandHandler('clean_bd', clean_bd, filter_admin), + CommandHandler(COMMAND_DICT['LOG'][0], send_log, filter_admin), + CommandHandler(COMMAND_DICT['CB_TW'][0], get_balance, filter_admin), + CommandHandler(COMMAND_DICT['TOPIC_DEL'][0], del_topic, filter_admin), + CommandHandler(COMMAND_DICT['TOPIC_START'][0], create_or_connect_topic, + filter_admin), + CommandHandler(COMMAND_DICT['UP_T_DATA'][0], update_ticket_data, + filter_admin), + CommandHandler(COMMAND_DICT['UP_S_DATA'][0], update_show_data, + filter_admin), + CommandHandler(COMMAND_DICT['UP_BD_PRICE'][0], update_bd_price, + filter_admin), + CommandHandler(COMMAND_DICT['UP_SPEC_PRICE'][0], + update_special_ticket_price, + filter_admin), + CommandHandler(COMMAND_DICT['ADM_INFO'][0], + update_admin_info, + filter_admin), + ]) + + application.add_handler( + CommandHandler('rcl', request_contact_location)) application.add_handler(MessageHandler(filters.LOCATION, get_location)) application.add_handler(MessageHandler(filters.CONTACT, get_contact)) From 4d729c5d7478e889ee306c9b3c1395a7b814a1ca Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Sun, 25 Feb 2024 16:01:38 +0300 Subject: [PATCH 12/71] =?UTF-8?q?refactor:=20=D0=A0=D0=B5=D1=84=D0=B0?= =?UTF-8?q?=D0=BA=D1=82=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20=D0=BE=D1=81=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D0=BD=D1=8B=D1=85=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D1=87=D0=B8=D0=BA=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Объединение обработчиков для повышения читаемости --- src/bot.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/bot.py b/src/bot.py index 64486c3..d4026d1 100644 --- a/src/bot.py +++ b/src/bot.py @@ -59,17 +59,18 @@ def bot(): application.bot_data.setdefault('config', config) - application.add_handler(CommandHandler(COMMAND_DICT['START'][0], - main_hl.start)) - - application.add_handler(CallbackQueryHandler(main_hl.confirm_reserve, - pattern='^confirm-reserve')) - application.add_handler(CallbackQueryHandler(main_hl.reject_reserve, - pattern='^reject-reserve')) - application.add_handler(CallbackQueryHandler(main_hl.confirm_birthday, - pattern='^confirm-birthday')) - application.add_handler(CallbackQueryHandler(main_hl.reject_birthday, - pattern='^reject-birthday')) + application.add_handlers([ + CommandHandler(COMMAND_DICT['START'][0], main_hl.start), + CommandHandler('reset', main_hl.reset), + CommandHandler('echo', echo), + ]) + + application.add_handlers([ + CallbackQueryHandler(main_hl.confirm_reserve, '^confirm-reserve'), + CallbackQueryHandler(main_hl.reject_reserve, '^reject-reserve'), + CallbackQueryHandler(main_hl.confirm_birthday, '^confirm-birthday'), + CallbackQueryHandler(main_hl.reject_birthday, '^reject-birthday'), + ]) conversation_handlers = [ reserve_conv_hl, @@ -81,9 +82,6 @@ def bot(): ] application.add_handlers(conversation_handlers) - application.add_handler(CommandHandler('reset', main_hl.reset)) - application.add_handler(CommandHandler('echo', echo)) - filter_admin = filters.User(ADMIN_ID) application.add_handlers([ CommandHandler('clean_ud', clean_ud, filter_admin), From a8a857a506e24082b6af441d695f4fc5b4294f2e Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Sun, 25 Feb 2024 16:19:45 +0300 Subject: [PATCH 13/71] =?UTF-8?q?feat:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20user=5Fid=20=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D1=8F=20=D0=BA=D0=B0=D0=BA=20=D1=83=D0=BD=D0=B8=D0=BA=D0=B0?= =?UTF-8?q?=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20=D0=BA=D0=BB=D1=8E=D1=87=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20=D1=81=D0=B2=D1=8F=D0=B7=D1=8B=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chat_id при этом всё равно оставляем для отправки сообщении --- src/db/models.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/db/models.py b/src/db/models.py index f142bf0..6252f17 100644 --- a/src/db/models.py +++ b/src/db/models.py @@ -11,9 +11,10 @@ class User(BaseModelTimed): __tablename__ = 'users' - chat_id: Mapped[int] = mapped_column(BigInteger, + user_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=False) + chat_id: Mapped[int] callback_name: Mapped[str] callback_phone: Mapped[Optional[str]] @@ -32,7 +33,7 @@ class Child(BaseModel): birthdate: Mapped[Optional[date]] user_id: Mapped[int] = mapped_column( - ForeignKey('users.chat_id', ondelete='CASCADE') + ForeignKey('users.user_id', ondelete='CASCADE') ) users: Mapped['User'] = relationship(back_populates='children') @@ -49,7 +50,7 @@ class Ticket(BaseModelTimed): child_id: Mapped[List['Child']] = mapped_column(ForeignKey('children.id')) user_id: Mapped[int] = mapped_column( - ForeignKey('users.chat_id', ondelete='CASCADE') + ForeignKey('users.user_id', ondelete='CASCADE') ) theater_event_id: Mapped[int] = mapped_column( ForeignKey('theater_events.id', ondelete='CASCADE') From 96ca8ed247cbb6e8b1f979eeb5ae8569e1dbbbf1 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Sun, 25 Feb 2024 16:33:43 +0300 Subject: [PATCH 14/71] =?UTF-8?q?chore:=20=D0=92=D1=8B=D0=BA=D0=BB=D1=8E?= =?UTF-8?q?=D1=87=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BF=D1=80=D0=B5=D0=B4=D1=83?= =?UTF-8?q?=D0=BF=D1=80=D0=B5=D0=B6=D0=B4=D0=B5=D0=BD=D0=B8=D0=B9=20=D1=81?= =?UTF-8?q?=D0=B2=D1=8F=D0=B7=D0=B0=D0=BD=D0=BD=D1=8B=D0=B5=20=D1=81=20Cal?= =?UTF-8?q?lbackQueryHandler?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/conv_hl/__init__.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/conv_hl/__init__.py b/src/conv_hl/__init__.py index 60e1cd5..c946fba 100644 --- a/src/conv_hl/__init__.py +++ b/src/conv_hl/__init__.py @@ -1,3 +1,6 @@ +from warnings import filterwarnings + +from telegram.warnings import PTBUserWarning from telegram.ext import CallbackQueryHandler from handlers import main_hl, reserve_hl @@ -24,3 +27,7 @@ CallbackQueryHandler(reserve_hl.choice_option_of_reserve), ], } + +filterwarnings(action="ignore", + message=r".*CallbackQueryHandler", + category=PTBUserWarning) From bf0a47ffa65e58817bb207d1b8e51dac16b83a74 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Sun, 25 Feb 2024 16:34:26 +0300 Subject: [PATCH 15/71] =?UTF-8?q?feat:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D1=87=D0=B8=D0=BA=D0=BE=D0=B2=20=D0=B4=D0=BB=D1=8F?= =?UTF-8?q?=20=D0=BE=D1=82=D0=BA=D1=80=D1=8B=D1=82=D0=B8=D1=8F=20=D0=B8=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=BA=D1=80=D1=8B=D1=82=D0=B8=D1=8F=20=D1=81=D0=B5?= =?UTF-8?q?=D1=81=D1=81=D0=B8=D0=B9=20=D0=B1=D0=B0=D0=B7=D1=8B=20=D0=B4?= =?UTF-8?q?=D0=B0=D0=BD=D0=BD=D1=8B=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/bot.py | 4 +++- src/db/__init__.py | 11 +++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/bot.py b/src/bot.py index d4026d1..a1dd3b6 100644 --- a/src/bot.py +++ b/src/bot.py @@ -4,7 +4,7 @@ filters, ) -from db.pickle_persistence import pickle_persistence +from db import pickle_persistence, middleware_db_add_handlers from log.logging_conf import load_log_config from handlers import main_hl from handlers.error_hl import error_handler @@ -59,6 +59,8 @@ def bot(): application.bot_data.setdefault('config', config) + middleware_db_add_handlers(application, config) + application.add_handlers([ CommandHandler(COMMAND_DICT['START'][0], main_hl.start), CommandHandler('reset', main_hl.reset), diff --git a/src/db/__init__.py b/src/db/__init__.py index efb302d..8655aa0 100644 --- a/src/db/__init__.py +++ b/src/db/__init__.py @@ -1,10 +1,5 @@ from .base import BaseModel, BaseModelTimed -from .models import ( - User, - Child, - Ticket, - TypeEvent, - TheaterEvent, - ScheduleEvent, -) +from .database import middleware_db_add_handlers from .enum_types import TicketStatusEnum, TicketPriceTypeEnum, PriceTypeEnum +from .models import User, Child, Ticket, TypeEvent, TheaterEvent, ScheduleEvent +from .pickle_persistence import pickle_persistence From d49168b823aa2dc151b1b953358ce74736b54534 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Sun, 25 Feb 2024 22:26:33 +0300 Subject: [PATCH 16/71] =?UTF-8?q?fix:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20schedule=5Fevents=20=D0=B2=20T?= =?UTF-8?q?icket?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/models.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/db/models.py b/src/db/models.py index 6252f17..cd32b89 100644 --- a/src/db/models.py +++ b/src/db/models.py @@ -55,11 +55,13 @@ class Ticket(BaseModelTimed): theater_event_id: Mapped[int] = mapped_column( ForeignKey('theater_events.id', ondelete='CASCADE') ) - schedule_event_id: Mapped[int] + schedule_event_id: Mapped[int] = mapped_column( + ForeignKey('schedule_events.id', ondelete='CASCADE') + ) notes: Mapped[Optional[str]] users: Mapped['User'] = relationship(back_populates='tickets') - theater_events: Mapped['TheaterEvent'] = relationship( + schedule_events: Mapped['ScheduleEvent'] = relationship( back_populates='tickets') From 4af308922c5451a9ef101c3dcdf521084419786e Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Sun, 25 Feb 2024 22:28:22 +0300 Subject: [PATCH 17/71] =?UTF-8?q?feat:=20=D0=A1=D0=BE=D0=B7=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=20=D0=BA=D0=BB=D0=B0=D1=81=D1=81=20AsyncORM=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20=D1=81=20?= =?UTF-8?q?=D0=B1=D0=B0=D0=B7=D0=BE=D0=B9=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B?= =?UTF-8?q?=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Добавлены create_user, get_user --- src/db/__init__.py | 1 + src/db/db_postgres.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 src/db/db_postgres.py diff --git a/src/db/__init__.py b/src/db/__init__.py index 8655aa0..f3f2701 100644 --- a/src/db/__init__.py +++ b/src/db/__init__.py @@ -3,3 +3,4 @@ from .enum_types import TicketStatusEnum, TicketPriceTypeEnum, PriceTypeEnum from .models import User, Child, Ticket, TypeEvent, TheaterEvent, ScheduleEvent from .pickle_persistence import pickle_persistence +from .db_postgres import AsyncORM diff --git a/src/db/db_postgres.py b/src/db/db_postgres.py new file mode 100644 index 0000000..2b5ee64 --- /dev/null +++ b/src/db/db_postgres.py @@ -0,0 +1,33 @@ +from sqlalchemy import select, exists, insert, update +from sqlalchemy.ext.asyncio import AsyncSession +from telegram import Update + +from db import User + + +class AsyncORM: + @staticmethod + async def create_user(update_tg: Update, session: AsyncSession): + stmt = insert(User).values( + user_id=update_tg.effective_user.id, + chat_id=update_tg.effective_chat.id, + callback_name=update_tg.effective_user.full_name, + username=update_tg.effective_user.username, + ) + result = await session.execute(stmt.returning(User.user_id)) + await session.commit() + return result.scalar() + + @staticmethod + async def get_user(update_tg: Update, session: AsyncSession): + exists_criteria = ( + exists().where(User.user_id == update_tg.effective_user.id)) + query = select(User).where(exists_criteria) + result = await session.execute(query) + user = result.all() + + if user: + return user + else: + return None + From e459f6af4c3e0929de57a81ac6f9a02ad39fc5a6 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Sun, 25 Feb 2024 22:29:51 +0300 Subject: [PATCH 18/71] =?UTF-8?q?refactor:=20=D0=9E=D0=BF=D1=82=D0=B8?= =?UTF-8?q?=D0=BC=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20=D0=B8=D0=BC=D0=BF?= =?UTF-8?q?=D0=BE=D1=80=D1=82=D0=BE=D0=B2,=20=D0=B4=D0=BB=D1=8F=20=D0=BF?= =?UTF-8?q?=D0=BE=D0=B2=D1=8B=D1=88=D0=B5=D0=BD=D0=B8=D1=8F=20=D1=87=D0=B8?= =?UTF-8?q?=D1=82=D0=B0=D0=B5=D0=BC=D0=BE=D1=81=D1=82=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/handlers/main_hl.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/handlers/main_hl.py b/src/handlers/main_hl.py index c3425ae..c6bef95 100644 --- a/src/handlers/main_hl.py +++ b/src/handlers/main_hl.py @@ -7,13 +7,10 @@ from handlers.sub_hl import write_old_seat_info, remove_inline_button from settings.settings import ( - COMMAND_DICT, - ADMIN_GROUP, - FEEDBACK_THREAD_ID_GROUP_ADMIN) + COMMAND_DICT, ADMIN_GROUP, FEEDBACK_THREAD_ID_GROUP_ADMIN +) from api.googlesheets import ( - get_quality_of_seats, - write_data_for_reserve, - set_approve_order + get_quality_of_seats, write_data_for_reserve, set_approve_order ) from utilities.utl_func import ( is_admin, get_back_context, clean_context, From 1fe1af782783e3e881c73229b796ce6d8654564d Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Sun, 25 Feb 2024 22:35:26 +0300 Subject: [PATCH 19/71] =?UTF-8?q?feat:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80?= =?UTF-8?q?=D0=BA=D0=B0=20=D1=81=D1=83=D1=89=D0=B5=D1=81=D1=82=D0=B2=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8F=20=D0=B2=20=D0=B1?= =?UTF-8?q?=D0=B0=D0=B7=D0=B5=20=D0=B8=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B2=20=D0=B1=D0=B0=D0=B7?= =?UTF-8?q?=D1=83=20=D0=BF=D1=80=D0=B8=20=D1=81=D1=82=D0=B0=D1=80=D1=82?= =?UTF-8?q?=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/handlers/main_hl.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/handlers/main_hl.py b/src/handlers/main_hl.py index c6bef95..7ba6ea5 100644 --- a/src/handlers/main_hl.py +++ b/src/handlers/main_hl.py @@ -16,6 +16,7 @@ is_admin, get_back_context, clean_context, clean_context_on_end_handler, utilites_logger ) +from db import AsyncORM main_handlers_logger = logging.getLogger('bot.main_handlers') @@ -25,6 +26,13 @@ async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): Приветственная команда при первом запуске бота, при перезапуске бота или при использовании команды start """ + if not await AsyncORM.get_user(update, context.session): + res = await AsyncORM.create_user(update, context.session) + if res: + main_handlers_logger.info( + f'Пользователь {res} начал общение с ботом') + else: + main_handlers_logger.info('Пользователь уже в есть в базе') clean_context(context) context.user_data['user'] = update.effective_user From d91aaf08b000b2932eca6da90572ac907bd5e0d3 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Sun, 25 Feb 2024 23:37:02 +0300 Subject: [PATCH 20/71] =?UTF-8?q?feat:=20=D0=A3=D1=81=D1=82=D0=B0=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D0=BA=D0=B0=20ParseMode.HTML=20=D0=BF=D0=BE=20?= =?UTF-8?q?=D1=83=D0=BC=D0=BE=D0=BB=D1=87=D0=B0=D0=BD=D0=B8=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/bot.py | 4 +++- src/handlers/error_hl.py | 1 - src/handlers/list_wait_hl.py | 2 -- src/handlers/main_hl.py | 17 +---------------- src/handlers/reserve_admin_hl.py | 2 -- src/handlers/reserve_hl.py | 22 ---------------------- src/utilities/utl_func.py | 6 ------ 7 files changed, 4 insertions(+), 50 deletions(-) diff --git a/src/bot.py b/src/bot.py index a1dd3b6..0a694aa 100644 --- a/src/bot.py +++ b/src/bot.py @@ -1,7 +1,8 @@ +from telegram.constants import ParseMode from telegram.ext import ( Application, CommandHandler, CallbackQueryHandler, MessageHandler, - filters, + filters, Defaults, ) from db import pickle_persistence, middleware_db_add_handlers @@ -53,6 +54,7 @@ def bot(): .token(config.bot.token.get_secret_value()) .persistence(pickle_persistence) .post_init(post_init) + .defaults(Defaults(parse_mode=ParseMode.HTML)) .build() ) diff --git a/src/handlers/error_hl.py b/src/handlers/error_hl.py index a2f6a04..483ef32 100644 --- a/src/handlers/error_hl.py +++ b/src/handlers/error_hl.py @@ -43,7 +43,6 @@ async def error_handler(update: Update, await context.bot.send_message( chat_id=CHAT_ID_MIKIEREMIKI, text=message, - parse_mode=ParseMode.HTML ) message = f"
{html.escape(tb_string)}
\n\n" diff --git a/src/handlers/list_wait_hl.py b/src/handlers/list_wait_hl.py index 9316773..ca730ec 100644 --- a/src/handlers/list_wait_hl.py +++ b/src/handlers/list_wait_hl.py @@ -46,13 +46,11 @@ async def send_clients_wait_data( await query.delete_message() await update.effective_chat.send_message( text=text, - parse_mode=ParseMode.HTML, message_thread_id=update.effective_message.message_thread_id ) else: await query.edit_message_text( text=text, - parse_mode=ParseMode.HTML ) state = ConversationHandler.END context.user_data['STATE'] = state diff --git a/src/handlers/main_hl.py b/src/handlers/main_hl.py index 7ba6ea5..52fcab0 100644 --- a/src/handlers/main_hl.py +++ b/src/handlers/main_hl.py @@ -54,7 +54,6 @@ async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): f'(инструкция)\n' f'/{COMMAND_DICT['BD_ORDER'][0]} - оформить заявку на проведение дня рождения ' f'(инструкция)', - parse_mode=ParseMode.HTML, reply_markup=ReplyKeyboardRemove() ) @@ -155,7 +154,6 @@ async def confirm_reserve(update: Update, context: ContextTypes.DEFAULT_TYPE): await context.bot.send_message( text=text, chat_id=chat_id, - parse_mode=ParseMode.HTML ) # TODO Добавить галочку подтверждения в клиентскую базу @@ -303,7 +301,6 @@ async def confirm_birthday(update: Update, context: ContextTypes.DEFAULT_TYPE): await context.bot.send_message( text=text, chat_id=chat_id, - parse_mode=ParseMode.HTML ) @@ -358,7 +355,6 @@ async def reject_birthday(update: Update, context: ContextTypes.DEFAULT_TYPE): await context.bot.send_message( text=text, chat_id=chat_id, - parse_mode=ParseMode.HTML ) @@ -383,7 +379,6 @@ async def back(update: Update, context: ContextTypes.DEFAULT_TYPE): await query.delete_message() await update.effective_chat.send_message( text=text, - parse_mode=ParseMode.HTML, reply_markup=reply_markup, message_thread_id=query.message.message_thread_id ) @@ -392,14 +387,12 @@ async def back(update: Update, context: ContextTypes.DEFAULT_TYPE): await query.edit_message_caption( caption=text, reply_markup=reply_markup, - parse_mode=ParseMode.HTML, ) except BadRequest as e: main_handlers_logger.error(e) await query.delete_message() await update.effective_chat.send_message( text=text, - parse_mode=ParseMode.HTML, reply_markup=reply_markup, message_thread_id=query.message.message_thread_id ) @@ -418,14 +411,12 @@ async def back(update: Update, context: ContextTypes.DEFAULT_TYPE): photo=photo, caption=text, reply_markup=reply_markup, - parse_mode=ParseMode.HTML, message_thread_id=query.message.message_thread_id ) else: await update.effective_chat.send_message( text=text, reply_markup=reply_markup, - parse_mode=ParseMode.HTML, message_thread_id=query.message.message_thread_id ) except BadRequest as e: @@ -433,19 +424,16 @@ async def back(update: Update, context: ContextTypes.DEFAULT_TYPE): await query.edit_message_text( text=text, reply_markup=reply_markup, - parse_mode=ParseMode.HTML ) elif state == 'TIME': await query.edit_message_text( text=text, reply_markup=reply_markup, - parse_mode=ParseMode.HTML ) else: await query.edit_message_text( text=text, reply_markup=reply_markup, - parse_mode=ParseMode.HTML, ) context.user_data['STATE'] = state return state @@ -468,7 +456,6 @@ async def cancel(update: Update, context: ContextTypes.DEFAULT_TYPE): text='Вы выбрали отмену\nИспользуйте команды:\n' f'/{COMMAND_DICT['RESERVE'][0]} - для повторного ' f'резервирования свободных мест на спектакль', - parse_mode=ParseMode.HTML, message_thread_id=query.message.message_thread_id ) @@ -505,7 +492,6 @@ async def cancel(update: Update, context: ContextTypes.DEFAULT_TYPE): f'резервирования свободных мест на спектакль\n' f'/{COMMAND_DICT['RESERVE_ADMIN'][0]} - для повторной ' f'записи без подтверждения', - parse_mode=ParseMode.HTML, message_thread_id=query.message.message_thread_id ) @@ -571,7 +557,7 @@ async def feedback_send_msg(update: Update, context: ContextTypes.DEFAULT_TYPE): 'Ссылка ВКонтакте на нашу группу' 'Задать любые интересующие вас вопросы вы можете через сообщения группы' ) - await update.effective_chat.send_message(text, parse_mode=ParseMode.HTML) + await update.effective_chat.send_message(text) chat_id = ADMIN_GROUP message = await update.message.forward( @@ -582,7 +568,6 @@ async def feedback_send_msg(update: Update, context: ContextTypes.DEFAULT_TYPE): chat_id, f'Сообщение от пользователя @{user.username} ' f'{user.full_name}', - parse_mode=ParseMode.HTML, reply_to_message_id=message.message_id, message_thread_id=message.message_thread_id ) diff --git a/src/handlers/reserve_admin_hl.py b/src/handlers/reserve_admin_hl.py index 26db6bb..ff1c399 100644 --- a/src/handlers/reserve_admin_hl.py +++ b/src/handlers/reserve_admin_hl.py @@ -79,7 +79,6 @@ async def enter_event_id(update: Update, context: ContextTypes.DEFAULT_TYPE): message = await query.edit_message_text( text=text, reply_markup=reply_markup, - parse_mode=ParseMode.HTML ) context.user_data['message'] = message.id @@ -218,7 +217,6 @@ async def start_forma_info( await query.edit_message_text( 'Напишите фамилию и имя (взрослого)', - parse_mode=ParseMode.HTML ) if common_data.get('dict_of_shows', False): diff --git a/src/handlers/reserve_hl.py b/src/handlers/reserve_hl.py index 8f18976..0e37781 100644 --- a/src/handlers/reserve_hl.py +++ b/src/handlers/reserve_hl.py @@ -256,14 +256,12 @@ async def choice_show_or_date( photo=photo, caption=text, reply_markup=reply_markup, - parse_mode=ParseMode.HTML ) else: await update.effective_chat.send_message( text=text, reply_markup=reply_markup, message_thread_id=update.callback_query.message.message_thread_id, - parse_mode=ParseMode.HTML ) reserve_user_data['number_of_month_str'] = number_of_month_str @@ -344,13 +342,11 @@ async def choice_date(update: Update, context: ContextTypes.DEFAULT_TYPE): await query.edit_message_caption( caption=text, reply_markup=reply_markup, - parse_mode=ParseMode.HTML ) else: await query.edit_message_text( text=text, reply_markup=reply_markup, - parse_mode=ParseMode.HTML ) if context.user_data['command'] == 'list_wait': @@ -453,7 +449,6 @@ async def choice_time(update: Update, context: ContextTypes.DEFAULT_TYPE): await update.effective_chat.send_message( text=text, reply_markup=reply_markup, - parse_mode=ParseMode.HTML, message_thread_id=update.callback_query.message.message_thread_id ) @@ -536,7 +531,6 @@ async def choice_option_of_reserve( text = text_select_show await query.edit_message_text( text=text, - parse_mode=ParseMode.HTML ) reserve_user_data['event_info_for_list_waiting'] = text @@ -685,7 +679,6 @@ async def choice_option_of_reserve( await query.message.edit_text( text=text, - parse_mode=ParseMode.HTML, reply_markup=reply_markup ) @@ -734,7 +727,6 @@ async def check_and_send_buy_info( await update.effective_chat.send_message( text=text, reply_markup=reply_markup, - parse_mode=ParseMode.HTML ) context.user_data['STATE'] = state return state @@ -800,7 +792,6 @@ async def check_and_send_buy_info( await query.message.edit_text( text=text, - parse_mode=ParseMode.HTML ) message = await update.effective_chat.send_message( 'Проверяю наличие свободных мест...') @@ -863,7 +854,6 @@ async def check_and_send_buy_info( await update.effective_chat.send_message( text=text, reply_markup=reply_markup, - parse_mode=ParseMode.HTML ) state = 'CHOOSING' context.user_data['STATE'] = state @@ -960,7 +950,6 @@ async def check_and_send_buy_info( 1. Заполнить анкету (она придет автоматически) 2. Дождаться подтверждения""", reply_markup=reply_markup, - parse_mode=ParseMode.HTML, disable_web_page_preview=True ) common_data = context.user_data['common_data'] @@ -1035,11 +1024,9 @@ async def forward_photo_or_file( ) await update.effective_chat.send_message( text=text_brief, - parse_mode=ParseMode.HTML, ) await update.effective_chat.send_message( 'Напишите фамилию и имя (взрослого)', - parse_mode=ParseMode.HTML ) # Сообщение для администратора @@ -1059,7 +1046,6 @@ async def forward_photo_or_file( f'Запросил подтверждение брони на сумму {chose_price} руб\n' f'Ждем заполнения анкеты, если всё хорошо, то только после ' f'нажимаем подтвердить', - parse_mode=ParseMode.HTML, reply_markup=reply_markup, message_thread_id=thread_id ) @@ -1082,7 +1068,6 @@ async def get_name_adult( await update.effective_chat.send_message( text='Напишите номер телефона', - parse_mode=ParseMode.HTML ) state = 'PHONE' @@ -1109,7 +1094,6 @@ async def get_phone(update: Update, context: ContextTypes.DEFAULT_TYPE): - Если детей несколько, напишите всех в одном сообщении - Один ребенок = одна строка - Не используйте дополнительные слова и пунктуацию, кроме тех, что указаны в примерах""", - parse_mode=ParseMode.HTML ) state = 'CHILDREN' @@ -1145,7 +1129,6 @@ async def get_name_children( reserve_hl_logger.info('Не верный формат текста') await update.effective_chat.send_message( text=text_for_message, - parse_mode=ParseMode.HTML ) return context.user_data['STATE'] @@ -1181,7 +1164,6 @@ async def get_name_children( await update.effective_chat.send_message(f'Вы ввели:\n{text}') await update.effective_chat.send_message( text=text_for_message, - parse_mode=ParseMode.HTML ) state = 'CHILDREN' context.user_data['STATE'] = state @@ -1277,7 +1259,6 @@ async def send_by_ticket_info(update, context): 'Вам придет сообщение: "Ваша бронь подтверждена"\n' 'Если сообщение не придет в течение суток, напишите в группу в ' 'контакте', - parse_mode=ParseMode.HTML ) text = context.user_data['common_data']['text_for_notification_massage'] text += (f'__________\n' @@ -1285,7 +1266,6 @@ async def send_by_ticket_info(update, context): 'https://vk.com/baby_theater_domik') message = await update.effective_chat.send_message( text=text, - parse_mode=ParseMode.HTML ) await message.pin() @@ -1401,7 +1381,6 @@ async def send_clients_data( reserve_hl_logger.info('Примечание не задано') await query.edit_message_text( text=text, - parse_mode=ParseMode.HTML ) state = ConversationHandler.END context.user_data['STATE'] = state @@ -1444,7 +1423,6 @@ async def get_phone_for_waiting( await context.bot.send_message( chat_id=ADMIN_GROUP, text=text, - parse_mode=ParseMode.HTML, message_thread_id=thread_id ) write_client_list_waiting(context) diff --git a/src/utilities/utl_func.py b/src/utilities/utl_func.py index 809855a..45e8908 100644 --- a/src/utilities/utl_func.py +++ b/src/utilities/utl_func.py @@ -75,7 +75,6 @@ async def echo(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: await context.bot.send_message( chat_id=update.effective_chat.id, text=text, - parse_mode=constants.ParseMode.HTML, message_thread_id=update.effective_message.message_thread_id ) @@ -244,7 +243,6 @@ async def send_message_to_admin( await context.bot.send_message( chat_id=chat_id, text=text, - parse_mode=ParseMode.HTML, reply_to_message_id=message_id, message_thread_id=thread_id ) @@ -261,7 +259,6 @@ async def send_message_to_admin( await context.bot.send_message( chat_id=chat_id, text=text, - parse_mode=ParseMode.HTML, message_thread_id=thread_id ) @@ -554,17 +551,14 @@ async def split_message(context, message: str): await context.bot.send_message( chat_id=CHAT_ID_MIKIEREMIKI, text='
' + message[start:] + '
', - parse_mode=ParseMode.HTML ) break await context.bot.send_message( chat_id=CHAT_ID_MIKIEREMIKI, text='
' + message[start:end] + '
', - parse_mode=ParseMode.HTML ) else: await context.bot.send_message( chat_id=CHAT_ID_MIKIEREMIKI, text='
' + message + '
', - parse_mode=ParseMode.HTML ) From bc032c215524253595f7b29298ddd3c502c89487 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Fri, 8 Mar 2024 16:27:26 +0300 Subject: [PATCH 21/71] =?UTF-8?q?feat:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20=D1=84=D0=B0=D0=B9=D0=BB=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Сервер на FastApi - Dockerfile для сборки образа - зависимости для python --- .gitignore | 1 + Dockerfile-server | 12 ++++++++++ requirements-server.txt | 3 +++ src/api/fastapi_nats.py | 52 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+) create mode 100644 Dockerfile-server create mode 100644 requirements-server.txt create mode 100644 src/api/fastapi_nats.py diff --git a/.gitignore b/.gitignore index 6795dd7..7b19036 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ config/* settings.py /src/utilities/pickle/ud_ids/ /src/utilities/pickle/user_ids +**/sql_files/ diff --git a/Dockerfile-server b/Dockerfile-server new file mode 100644 index 0000000..4907922 --- /dev/null +++ b/Dockerfile-server @@ -0,0 +1,12 @@ +FROM python:3.12-slim as base + +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 + +WORKDIR /app + +RUN --mount=type=bind,source=requirements-server.txt,target=requirements-server.txt \ + python -m pip install -r requirements-server.txt + +COPY /src/api/fastapi_nats.py . +CMD python3 -m fastapi_nats run \ No newline at end of file diff --git a/requirements-server.txt b/requirements-server.txt new file mode 100644 index 0000000..744dc2c --- /dev/null +++ b/requirements-server.txt @@ -0,0 +1,3 @@ +uvicorn +fastapi +faststream[nats] \ No newline at end of file diff --git a/src/api/fastapi_nats.py b/src/api/fastapi_nats.py new file mode 100644 index 0000000..d649a46 --- /dev/null +++ b/src/api/fastapi_nats.py @@ -0,0 +1,52 @@ +import asyncio +from dataclasses import dataclass + +import uvicorn +from fastapi import FastAPI, Response +from faststream.nats.fastapi import NatsRouter, Logger, NatsBroker + +router = NatsRouter('nats://nats:4222') + + +@dataclass +class WebhookNotification: + type: str + event: str + object: dict + + +@router.get("/") +async def hello_http(): + return "Hello, HTTP!" + + +@router.post("/yookassa") +async def post_notification( + message: WebhookNotification, + logger: Logger, + broker: NatsBroker +): + logger.info(message) + await broker.publish(message, 'bot', stream="baby_domik") + return Response(status_code=200) + + +app = FastAPI(lifespan=router.lifespan_context) +app.include_router(router, tags=["main requests"]) + + +async def main(): + webserver = uvicorn.Server( + config=uvicorn.Config( + app=app, + host='0.0.0.0', + port=443, + ssl_keyfile='server.key', + ssl_certfile='server.crt' + ) + ) + await webserver.serve() + + +if __name__ == "__main__": + asyncio.run(main()) From 773cee4c6cf7eb093fc5e3a212d7e0d962b4aeae Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Tue, 12 Mar 2024 21:56:37 +0300 Subject: [PATCH 22/71] =?UTF-8?q?feat:=20=D0=9F=D0=B5=D1=80=D0=B5=D1=80?= =?UTF-8?q?=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D0=BD=D0=B0=20=D0=BC=D0=BE=D0=B4?= =?UTF-8?q?=D0=B5=D0=BB=D1=8C=20=D0=B1=D0=B0=D0=B7=D1=8B=20=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=D0=BD=D1=8B=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Добавлены классы: Person и Child|Adult для вспомог данных Добавлены две таблицы UserTicket и PersonTicket для связей m2m Оптимизированы relationship для o2m связей --- src/db/models.py | 107 +++++++++++++++++++++++++++++++---------------- 1 file changed, 70 insertions(+), 37 deletions(-) diff --git a/src/db/models.py b/src/db/models.py index cd32b89..ef04454 100644 --- a/src/db/models.py +++ b/src/db/models.py @@ -5,37 +5,77 @@ from sqlalchemy.orm import Mapped, mapped_column, relationship from db import BaseModel, BaseModelTimed -from db.enum_types import TicketStatus, TicketPriceType, PriceType +from db.enum import TicketStatus, TicketPriceType, PriceType, AgeType class User(BaseModelTimed): __tablename__ = 'users' - user_id: Mapped[int] = mapped_column(BigInteger, - primary_key=True, - autoincrement=False) - chat_id: Mapped[int] + id: Mapped[int] = mapped_column( + BigInteger, primary_key=True, autoincrement=False, name='user_id') + chat_id: Mapped[int] = mapped_column(BigInteger) - callback_name: Mapped[str] - callback_phone: Mapped[Optional[str]] username: Mapped[Optional[str]] + email: Mapped[Optional[str]] - children: Mapped[List['Child']] = relationship(back_populates='users') - tickets: Mapped[List['Ticket']] = relationship(back_populates='users') + people: Mapped[List['Person']] = relationship(lazy='selectin') + tickets: Mapped[List['Ticket']] = relationship( + back_populates='users', secondary='users_tickets', lazy='selectin') + + +class Person(BaseModelTimed): + __tablename__ = 'people' + + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[Optional[str]] + age_type: Mapped[AgeType] + + user_id: Mapped[int] = mapped_column( + BigInteger, ForeignKey('users.id', ondelete='CASCADE')) + + children: Mapped[List['Person']] = relationship(lazy='selectin') + adults: Mapped[List['Person']] = relationship(lazy='selectin') + tickets: Mapped[List['Ticket']] = relationship( + back_populates='people', secondary='people_tickets', lazy='selectin') class Child(BaseModel): __tablename__ = 'children' id: Mapped[int] = mapped_column(primary_key=True) - name: Mapped[str] - age: Mapped[float] + age: Mapped[Optional[float]] birthdate: Mapped[Optional[date]] + person_id: Mapped[int] = mapped_column( + ForeignKey('people.id', ondelete='CASCADE')) + + +class Adult(BaseModel): + __tablename__ = 'adults' + + id: Mapped[int] = mapped_column(primary_key=True) + phone: Mapped[Optional[str]] + + person_id: Mapped[int] = mapped_column( + ForeignKey('people.id', ondelete='CASCADE')) + + +class UserTicket(BaseModelTimed): + __tablename__ = 'users_tickets' + user_id: Mapped[int] = mapped_column( - ForeignKey('users.user_id', ondelete='CASCADE') - ) - users: Mapped['User'] = relationship(back_populates='children') + ForeignKey('users.id'), primary_key=True) + ticket_id: Mapped[int] = mapped_column( + ForeignKey('tickets.id'), primary_key=True) + + +class PersonTicket(BaseModelTimed): + __tablename__ = 'people_tickets' + + person_id: Mapped[int] = mapped_column( + ForeignKey('people.id'), primary_key=True) + ticket_id: Mapped[int] = mapped_column( + ForeignKey('tickets.id'), primary_key=True) class Ticket(BaseModelTimed): @@ -47,22 +87,18 @@ class Ticket(BaseModelTimed): price: Mapped[int] exclude: Mapped[bool] = mapped_column(default=False) status: Mapped[TicketStatus] + notes: Mapped[Optional[str]] + + payment_id: Mapped[Optional[str]] + idempotency_id: Mapped[Optional[str]] - child_id: Mapped[List['Child']] = mapped_column(ForeignKey('children.id')) - user_id: Mapped[int] = mapped_column( - ForeignKey('users.user_id', ondelete='CASCADE') - ) - theater_event_id: Mapped[int] = mapped_column( - ForeignKey('theater_events.id', ondelete='CASCADE') - ) schedule_event_id: Mapped[int] = mapped_column( - ForeignKey('schedule_events.id', ondelete='CASCADE') - ) - notes: Mapped[Optional[str]] + ForeignKey('schedule_events.id')) - users: Mapped['User'] = relationship(back_populates='tickets') - schedule_events: Mapped['ScheduleEvent'] = relationship( - back_populates='tickets') + users: Mapped['User'] = relationship( + secondary='users_tickets', back_populates='tickets', lazy='selectin') + people: Mapped[List['Person']] = relationship( + secondary='people_tickets', back_populates='tickets', lazy='selectin') class TypeEvent(BaseModel): @@ -91,19 +127,17 @@ class TheaterEvent(BaseModel): flag_indiv_cost: Mapped[bool] = mapped_column(default=False) price_type: Mapped[PriceType] = mapped_column(default=PriceType.NONE) + schedule_events: Mapped[List['ScheduleEvent']] = relationship( + lazy='selectin') + class ScheduleEvent(BaseModelTimed): __tablename__ = 'schedule_events' id: Mapped[int] = mapped_column(primary_key=True) - event_type: Mapped[int] - - type_id: Mapped[int] = mapped_column( - ForeignKey('type_events.id', ondelete='CASCADE') - ) - theater_events_id: Mapped[int] = mapped_column( - ForeignKey('theater_events.id', ondelete='CASCADE') - ) + + type_event_id: Mapped[int] = mapped_column(ForeignKey('type_events.id')) + theater_events_id: Mapped[int] = mapped_column(ForeignKey('theater_events.id')) flag_turn_in_bot: Mapped[bool] = mapped_column(default=False) datetime_event: Mapped[datetime] @@ -121,5 +155,4 @@ class ScheduleEvent(BaseModelTimed): ticket_price_type: Mapped[TicketPriceType] = mapped_column( default=TicketPriceType.NONE) - tickets: Mapped[List['Ticket']] = relationship( - back_populates='schedule_events') + tickets: Mapped[List['Ticket']] = relationship(lazy='selectin') From ab5762b4b3f61d2e8a9f423d5b45c7c943561919 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Tue, 12 Mar 2024 21:57:25 +0300 Subject: [PATCH 23/71] =?UTF-8?q?feat:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20=D1=82=D0=B8=D0=BF=20=D0=B4=D0=BB=D1=8F=20?= =?UTF-8?q?=D0=92=D0=B7=D1=80=D0=BE=D1=81=D0=BB=D1=8B=D0=B9/=D0=A0=D0=B5?= =?UTF-8?q?=D0=B1=D0=B5=D0=BD=D0=BE=D0=BA=20=D0=B2=20=D1=82=D0=B0=D0=B1?= =?UTF-8?q?=D0=BB=D0=B8=D1=86=D0=B5=20Person?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/{enum_types.py => enum.py} | 10 ++++++++++ 1 file changed, 10 insertions(+) rename src/db/{enum_types.py => enum.py} (83%) diff --git a/src/db/enum_types.py b/src/db/enum.py similarity index 83% rename from src/db/enum_types.py rename to src/db/enum.py index b630c17..2ee4d2f 100644 --- a/src/db/enum_types.py +++ b/src/db/enum.py @@ -28,6 +28,11 @@ class TicketPriceType(enum.Enum): weekend = 'выходные' +class AgeType(enum.Enum): + adult = 'взрослый' + child = 'ребенок' + + TicketStatusEnum = sa.Enum( TicketStatus, name="TicketStatusEnum", @@ -43,3 +48,8 @@ class TicketPriceType(enum.Enum): name="TicketPriceTypeEnum", metadata=BaseModel.metadata ) +AgeTypeEnum = sa.Enum( + AgeType, + name="AgeTypeEnum", + metadata=BaseModel.metadata +) From ddb6ca09c50c5d07d377b3f7eec8a7262fcc3703 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Tue, 12 Mar 2024 22:13:24 +0300 Subject: [PATCH 24/71] =?UTF-8?q?refactor:=20=D0=94=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BE=D0=BF=D1=86=D0=B8?= =?UTF-8?q?=D0=BE=D0=BD=D0=B0=D0=BB=D1=8C=D0=BD=D0=BE=D1=81=D1=82=D0=B8=20?= =?UTF-8?q?id=20=D1=80=D0=B0=D1=81=D0=BF=D0=B8=D1=81=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D1=8E=20=D0=B4=D0=BB=D1=8F=20=D1=82=D0=B0=D0=B1=D0=BB=D0=B8?= =?UTF-8?q?=D1=86=D1=8B=20=D0=B1=D0=B8=D0=BB=D0=B5=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/models.py b/src/db/models.py index ef04454..100a69b 100644 --- a/src/db/models.py +++ b/src/db/models.py @@ -92,7 +92,7 @@ class Ticket(BaseModelTimed): payment_id: Mapped[Optional[str]] idempotency_id: Mapped[Optional[str]] - schedule_event_id: Mapped[int] = mapped_column( + schedule_event_id: Mapped[Optional[int]] = mapped_column( ForeignKey('schedule_events.id')) users: Mapped['User'] = relationship( From 30a47f2069aaeda7c94f808e3404326bdebaa19e Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 20:25:12 +0300 Subject: [PATCH 25/71] =?UTF-8?q?feat:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=B8=D0=BD=D1=82=D0=B5=D0=B3=D1=80?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D1=8F=20=D1=81=20nats?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/broker_nats.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/api/broker_nats.py diff --git a/src/api/broker_nats.py b/src/api/broker_nats.py new file mode 100644 index 0000000..169c121 --- /dev/null +++ b/src/api/broker_nats.py @@ -0,0 +1,25 @@ +from faststream import Logger +from faststream.nats import JStream, NatsMessage, NatsBroker +from telegram.ext import Application +from yookassa.domain.notification import WebhookNotificationFactory + +from settings.settings import nats_url + + +def connect_to_nats(app: Application, + webhook_notification_factory: WebhookNotificationFactory): + broker = NatsBroker(nats_url) + stream = JStream(name="baby_domik", max_msgs=100, max_age=60*60*24*7) + + @broker.subscriber("bot", stream=stream, durable='yookassa') + async def nats_handler( + data: dict, + logger: Logger, + nats_message: NatsMessage, + ): + logger.info(f'{data=}') + notification = webhook_notification_factory.create(data) + # print(f'{nats_message=}') + await app.update_queue.put(notification) + + return broker From 021a61623cad73f9a61e6a7ee3e7885f522fa1e5 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 21:22:32 +0300 Subject: [PATCH 26/71] =?UTF-8?q?feat:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=B8=D0=BD=D1=82=D0=B5=D0=B3=D1=80?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D1=8F=20=D1=81=20nats,=20faststream?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Добавление хэндлеров вынесено в отдельную функцию. Изменение логики запуска поллинга через контекст FastStream приложения --- src/bot.py | 186 ++++++++++++++++++----------------------------------- 1 file changed, 61 insertions(+), 125 deletions(-) diff --git a/src/bot.py b/src/bot.py index 0a694aa..798570c 100644 --- a/src/bot.py +++ b/src/bot.py @@ -1,139 +1,75 @@ -from telegram.constants import ParseMode -from telegram.ext import ( - Application, - CommandHandler, CallbackQueryHandler, MessageHandler, - filters, Defaults, -) +import asyncio +from contextlib import asynccontextmanager -from db import pickle_persistence, middleware_db_add_handlers +from telegram.constants import ParseMode +from telegram.ext import Application, Defaults +from yookassa import Configuration +from faststream import FastStream +from yookassa.domain.notification import WebhookNotificationFactory + +from api.broker_nats import connect_to_nats +from db import pickle_persistence +from handlers.set_handlers import set_handlers from log.logging_conf import load_log_config -from handlers import main_hl -from handlers.error_hl import error_handler -from handlers.timeweb_hl import get_balance -from handlers.sub_hl import ( - update_ticket_data, update_show_data, update_admin_info, update_bd_price, - update_special_ticket_price -) -from conv_hl.reserve_conv_hl import reserve_conv_hl -from conv_hl.reserve_admin_conv_hl import reserve_admin_conv_hl -from conv_hl.list_wait_conv_hl import list_wait_conv_hl -from conv_hl.birthday_conv_hl import birthday_conv_hl, birthday_paid_conv_hl -from conv_hl.afisha_conv_hl import afisha_conv_hl from utilities.utl_func import ( - echo, send_log, set_menu, set_description, set_ticket_data, set_show_data, set_special_ticket_price, - get_location, get_contact, request_contact_location, - print_ud, clean_ud, clean_bd, - create_or_connect_topic, del_topic, ) -from settings.settings import ADMIN_ID, COMMAND_DICT from settings.config_loader import parse_settings -async def post_init(application: Application): - await set_menu(application.bot) - await set_description(application.bot) - set_ticket_data(application) - set_show_data(application) - set_special_ticket_price(application) - - application.bot_data.setdefault('admin', {}) - application.bot_data['admin'].setdefault('contacts', {}) - application.bot_data.setdefault('dict_topics_name', {}) - - -def bot(): - bot_logger = load_log_config() - bot_logger.info('Инициализация бота') - - config = parse_settings() - - application = ( - Application.builder() - .token(config.bot.token.get_secret_value()) - .persistence(pickle_persistence) - .post_init(post_init) - .defaults(Defaults(parse_mode=ParseMode.HTML)) - - .build() - ) - - application.bot_data.setdefault('config', config) - - middleware_db_add_handlers(application, config) - - application.add_handlers([ - CommandHandler(COMMAND_DICT['START'][0], main_hl.start), - CommandHandler('reset', main_hl.reset), - CommandHandler('echo', echo), - ]) - - application.add_handlers([ - CallbackQueryHandler(main_hl.confirm_reserve, '^confirm-reserve'), - CallbackQueryHandler(main_hl.reject_reserve, '^reject-reserve'), - CallbackQueryHandler(main_hl.confirm_birthday, '^confirm-birthday'), - CallbackQueryHandler(main_hl.reject_birthday, '^reject-birthday'), - ]) - - conversation_handlers = [ - reserve_conv_hl, - reserve_admin_conv_hl, - list_wait_conv_hl, - birthday_conv_hl, - birthday_paid_conv_hl, - afisha_conv_hl, - ] - application.add_handlers(conversation_handlers) - - filter_admin = filters.User(ADMIN_ID) - application.add_handlers([ - CommandHandler('clean_ud', clean_ud, filter_admin), - CommandHandler('print_ud', print_ud, filter_admin), - CommandHandler('clean_bd', clean_bd, filter_admin), - CommandHandler(COMMAND_DICT['LOG'][0], send_log, filter_admin), - CommandHandler(COMMAND_DICT['CB_TW'][0], get_balance, filter_admin), - CommandHandler(COMMAND_DICT['TOPIC_DEL'][0], del_topic, filter_admin), - CommandHandler(COMMAND_DICT['TOPIC_START'][0], create_or_connect_topic, - filter_admin), - CommandHandler(COMMAND_DICT['UP_T_DATA'][0], update_ticket_data, - filter_admin), - CommandHandler(COMMAND_DICT['UP_S_DATA'][0], update_show_data, - filter_admin), - CommandHandler(COMMAND_DICT['UP_BD_PRICE'][0], update_bd_price, - filter_admin), - CommandHandler(COMMAND_DICT['UP_SPEC_PRICE'][0], - update_special_ticket_price, - filter_admin), - CommandHandler(COMMAND_DICT['ADM_INFO'][0], - update_admin_info, - filter_admin), - ]) - - application.add_handler( - CommandHandler('rcl', request_contact_location)) - application.add_handler(MessageHandler(filters.LOCATION, get_location)) - application.add_handler(MessageHandler(filters.CONTACT, get_contact)) - - application.add_handler(MessageHandler( - filters.ChatType.PRIVATE & (filters.TEXT | - filters.ATTACHMENT | - filters.VIDEO | - filters.PHOTO | - filters.FORWARDED | - filters.Document.IMAGE | - filters.Document.PDF), - main_hl.feedback_send_msg), - ) - - application.add_error_handler(error_handler) - - bot_logger.info('Всё готово к поллингу') - - application.run_polling() +async def post_init(app: Application): + await set_menu(app.bot) + await set_description(app.bot) + set_ticket_data(app) + set_show_data(app) + set_special_ticket_price(app) + + app.bot_data.setdefault('admin', {}) + app.bot_data['admin'].setdefault('contacts', {}) + app.bot_data.setdefault('dict_topics_name', {}) + + +bot_logger = load_log_config() +bot_logger.info('Инициализация бота') + +config = parse_settings() + +application = ( + Application.builder() + .token(config.bot.token.get_secret_value()) + .persistence(pickle_persistence) + .post_init(post_init) + .defaults(Defaults(parse_mode=ParseMode.HTML)) + .build() +) +application.bot_data.setdefault('config', config) + +Configuration.configure(config.yookassa.account_id, + config.yookassa.secret_key.get_secret_value()) + +webhook_notification_factory = WebhookNotificationFactory() +broker = connect_to_nats(application, webhook_notification_factory) + +@asynccontextmanager +async def lifespan(): + set_handlers(application, config) + await application.initialize() + await post_init(application) + await application.start() + await application.updater.start_polling() + + yield + + await application.updater.stop() + await application.stop() bot_logger.info('Бот остановлен') + await application.shutdown() + + +fast_stream = FastStream(broker, lifespan=lifespan) if __name__ == '__main__': - bot() + asyncio.run(fast_stream.run()) From acccd267d1a87b4ac20933285b7a629f818201df Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 21:23:03 +0300 Subject: [PATCH 27/71] =?UTF-8?q?feat:=20=D0=92=D1=8B=D0=BD=D0=B5=D1=81?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BE=D0=B1=D1=89=D0=B8=D1=85=20?= =?UTF-8?q?=D1=85=D1=8D=D0=BD=D0=B4=D0=BB=D0=B5=D1=80=D0=BE=D0=B2=20=D0=B2?= =?UTF-8?q?=20=D0=BE=D1=82=D0=B4=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20?= =?UTF-8?q?=D1=84=D0=B0=D0=B9=D0=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/conv_hl/common_hl.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/conv_hl/common_hl.py diff --git a/src/conv_hl/common_hl.py b/src/conv_hl/common_hl.py new file mode 100644 index 0000000..a492832 --- /dev/null +++ b/src/conv_hl/common_hl.py @@ -0,0 +1,37 @@ +from warnings import filterwarnings + +from telegram.ext import CallbackQueryHandler, filters +from telegram.warnings import PTBUserWarning + +from handlers import main_hl, reserve_hl + +F_text_and_no_command = filters.TEXT & ~filters.COMMAND +cancel_callback_handler = CallbackQueryHandler(main_hl.cancel, '^Отменить') +back_callback_handler = CallbackQueryHandler(main_hl.back, '^Назад') + +base_handlers = { + 'MONTH': [ + cancel_callback_handler, + CallbackQueryHandler(reserve_hl.choice_show_or_date), + ], + 'SHOW': [ + cancel_callback_handler, + CallbackQueryHandler(main_hl.back, pattern='^Назад-MONTH'), + CallbackQueryHandler(reserve_hl.choice_date), + ], + 'DATE': [ + cancel_callback_handler, + CallbackQueryHandler(main_hl.back, pattern='^Назад-MONTH'), + CallbackQueryHandler(main_hl.back, pattern='^Назад-SHOW'), + CallbackQueryHandler(reserve_hl.choice_time), + ], + 'TIME': [ + cancel_callback_handler, + CallbackQueryHandler(main_hl.back, pattern='^Назад-DATE'), + CallbackQueryHandler(reserve_hl.choice_option_of_reserve), + ], +} + +filterwarnings(action="ignore", + message=r".*CallbackQueryHandler", + category=PTBUserWarning) From 15a2f36db59e6ce2179992e4e0f9ea9865830538 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 21:23:31 +0300 Subject: [PATCH 28/71] =?UTF-8?q?feat:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BD=D0=B0=D1=81=D1=82=D1=80?= =?UTF-8?q?=D0=BE=D0=B5=D0=BA=20=D0=B4=D0=BB=D1=8F=20=D0=AE=D0=BA=D0=B0?= =?UTF-8?q?=D1=81=D1=81=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/settings/config_loader.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/settings/config_loader.py b/src/settings/config_loader.py index 0566df2..9aaa800 100644 --- a/src/settings/config_loader.py +++ b/src/settings/config_loader.py @@ -31,11 +31,17 @@ class GoogleSheetsSettings(BaseModel): sheet_id: str +class YookassaSettings(BaseModel): + account_id: int + secret_key: SecretStr + + class Settings(BaseSettings): bot: BotSettings timeweb: TimeWebSettings postgres: PostgresSettings sheets: GoogleSheetsSettings + yookassa: YookassaSettings def parse_settings(local_file_name: str = "config/settings.yml") -> Settings: From 124a3f78245c9fba82f60bcabfbcfb53048b9e59 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 21:25:18 +0300 Subject: [PATCH 29/71] =?UTF-8?q?feat:=20=D0=A3=D0=BF=D1=80=D0=BE=D1=89?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20reserve=5Fadmin=5Fdata=20(=D1=82?= =?UTF-8?q?=D0=B5=D0=BF=D0=B5=D1=80=D1=8C=20=D1=81=D0=BE=D0=B4=D0=B5=D1=80?= =?UTF-8?q?=D0=B6=D0=B8=D1=82=20=D1=82=D0=BE=D0=BB=D1=8C=D0=BA=D0=BE=20?= =?UTF-8?q?=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D0=B5=20=D0=BF=D0=BE=20=D0=BE?= =?UTF-8?q?=D0=B4=D0=BD=D0=BE=D0=BC=D1=83=20=D0=BF=D0=BB=D0=B0=D1=82=D0=B5?= =?UTF-8?q?=D0=B6=D1=83)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Добавление key_option_for_reserve --- src/utilities/schemas/context_user_data.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/utilities/schemas/context_user_data.py b/src/utilities/schemas/context_user_data.py index 04a187f..33f62a2 100644 --- a/src/utilities/schemas/context_user_data.py +++ b/src/utilities/schemas/context_user_data.py @@ -65,13 +65,14 @@ }, 'chose_price': int, 'type_ticket_price': str, + 'key_option_for_reserve': int, }, 'reserve_admin_data': { - int: { # payment_id + 'payment_data': { 'event_id': str, - 'chose_ticket': BaseTicket + 'chose_ticket': BaseTicket, + 'ticket_id': int, }, - 'payment_id': int, # Указатель на последний идентификатор платежа }, 'month_afisha': int, } From 0bcbc072f479f77bb5ac52392500b3f062b40000 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 21:27:14 +0300 Subject: [PATCH 30/71] =?UTF-8?q?refactor:=20=D0=9E=D1=82=D0=B4=D0=B5?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20engine=20=D0=B8=20sessionmaker=20=D0=B2=20?= =?UTF-8?q?=D1=81=D0=B2=D0=BE=D1=8E=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8?= =?UTF-8?q?=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/database.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/db/database.py b/src/db/database.py index d7d0e9f..7379aa6 100644 --- a/src/db/database.py +++ b/src/db/database.py @@ -4,17 +4,21 @@ from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker -def middleware_db_add_handlers(application, config): +def create_sessionmaker_and_engine(db_url, echo=True): async_engine = create_async_engine( - url=str(config.postgres.db_url), - echo=True, + url=db_url, + echo=echo, connect_args={"options": "-c timezone=utc"}, ) - sessionmaker = async_sessionmaker( + return async_sessionmaker( async_engine, expire_on_commit=False ) + +def middleware_db_add_handlers(application, config): + sessionmaker = create_sessionmaker_and_engine(str(config.postgres.db_url)) + async def open_session_handler( _: Update, context: ContextTypes.DEFAULT_TYPE From 4caaebba19ffe0fdac0031ae1939e65d78451cb6 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 21:31:05 +0300 Subject: [PATCH 31/71] =?UTF-8?q?feat:=20=D0=A1=D0=BE=D0=B7=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=D1=8B=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B8=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20?= =?UTF-8?q?=D1=81=20=D0=91=D0=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/db_postgres.py | 366 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 339 insertions(+), 27 deletions(-) diff --git a/src/db/db_postgres.py b/src/db/db_postgres.py index 2b5ee64..987d16b 100644 --- a/src/db/db_postgres.py +++ b/src/db/db_postgres.py @@ -1,33 +1,345 @@ -from sqlalchemy import select, exists, insert, update +from sqlalchemy import select, exists, insert, delete, update from sqlalchemy.ext.asyncio import AsyncSession -from telegram import Update -from db import User +from db import User, Person, Adult, TheaterEvent, Ticket, Child, ScheduleEvent +from db.enum import PriceType, TicketStatus, TicketPriceType, AgeType -class AsyncORM: - @staticmethod - async def create_user(update_tg: Update, session: AsyncSession): - stmt = insert(User).values( - user_id=update_tg.effective_user.id, - chat_id=update_tg.effective_chat.id, - callback_name=update_tg.effective_user.full_name, - username=update_tg.effective_user.username, +async def create_person(session: AsyncSession, user_id, name, age_type): + user = await session.get(User, user_id) + person = Person(name=name, age_type=age_type) + user.people.append(person) + + await session.commit() + return person + +async def get_persons(session: AsyncSession, user_id: int): + query = select(Person).where(exists().where(Person.user_id == user_id)) + result = await session.execute(query) + persons = result.all() + + if persons: + return persons + else: + return None + + +async def create_adult(session: AsyncSession, user_id, name, phone): + person = await create_person(session, user_id, name, AgeType.adult) + await session.refresh(person) + adult = Adult(phone=phone) + person.adults.append(adult) + + await session.commit() + return adult + + +async def create_child( + session: AsyncSession, + user_id, + name, + age=None, + birthdate=None, +): + person = await create_person(session, user_id, name, AgeType.child) + await session.refresh(person) + child = Child(age=age, birthdate=birthdate) + person.children.append(child) + + await session.commit() + return child + + +async def create_people( + session: AsyncSession, + user_id, + client_data, +): + people_ids = [] + name_adult = client_data['name_adult'] + phone = client_data['phone'] + data_children = client_data['data_children'] + adult = await create_adult(session, user_id, name_adult, phone) + people_ids.append(adult.person_id) + for item in data_children: + name_child = item[0] + age = item[1] + child = await create_child(session, user_id, name_child, age) + people_ids.append(child.person_id) + + await session.commit() + return people_ids + + +async def add_people_ticket( + session: AsyncSession, + ticket_id, + people_ids +): + ticket = await session.get(Ticket, ticket_id) + for person_id in people_ids: + person = await session.get(Person, person_id) + ticket.people.append(person) + await session.commit() + return ticket + + +async def add_user_ticket(session: AsyncSession, ticket_id, user_id): + ticket = await session.get(Ticket, ticket_id) + user = await session.get(User, user_id) + ticket.users = user + await session.commit() + return ticket + + +async def attach_user_and_people_to_ticket( + session: AsyncSession, + ticket_id, + user_id, + people +): + await add_people_ticket(session, ticket_id, people) + await add_user_ticket(session, ticket_id, user_id) + + +async def create_user( + session: AsyncSession, + user_id, + chat_id, + username=None, + email=None, +): + stmt = insert(User).values( + id=user_id, + chat_id=chat_id, + username=username, + email=email, + ) + result = await session.execute(stmt.returning(User.id)) + await session.commit() + return result.scalar() + + +async def update_user( + session: AsyncSession, + user_id, + **kwargs +): + user = await session.get(User, user_id) + for key, value in kwargs.items(): + setattr(user, key, value) + await session.commit() + return user + + +async def get_user(session: AsyncSession, user_id: int): + exists_criteria = ( + exists().where(User.id == user_id)) + query = select(User).where(exists_criteria) + result = await session.execute(query) + user = result.all() + + if user: + return user + else: + return None + + +async def create_theater_event( + session: AsyncSession, + name, + min_age_child, + show_emoji, + max_age_child=0, + flag_premier=False, + flag_active_repertoire=False, + flag_active_bd=False, + max_num_child_bd=8, + max_num_adult_bd=10, + flag_indiv_cost=False, + price_type=PriceType.NONE, + theater_event_id=None, +): + if not theater_event_id: + stmt = insert(TheaterEvent).values( + name=name, + min_age_child=min_age_child, + show_emoji=show_emoji, + max_age_child=max_age_child, + flag_premier=flag_premier, + flag_active_repertoire=flag_active_repertoire, + flag_active_bd=flag_active_bd, + max_num_child_bd=max_num_child_bd, + max_num_adult_bd=max_num_adult_bd, + flag_indiv_cost=flag_indiv_cost, + price_type=price_type, + ) + else: + stmt = insert(TheaterEvent).values( + id=theater_event_id, + name=name, + min_age_child=min_age_child, + show_emoji=show_emoji, + max_age_child=max_age_child, + flag_premier=flag_premier, + flag_active_repertoire=flag_active_repertoire, + flag_active_bd=flag_active_bd, + max_num_child_bd=max_num_child_bd, + max_num_adult_bd=max_num_adult_bd, + flag_indiv_cost=flag_indiv_cost, + price_type=price_type, ) - result = await session.execute(stmt.returning(User.user_id)) - await session.commit() - return result.scalar() - - @staticmethod - async def get_user(update_tg: Update, session: AsyncSession): - exists_criteria = ( - exists().where(User.user_id == update_tg.effective_user.id)) - query = select(User).where(exists_criteria) - result = await session.execute(query) - user = result.all() - - if user: - return user - else: - return None + result = await session.execute(stmt.returning(TheaterEvent.id)) + await session.commit() + return result.scalar() + + +async def get_theater_event(session: AsyncSession, theater_event_id: int): + return await session.get(TheaterEvent, theater_event_id) + + +async def del_theater_event(session: AsyncSession, theater_event_id: int): + query = ( + delete(TheaterEvent) + .where(TheaterEvent.id == theater_event_id) + ) + return await session.execute(query) + + +async def get_all_theater_events(session: AsyncSession): + query = select(TheaterEvent) + result = await session.execute(query) + return result.all() + + +async def create_ticket( + session: AsyncSession, + base_ticket_id, + price, + schedule_event_id, + exclude=False, + status=TicketStatus.CREATED, + notes=None, + payment_id=None, + idempotency_id=None, +): + stmt = insert(Ticket).values( + base_ticket_id=base_ticket_id, + price=price, + schedule_event_id=schedule_event_id, + exclude=exclude, + status=status, + notes=notes, + payment_id=payment_id, + idempotency_id=idempotency_id, + ) + result = await session.execute(stmt.returning(Ticket.id)) + await session.commit() + return result.scalar() + + +async def update_ticket( + session: AsyncSession, + ticket_id, + **kwargs +): + ticket = await session.get(Ticket, ticket_id) + for key, value in kwargs.items(): + setattr(ticket, key, value) + await session.commit() + return ticket + + +async def get_ticket( + session: AsyncSession, + ticket_id, +): + return await session.get(Ticket, ticket_id) + + +async def create_schedule_event( + session: AsyncSession, + type_event_id, + theater_events_id, + flag_turn_in_bot, + datetime_event, + qty_child=0, + qty_child_free_seat=0, + qty_child_nonconfirm_seat=0, + qty_adult=0, + qty_adult_free_seat=0, + qty_adult_nonconfirm_seat=0, + flag_gift=False, + flag_christmas_tree=False, + flag_santa=False, + ticket_price_type=TicketPriceType.NONE, + schedule_event_id=None, +): + if not schedule_event_id: + stmt = insert(ScheduleEvent).values( + type_event_id=type_event_id, + theater_events_id=theater_events_id, + flag_turn_in_bot=flag_turn_in_bot, + datetime_event=datetime_event, + qty_child=qty_child, + qty_child_free_seat=qty_child_free_seat, + qty_child_nonconfirm_seat=qty_child_nonconfirm_seat, + qty_adult=qty_adult, + qty_adult_free_seat=qty_adult_free_seat, + qty_adult_nonconfirm_seat=qty_adult_nonconfirm_seat, + flag_gift=flag_gift, + flag_christmas_tree=flag_christmas_tree, + flag_santa=flag_santa, + ticket_price_type=ticket_price_type, + ) + else: + stmt = insert(ScheduleEvent).values( + id=schedule_event_id, + type_event_id=type_event_id, + theater_events_id=theater_events_id, + flag_turn_in_bot=flag_turn_in_bot, + datetime_event=datetime_event, + qty_child=qty_child, + qty_child_free_seat=qty_child_free_seat, + qty_child_nonconfirm_seat=qty_child_nonconfirm_seat, + qty_adult=qty_adult, + qty_adult_free_seat=qty_adult_free_seat, + qty_adult_nonconfirm_seat=qty_adult_nonconfirm_seat, + flag_gift=flag_gift, + flag_christmas_tree=flag_christmas_tree, + flag_santa=flag_santa, + ticket_price_type=ticket_price_type, + ) + result = await session.execute(stmt.returning(ScheduleEvent.id)) + await session.commit() + return result.scalar() + + +async def update_schedule_event( + session: AsyncSession, + schedule_event_id, + **kwargs +): + event = await session.get(ScheduleEvent, schedule_event_id) + for key, value in kwargs.items(): + setattr(event, key, value) + await session.commit() + return event + + +async def get_schedule_event(session: AsyncSession, schedule_event_id: int): + return await session.get(ScheduleEvent, schedule_event_id) + + +async def del_schedule_event(session: AsyncSession, schedule_event_id: int): + query = ( + delete(ScheduleEvent) + .where(ScheduleEvent.id == schedule_event_id) + ) + return await session.execute(query) + +async def get_all_schedule_events(session: AsyncSession): + query = select(ScheduleEvent) + result = await session.execute(query) + return result.all() From db067c434fdc4e98c49d70d9b17e7a4275ded09f Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 21:34:12 +0300 Subject: [PATCH 32/71] =?UTF-8?q?refactor:=20=D0=98=D0=B7=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D1=8B=20=D1=81=20payment=5Fdata=20=D0=B8=20=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=BA=D0=B8=20=D0=BF=D0=BE=20=D0=BF=D0=BE=D1=80=D1=8F?= =?UTF-8?q?=D0=B4=D0=BA=D1=83=20=D1=81=D1=82=D1=80=D0=BE=D0=BA=D1=83=20fix?= =?UTF-8?q?:=20=D0=A3=D0=B1=D1=80=D0=B0=D0=BD=D1=8B=20=D1=81=D0=B8=D0=BC?= =?UTF-8?q?=D0=B2=D0=BE=D0=BB=D1=8B=20<>=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0?= =?UTF-8?q?=D1=8E=D1=82=20=D0=BF=D1=80=D0=BE=D0=B1=D0=BB=D0=B5=D0=BC=D1=83?= =?UTF-8?q?=20=D0=BF=D1=80=D0=B8=20HTML=20parse=20mode=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=20=D0=BE=D1=82=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B5=20?= =?UTF-8?q?=D0=B2=20=D1=82=D0=B5=D0=BB=D0=B5=D0=B3=D1=80=D0=B0=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/base.py | 2 +- src/handlers/__init__.py | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/db/base.py b/src/db/base.py index 3e00a1a..308f2fe 100644 --- a/src/db/base.py +++ b/src/db/base.py @@ -40,7 +40,7 @@ def __repr__(self): for idx, col in enumerate(self.__table__.columns.keys()): if col in self.repr_cols or idx < self.repr_cols_num: cols.append(f'{col}={getattr(self, col)}') - return f"<{self.__class__.__name__} {','.join(cols)}>" + return f"{self.__class__.__name__} {', '.join(cols)}" class BaseModelTimed(BaseModel): diff --git a/src/handlers/__init__.py b/src/handlers/__init__.py index 89444e5..a6305ea 100644 --- a/src/handlers/__init__.py +++ b/src/handlers/__init__.py @@ -7,15 +7,12 @@ def init_conv_hl_dialog(update, context): context.user_data['STATE'] = state context.user_data['command'] = extract_command( update.effective_message.text) + context.user_data.setdefault('common_data', {}) + context.user_data.setdefault('reserve_admin_data', {}) context.user_data['reserve_user_data'] = {} context.user_data['reserve_user_data']['back'] = {} context.user_data['reserve_user_data']['client_data'] = {} context.user_data['reserve_user_data']['choose_event_info'] = {} - context.user_data.setdefault('common_data', {}) - context.user_data.setdefault('reserve_admin_data', {'payment_id': 0}) - if not isinstance( - context.user_data['reserve_admin_data']['payment_id'], - int): - context.user_data['reserve_admin_data'] = {'payment_id': 0} + context.user_data['reserve_admin_data']['payment_data'] = {} return state From 5b8969b3a239588d2fb405800820546498e3f2a7 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 21:38:37 +0300 Subject: [PATCH 33/71] =?UTF-8?q?feat:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D1=87=D0=B8=D0=BA=20=D1=83=D0=B2=D0=B5=D0=B4=D0=BE=D0=BC=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=BE=D1=82=20=D0=AE=D0=BA=D0=B0?= =?UTF-8?q?=D1=81=D1=81=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/handlers/webhook_hl.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/handlers/webhook_hl.py diff --git a/src/handlers/webhook_hl.py b/src/handlers/webhook_hl.py new file mode 100644 index 0000000..f7a787c --- /dev/null +++ b/src/handlers/webhook_hl.py @@ -0,0 +1,32 @@ +from telegram import InlineKeyboardButton, InlineKeyboardMarkup +from telegram.ext import TypeHandler, ContextTypes +from yookassa.domain.notification import WebhookNotification + +from settings.settings import CHAT_ID_MIKIEREMIKI + + +async def webhook_update(update: WebhookNotification, + context: ContextTypes.DEFAULT_TYPE): + await context.bot.send_message(CHAT_ID_MIKIEREMIKI, update.json()) + if update.object.status == 'pending': + await context.bot.send_message(CHAT_ID_MIKIEREMIKI, + 'Платеж ожидает оплаты') + if update.object.status == 'waiting_for_capture': + pass # Для двух стадийной оплаты + if update.object.status == 'succeeded': + message_id = update.object.metadata['message_id'] + chat_id = update.object.metadata['chat_id'] + + await context.bot.edit_message_reply_markup(chat_id, message_id) + + text = 'Платеж успешно обработан\nНажмите далее' + reply_markup = InlineKeyboardMarkup( + [[InlineKeyboardButton('Далее', callback_data='Next')]]) + await context.bot.send_message(chat_id=chat_id, + text=text, + reply_markup=reply_markup) + if update.object.status == 'canceled': + pass + + +WebhookHandler = TypeHandler(WebhookNotification, webhook_update) From 5d12dd5a21f9eb1f4dbcc2444a7909dc66efe919 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 21:45:17 +0300 Subject: [PATCH 34/71] =?UTF-8?q?feat:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=B4=D0=BB=D1=8F=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D1=81=D0=BB=D0=BE=D0=B2=D0=B0=D1=80=D1=8F?= =?UTF-8?q?=20=D1=81=20=D0=BF=D0=B0=D1=80=D0=B0=D0=BC=D0=B5=D1=82=D1=80?= =?UTF-8?q?=D0=B0=D0=BC=D0=B8=20=D0=B4=D0=BB=D1=8F=20=D1=81=D0=BE=D0=B7?= =?UTF-8?q?=D0=B4=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=BF=D0=BB=D0=B0=D1=82=D0=B5?= =?UTF-8?q?=D0=B6=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/yookassa_connect.py | 42 +++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/api/yookassa_connect.py diff --git a/src/api/yookassa_connect.py b/src/api/yookassa_connect.py new file mode 100644 index 0000000..89ac253 --- /dev/null +++ b/src/api/yookassa_connect.py @@ -0,0 +1,42 @@ +from yookassa.domain import models +from settings.settings import URL_BOT + + +def create_param_payment( + price: [str, int], + description: str, + return_url: str = URL_BOT, + *, + quantity: str = "1", + payment_method_type: str = "sbp", # "yoo_money" + payment_mode: str = "full_payment", + payment_subject: str = "service", + **kwargs, +) -> dict: + return { + "amount": models.Amount(value=price, currency="RUB"), + "payment_method_data": { + "type": payment_method_type + }, + "confirmation": { + "type": "redirect", + "return_url": return_url + }, + "capture": True, + 'receipt': { + "customer": { + 'email': 'mr@eremin-ma.ru' + }, + "items": [ + { + "description": description, + "quantity": quantity, + "amount": models.Amount(value=price, currency="RUB"), + "vat_code": "1", + "payment_mode": payment_mode, + "payment_subject": payment_subject, + } + ] + }, + 'metadata': kwargs + } From 3c679221ad28b67f48c8a683f2d71c3486855ba9 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 21:46:01 +0300 Subject: [PATCH 35/71] =?UTF-8?q?refactor:=20=D0=A3=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D0=BD=20=D0=BB=D0=B8=D1=88=D0=BD=D0=B8=D0=B9=20=D0=B8=D0=BC?= =?UTF-8?q?=D0=BF=D0=BE=D1=80=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utilities/utl_func.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/utilities/utl_func.py b/src/utilities/utl_func.py index 45e8908..89918ab 100644 --- a/src/utilities/utl_func.py +++ b/src/utilities/utl_func.py @@ -12,7 +12,6 @@ InlineKeyboardButton, InlineKeyboardMarkup, constants, ) -from telegram.constants import ParseMode from telegram.ext import ( ContextTypes, ExtBot, From 2c4961991349ba127dcc9dd586006e7b2ddb8545 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 21:47:09 +0300 Subject: [PATCH 36/71] =?UTF-8?q?refactor:=20=D0=94=D0=BE=D1=80=D0=B0?= =?UTF-8?q?=D0=B1=D0=BE=D1=82=D0=B0=D0=BD=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA?= =?UTF-8?q?=D1=86=D0=B8=D1=8F=20=D0=B4=D0=BB=D1=8F=20=D0=BE=D1=87=D0=B8?= =?UTF-8?q?=D1=81=D1=82=D0=BA=D0=B8=20=D0=BA=D0=BE=D0=BD=D1=82=D0=B5=D0=BA?= =?UTF-8?q?=D1=81=D1=82=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Сначала проверяем существует ли ключ, и если да то удаляем его --- src/utilities/utl_func.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/utilities/utl_func.py b/src/utilities/utl_func.py index 89918ab..79402ba 100644 --- a/src/utilities/utl_func.py +++ b/src/utilities/utl_func.py @@ -79,20 +79,21 @@ async def echo(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: async def clean_context_on_end_handler(logger, context): - logger.info( - f'Обработчик завершился на этапе {context.user_data['STATE']}') + if context.user_data.get('STATE', False): + logger.info( + f'Обработчик завершился на этапе {context.user_data['STATE']}') + context.user_data.pop('STATE') + else: + logger.info('STATE не задан') + + if context.user_data.get('command', False): + context.user_data.pop('command') if context.user_data.get('common_data', False): - context.user_data['common_data'].clear() + context.user_data.pop('common_data') if context.user_data.get('birthday_user_data', False): - context.user_data['birthday_user_data'].clear() + context.user_data.pop('birthday_user_data') if context.user_data.get('reserve_user_data', False): - context.user_data['reserve_user_data'].clear() - context.user_data.pop('STATE') - context.user_data.pop('command') - if context.user_data.get('STATE', False): - context.user_data['STATE'].clear() - if context.user_data.get('command', False): - context.user_data['command'].clear() + context.user_data.pop('reserve_user_data') async def delete_message_for_job_in_callback( From 2534e8c6d64594db003885dc269dcedd625400ed Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 21:47:50 +0300 Subject: [PATCH 37/71] =?UTF-8?q?feat:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=B4=D0=BB=D1=8F=20=D0=BF=D1=80=D0=BE=D1=81=D1=82?= =?UTF-8?q?=D0=BE=D0=B9=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA=D0=B8?= =?UTF-8?q?=20=D0=BD=D0=B0=20=D0=BA=D0=BE=D1=80=D1=80=D0=B5=D0=BA=D1=82?= =?UTF-8?q?=D0=BD=D0=BE=D1=81=D1=82=D1=8C=20email?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utilities/utl_func.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/utilities/utl_func.py b/src/utilities/utl_func.py index 79402ba..02d1f8b 100644 --- a/src/utilities/utl_func.py +++ b/src/utilities/utl_func.py @@ -268,6 +268,10 @@ def extract_phone_number_from_text(phone): return re.sub(r'^[78]{,2}(?=9)', '', phone) +def check_email(email): + return re.fullmatch(r'[^@]+@[^@]+\.[^@]+', email) + + def yrange(n): i = 0 while i < n: From 1a2dd9bdee1c8a5d4df1ea2d97849847f029fa7a Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 21:51:42 +0300 Subject: [PATCH 38/71] =?UTF-8?q?feat:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20=D1=81=D0=BB=D0=BE=D0=B2=D0=B0=D1=80=D1=8C?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20?= =?UTF-8?q?=D1=81=20=D0=B0=D1=82=D1=80=D0=B8=D0=B1=D1=83=D1=82=D0=B0=D0=BC?= =?UTF-8?q?=D0=B8=20=D1=81=20=D0=B0=D1=82=D1=80=D0=B8=D0=B1=D1=83=D1=82?= =?UTF-8?q?=D0=B0=D0=BC=D0=B8=20=D1=81=D0=BE=D0=B1=D1=8B=D1=82=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utilities/schemas/theater_event.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/utilities/schemas/theater_event.py b/src/utilities/schemas/theater_event.py index 1e6175c..fcd349c 100644 --- a/src/utilities/schemas/theater_event.py +++ b/src/utilities/schemas/theater_event.py @@ -58,6 +58,20 @@ class TheaterEvent(BaseModel): qty_adult_nonconfirm_seat: int +kv_name_attr_theater_event = { + 'name': 'Название спектакля', + 'min_age_child': 'Мин возраст', + 'max_age_child': 'Макс возраст', + 'show_emoji': 'Эмодзи', + 'flag_premier': 'Премьера', + 'flag_active_repertoire': 'В репертуаре', + 'flag_active_bd': 'День рождения', + 'max_num_child_bd': 'Макс кол-во детей ДР', + 'max_num_adult_bd': 'Макс кол-во взрослых ДР', + 'flag_indiv_cost': 'Индив стоимость', + 'price_type': 'Расчет стоимости', +} + if __name__ == '__main__': a = TheaterPerformance( show_id=1, From 3a9efecbd02cfba3b58289b3dddab612db718862 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 21:54:39 +0300 Subject: [PATCH 39/71] =?UTF-8?q?feat:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D1=87=D0=B8=D0=BA=D0=B8=20=D0=B4=D0=BB=D1=8F=20=D0=BD?= =?UTF-8?q?=D0=B0=D1=81=D1=82=D1=80=D0=BE=D0=B9=D0=BA=D0=B8=20=D0=B1=D0=B0?= =?UTF-8?q?=D0=B7=D1=8B=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D1=85=20(=D1=81?= =?UTF-8?q?=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8=D0=B5=20=D1=8D=D0=BB=D0=B5?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D1=82=D0=BE=D0=B2=20=D0=B2=20=D1=80=D0=B5?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D1=82=D1=83=D0=B0=D1=80=D0=B5=20=D0=B8=20?= =?UTF-8?q?=D1=80=D0=B0=D1=81=D0=BF=D0=B8=D1=81=D0=B0=D0=BD=D0=B8=D0=B8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/conv_hl/support_conv_hl.py | 61 +++++ src/handlers/support_hl.py | 395 +++++++++++++++++++++++++++++++++ 2 files changed, 456 insertions(+) create mode 100644 src/conv_hl/support_conv_hl.py create mode 100644 src/handlers/support_hl.py diff --git a/src/conv_hl/support_conv_hl.py b/src/conv_hl/support_conv_hl.py new file mode 100644 index 0000000..9e9c3c2 --- /dev/null +++ b/src/conv_hl/support_conv_hl.py @@ -0,0 +1,61 @@ +from typing import Dict, List + +from telegram.ext import ( + BaseHandler, + ConversationHandler, CommandHandler, CallbackQueryHandler, MessageHandler, +) + +from handlers import support_hl, main_hl +from conv_hl import F_text_and_no_command, cancel_callback_handler +from settings.settings import COMMAND_DICT, RESERVE_TIMEOUT + +states: Dict[object, List[BaseHandler]] = { + 1: [ + cancel_callback_handler, + CallbackQueryHandler(support_hl.choice_db_settings), + ], + 2: [ + cancel_callback_handler, + CallbackQueryHandler(support_hl.theater_event_settings, + '^theater_event'), + CallbackQueryHandler(support_hl.schedule_event_settings, + '^schedule_event'), + ], + 3: [ + cancel_callback_handler, + CallbackQueryHandler(support_hl.theater_event_select, + '^theater_event_select$'), + CallbackQueryHandler(support_hl.theater_event_preview, + '^theater_event_create$'), + CallbackQueryHandler(support_hl.schedule_event_select, + '^schedule_event_select$'), + CallbackQueryHandler(support_hl.schedule_event_preview, + '^schedule_event_create$'), + ], + 41: [ + cancel_callback_handler, + MessageHandler(F_text_and_no_command, support_hl.theater_event_check), + CallbackQueryHandler(support_hl.theater_event_create), + ], + 42: [ + cancel_callback_handler, + MessageHandler(F_text_and_no_command, support_hl.schedule_event_check), + CallbackQueryHandler(support_hl.schedule_event_create), + ], +} + +for key in states.keys(): + states[key].append(CommandHandler('reset', main_hl.reset)) +states[ConversationHandler.TIMEOUT] = [support_hl.TIMEOUT_HANDLER] + +support_conv_hl = ConversationHandler( + entry_points=[ + CommandHandler('settings', support_hl.start_settings), + ], + states=states, + fallbacks=[CommandHandler('help', main_hl.help_command)], + conversation_timeout=RESERVE_TIMEOUT * 60, + name='support', + persistent=True, + allow_reentry=True +) diff --git a/src/handlers/support_hl.py b/src/handlers/support_hl.py new file mode 100644 index 0000000..f649b11 --- /dev/null +++ b/src/handlers/support_hl.py @@ -0,0 +1,395 @@ +import logging + +from telegram import Update, InlineKeyboardMarkup, InlineKeyboardButton +from telegram.ext import ContextTypes, TypeHandler, ConversationHandler + +from db import db_postgres +from db.enum import PriceType, TicketPriceType +from settings.settings import RESERVE_TIMEOUT +from utilities.schemas.schedule_event import kv_name_attr_schedule_event +from utilities.schemas.theater_event import kv_name_attr_theater_event +from utilities.utl_func import add_btn_back_and_cancel + +support_hl_logger = logging.getLogger('bot.support_hl') + + +def create_keyboard_crud(name: str): + button_create = InlineKeyboardButton(text='Добавить', + callback_data=f'{name}_event_create') + button_update = InlineKeyboardButton(text='Изменить', + callback_data=f'{name}_event_update') + button_delete = InlineKeyboardButton(text='Удалить', + callback_data=f'{name}_event_delete') + button_select = InlineKeyboardButton(text='Посмотреть', + callback_data=f'{name}_event_select') + button_cancel = add_btn_back_and_cancel(postfix_for_cancel='settings', + add_back_btn=False) + keyboard = [ + [button_create, ], + [button_update, ], + [button_delete, ], + [button_select, ], + [*button_cancel, ], + ] + + return InlineKeyboardMarkup(keyboard) + + +def create_keyboard_confirm(): + button_accept = InlineKeyboardButton(text='Подтвердить', + callback_data='accept') + button_cancel = add_btn_back_and_cancel(postfix_for_cancel='settings', + add_back_btn=False) + keyboard = [ + [button_accept, ], + [*button_cancel, ], + ] + + return InlineKeyboardMarkup(keyboard) + + +def get_validated_data(string, option): + query = string.split('\n') + data = {} + for kv in query: + key, value = kv.split('=') + validated_value = validate_value(value, option) + for k, v in kv_name_attr_schedule_event.items(): + if key == v: + data[k] = validated_value + return data + + +async def start_settings(update: Update, context: ContextTypes.DEFAULT_TYPE): + button_db = InlineKeyboardButton(text='База данных', callback_data='db') + button_cancel = add_btn_back_and_cancel(postfix_for_cancel='settings', + add_back_btn=False) + keyboard = [ + [button_db, ], + [*button_cancel, ], + ] + + reply_markup = InlineKeyboardMarkup(keyboard) + + text = 'Выберите что хотите настроить' + await update.effective_chat.send_message( + text=text, + reply_markup=reply_markup + ) + + return 1 + + +async def choice_db_settings( + update: Update, + context: ContextTypes.DEFAULT_TYPE +): + query = update.callback_query + await query.answer() + + button_event = InlineKeyboardButton(text='Репертуар', + callback_data='theater_event') + button_schedule = InlineKeyboardButton(text='Расписание', + callback_data='schedule_event') + button_cancel = add_btn_back_and_cancel(postfix_for_cancel='settings', + add_back_btn=False) + keyboard = [ + [button_event, button_schedule], + [*button_cancel, ], + ] + + reply_markup = InlineKeyboardMarkup(keyboard) + + text = 'Выберите что хотите настроить' + await query.edit_message_text( + text=text, + reply_markup=reply_markup + ) + + return 2 + + +async def theater_event_settings( + update: Update, + context: ContextTypes.DEFAULT_TYPE +): + query = update.callback_query + await query.answer() + + reply_markup = create_keyboard_crud('theater') + + text = 'Выберите что хотите настроить' + await query.edit_message_text( + text=text, + reply_markup=reply_markup + ) + + context.user_data['reply_markup'] = reply_markup + return 3 + + +async def schedule_event_settings( + update: Update, + context: ContextTypes.DEFAULT_TYPE +): + query = update.callback_query + await query.answer() + + reply_markup = create_keyboard_crud('schedule') + + text = 'Выберите что хотите настроить' + await query.edit_message_text( + text=text, + reply_markup=reply_markup + ) + + context.user_data['reply_markup'] = reply_markup + return 3 + + +async def theater_event_select( + update: Update, + context: ContextTypes.DEFAULT_TYPE +): + query = update.callback_query + await query.answer() + + res = await db_postgres.get_all_theater_events(context.session) + text = '' + for row in res: + text += str(row[0]) + '\n' + + reply_markup = context.user_data['reply_markup'] + await query.edit_message_text(text, + reply_markup=reply_markup) + return 3 + + +async def schedule_event_select( + update: Update, + context: ContextTypes.DEFAULT_TYPE +): + query = update.callback_query + await query.answer() + + res = await db_postgres.get_all_schedule_events(context.session) + text = '' + for row in res: + text += str(row[0]) + '\n' + + reply_markup = context.user_data['reply_markup'] + await query.edit_message_text(text, + reply_markup=reply_markup) + return 3 + + +async def theater_event_preview( + update: Update, + context: ContextTypes.DEFAULT_TYPE +): + query = update.callback_query + await query.answer() + + text = (f'{kv_name_attr_theater_event['name']}=Название\n' + f'{kv_name_attr_theater_event['min_age_child']}=1\n' + f'{kv_name_attr_theater_event['max_age_child']}=0\n' + f'{kv_name_attr_theater_event['show_emoji']}=\n' + f'{kv_name_attr_theater_event['flag_premier']}=Нет\n' + f'{kv_name_attr_theater_event['flag_active_repertoire']}=Да\n' + f'{kv_name_attr_theater_event['flag_active_bd']}=Нет\n' + f'{kv_name_attr_theater_event['max_num_child_bd']}=8\n' + f'{kv_name_attr_theater_event['max_num_adult_bd']}=10\n' + f'{kv_name_attr_theater_event['flag_indiv_cost']}=Нет\n' + f'{kv_name_attr_theater_event['price_type']}=По умолчанию') + await query.edit_message_text(text) + + return 41 + + +async def schedule_event_preview( + update: Update, + context: ContextTypes.DEFAULT_TYPE +): + query = update.callback_query + await query.answer() + + text = (f'{kv_name_attr_schedule_event['type_event_id']}=\n' + f'{kv_name_attr_schedule_event['theater_events_id']}=\n' + f'{kv_name_attr_schedule_event['flag_turn_in_bot']}=Нет\n' + f'{kv_name_attr_schedule_event['datetime_event']}=2024-01-01T00:00 +3\n' + f'{kv_name_attr_schedule_event['qty_child']}=8\n' + f'{kv_name_attr_schedule_event['qty_adult']}=10\n' + f'{kv_name_attr_schedule_event['flag_gift']}=Нет\n' + f'{kv_name_attr_schedule_event['flag_christmas_tree']}=Нет\n' + f'{kv_name_attr_schedule_event['flag_santa']}=Нет\n' + f'{kv_name_attr_schedule_event['ticket_price_type']}=По умолчанию/будни/выходные\n') + await query.edit_message_text(text) + + return 42 + + +def validate_value(value, option): + if value == 'Да': + value = True + if value == 'Нет': + value = False + if option == 'theater': + if value == 'По умолчанию': + value = PriceType.NONE + if value == 'Базовая стоимость': + value = PriceType.BASE_PRICE + if value == 'Опции': + value = PriceType.OPTIONS + if value == 'Индивидуальная': + value = PriceType.INDIVIDUAL + if option == 'schedule': + if value == 'По умолчанию': + value = TicketPriceType.NONE + if value == 'будни': + value = TicketPriceType.weekday + if value == 'выходные': + value = TicketPriceType.weekend + + return value + + +async def theater_event_check( + update: Update, + context: ContextTypes.DEFAULT_TYPE +): + await update.effective_chat.send_message( + 'Проверьте и отправьте текст еще раз или нажмите подтвердить') + + reply_markup = create_keyboard_confirm() + + text = update.effective_message.text + await update.effective_chat.send_message(text, reply_markup=reply_markup) + + context.user_data['theater_event'] = get_validated_data(text, 'theater') + return 41 + + +async def schedule_event_check( + update: Update, + context: ContextTypes.DEFAULT_TYPE +): + await update.effective_chat.send_message( + 'Проверьте и отправьте текст еще раз или нажмите подтвердить') + + reply_markup = create_keyboard_confirm() + + text = update.effective_message.text + await update.effective_chat.send_message(text, reply_markup=reply_markup) + + context.user_data['schedule_event'] = get_validated_data(text, 'schedule') + return 42 + + +async def theater_event_create( + update: Update, + context: ContextTypes.DEFAULT_TYPE +): + query = update.callback_query + await query.answer() + + theater_event = context.user_data['theater_event'] + reply_markup = context.user_data['reply_markup'] + + res = await db_postgres.create_theater_event( + context.session, + name=theater_event['name'], + min_age_child=theater_event['min_age_child'], + max_age_child=theater_event['max_age_child'], + show_emoji=theater_event['show_emoji'], + flag_premier=theater_event['flag_premier'], + flag_active_repertoire=theater_event['flag_active_repertoire'], + flag_active_bd=theater_event['flag_active_bd'], + max_num_child_bd=theater_event['max_num_child_bd'], + max_num_adult_bd=theater_event['max_num_adult_bd'], + flag_indiv_cost=theater_event['flag_indiv_cost'], + price_type=theater_event['price_type'], + ) + + context.user_data.pop('theater_event') + if res: + await query.edit_message_text( + text=f'{theater_event['name']}\nУспешно добавлено', + reply_markup=reply_markup + ) + return 3 + else: + await query.edit_message_text( + 'Попробуйте еще раз или обратитесь в тех поддержку') + return 41 + + +async def schedule_event_create( + update: Update, + context: ContextTypes.DEFAULT_TYPE +): + query = update.callback_query + await query.answer() + + schedule_event = context.user_data['schedule_event'] + reply_markup = context.user_data['reply_markup'] + + res = await db_postgres.create_schedule_event( + context.session, + type_event_id=schedule_event['type_event_id'], + theater_events_id=schedule_event['theater_events_id'], + flag_turn_in_bot=schedule_event['flag_turn_in_bot'], + datetime_event=schedule_event['datetime_event'], + qty_child=schedule_event['qty_child'], + qty_child_free_seat=schedule_event['qty_child'], + qty_adult=schedule_event['qty_adult'], + qty_adult_free_seat=schedule_event['qty_adult'], + flag_gift=schedule_event['flag_gift'], + flag_christmas_tree=schedule_event['flag_christmas_tree'], + flag_santa=schedule_event['flag_santa'], + ticket_price_type=schedule_event['ticket_price_type'], + ) + + context.user_data.pop('schedule_event') + if res: + res = await db_postgres.get_theater_event( + context.session, + schedule_event['theater_events_id']) + await query.edit_message_text( + text=f'{res.name}\nУспешно добавлено', + reply_markup=reply_markup + ) + return 3 + else: + await query.edit_message_text( + 'Попробуйте еще раз или обратитесь в тех поддержку') + return 42 + + +async def conversation_timeout( + update: Update, + context: ContextTypes.DEFAULT_TYPE +) -> int: + """Informs the user that the operation has timed out, + calls :meth:`remove_reply_markup` and ends the conversation. + :return: + int: :attr:`telegram.ext.ConversationHandler.END`. + """ + user = context.user_data['user'] + + await update.effective_chat.send_message( + 'От Вас долго не было ответа, пожалуйста выполните новый запрос', + message_thread_id=update.effective_message.message_thread_id + ) + + support_hl_logger.info(": ".join( + [ + 'Пользователь', + f'{user}', + f'AFK уже {RESERVE_TIMEOUT} мин' + ] + )) + + return ConversationHandler.END + + +TIMEOUT_HANDLER = TypeHandler(Update, conversation_timeout) From 9de8aa0e332439b5ff513fac8f56c6920b82b453 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 21:55:40 +0300 Subject: [PATCH 40/71] =?UTF-8?q?refactor:=20=D0=98=D0=B7=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D1=87=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BC=D0=B5=D1=82=D0=BE?= =?UTF-8?q?=D0=B4=D0=BE=D0=B2=20=D0=B4=D0=BB=D1=8F=20=D1=81=D0=BE=D0=BA?= =?UTF-8?q?=D1=80=D0=B0=D1=89=D0=B5=D0=BD=D0=B8=D1=8F=20=D1=80=D0=B0=D0=B7?= =?UTF-8?q?=D0=BC=D0=B5=D1=80=D0=B0=20=D1=85=D1=8D=D0=BD=D0=B4=D0=BB=D0=B5?= =?UTF-8?q?=D1=80=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/handlers/sub_hl.py | 89 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) diff --git a/src/handlers/sub_hl.py b/src/handlers/sub_hl.py index a03723f..5025557 100644 --- a/src/handlers/sub_hl.py +++ b/src/handlers/sub_hl.py @@ -3,11 +3,14 @@ from telegram import Update, ReplyKeyboardRemove from telegram.ext import ContextTypes -from settings.settings import SUPPORT_DATA +from db import db_postgres +from settings.settings import SUPPORT_DATA, ADMIN_GROUP from db.db_googlesheets import ( load_ticket_data, load_list_show, load_special_ticket_price) from api.googlesheets import get_quality_of_seats, write_data_for_reserve +from utilities.hlp_func import create_approve_and_reject_replay from utilities.schemas.ticket import BaseTicket +from utilities.utl_func import send_message_to_admin sub_hl_logger = logging.getLogger('bot.sub_hl') @@ -237,3 +240,87 @@ async def get_emoji_and_options_for_event(event, name_column=None): if event[name_column['flag_santa']] == 'TRUE': text_emoji += f'{SUPPORT_DATA['Дед'][0]}' return option, text_emoji + + +async def send_breaf_message(update: Update): + """ + Сообщение для опроса + :param update: обновление от telegram + :return: None + """ + text_brief = ( + 'Для подтверждения брони заполните, пожалуйста, анкету.\n' + 'Вход на мероприятие ведется по спискам.\n' + '__________\n' + 'Пожалуйста, не пишите лишней информации/дополнительных слов в ' + 'сообщении.\n' + 'Вопросы будут приходить последовательно (их будет всего 3)' + ) + await update.effective_chat.send_message( + text=text_brief, + ) + await update.effective_chat.send_message( + 'Напишите фамилию и имя (взрослого)', + ) + + +async def send_approve_reject_message_to_admin( + update: Update, + context: ContextTypes.DEFAULT_TYPE +): + # Сообщение для администратора + user = update.effective_user + message_id = context.user_data['common_data']['message_id_buy_info'] + text = context.user_data['common_data']['text_for_notification_massage'] + thread_id = (context.bot_data['dict_topics_name'] + .get('Бронирование спектаклей', None)) + res = await context.bot.send_message( + chat_id=ADMIN_GROUP, + text=f'#Бронирование\n' + f'Квитанция пользователя @{user.username} {user.full_name}\n', + message_thread_id=thread_id + ) + await update.effective_message.forward( + chat_id=ADMIN_GROUP, + message_thread_id=thread_id + ) + message_id_for_admin = res.message_id + await send_message_to_admin(ADMIN_GROUP, + text, + message_id_for_admin, + context, + thread_id) + reply_markup = create_approve_and_reject_replay( + 'reserve', + update.effective_user.id, + message_id, + ) + chose_price = context.user_data['reserve_user_data']['chose_price'] + message = await context.bot.send_message( + chat_id=ADMIN_GROUP, + text=f'Пользователь @{user.username} {user.full_name}\n' + f'Запросил подтверждение брони на сумму {chose_price} руб\n' + f'Ждем заполнения анкеты, если всё хорошо, то только после ' + f'нажимаем подтвердить', + reply_markup=reply_markup, + message_thread_id=thread_id + ) + return message + + +async def remove_button_from_last_message(update, context): + # Убираем у старого сообщения кнопки + message_id = context.user_data['common_data']['message_id_buy_info'] + await context.bot.edit_message_reply_markup( + chat_id=update.effective_chat.id, + message_id=message_id + ) + + +async def update_ticket_status(context, new_status): + ticket_id = context.user_data['reserve_admin_data']['payment_data']['ticket_id'] + await db_postgres.update_ticket( + context.session, + ticket_id, + status=new_status, + ) From 4313a5ac7fa189c424f5510abd75d44d8ecefc07 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 21:56:16 +0300 Subject: [PATCH 41/71] =?UTF-8?q?refactor:=20=D0=94=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA=D1=86?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D1=8F=D1=8E?= =?UTF-8?q?=D1=89=D0=B0=D1=8F=20=D0=B1=D0=BE=D0=BB=D1=8C=D1=88=D1=83=D1=8E?= =?UTF-8?q?=20=D1=87=D0=B0=D1=81=D1=82=D1=8C=20=D0=BE=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D0=B1=D0=BE=D1=82=D1=87=D0=B8=D0=BA=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/handlers/set_handlers.py | 102 +++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 src/handlers/set_handlers.py diff --git a/src/handlers/set_handlers.py b/src/handlers/set_handlers.py new file mode 100644 index 0000000..be264bc --- /dev/null +++ b/src/handlers/set_handlers.py @@ -0,0 +1,102 @@ +import logging + +from telegram.ext import ( + CommandHandler, CallbackQueryHandler, MessageHandler, filters, +) + +from db import middleware_db_add_handlers +from handlers.webhook_hl import WebhookHandler +from handlers import main_hl +from handlers.error_hl import error_handler +from handlers.timeweb_hl import get_balance +from handlers.sub_hl import ( + update_ticket_data, update_show_data, update_admin_info, update_bd_price, + update_special_ticket_price +) +from conv_hl import ( + reserve_conv_hl, reserve_admin_conv_hl, list_wait_conv_hl, birthday_conv_hl, + birthday_paid_conv_hl, afisha_conv_hl, support_conv_hl +) +from utilities.utl_func import ( + echo, send_log, + get_location, get_contact, request_contact_location, + print_ud, clean_ud, clean_bd, + create_or_connect_topic, del_topic, +) +from settings.settings import ADMIN_ID, COMMAND_DICT + +set_handlers_logger = logging.getLogger('bot.set_handlers') + + +def set_handlers(application, config): + middleware_db_add_handlers(application, config) + + application.add_handlers([ + CommandHandler(COMMAND_DICT['START'][0], main_hl.start), + CommandHandler('reset', main_hl.reset), + CommandHandler('send', main_hl.send_approve_msg), + CommandHandler('echo', echo), + ]) + + application.add_handlers([ + CallbackQueryHandler(main_hl.confirm_reserve, '^confirm-reserve'), + CallbackQueryHandler(main_hl.reject_reserve, '^reject-reserve'), + CallbackQueryHandler(main_hl.confirm_birthday, '^confirm-birthday'), + CallbackQueryHandler(main_hl.reject_birthday, '^reject-birthday'), + ]) + + conversation_handlers = [ + reserve_conv_hl, + reserve_admin_conv_hl, + list_wait_conv_hl, + birthday_conv_hl, + birthday_paid_conv_hl, + afisha_conv_hl, + support_conv_hl, + ] + application.add_handlers(conversation_handlers) + + filter_admin = filters.User(ADMIN_ID) + application.add_handlers([ + CommandHandler('clean_ud', clean_ud, filter_admin), + CommandHandler('print_ud', print_ud, filter_admin), + CommandHandler('clean_bd', clean_bd, filter_admin), + CommandHandler(COMMAND_DICT['LOG'][0], send_log, filter_admin), + CommandHandler(COMMAND_DICT['CB_TW'][0], get_balance, filter_admin), + CommandHandler(COMMAND_DICT['TOPIC_DEL'][0], del_topic, filter_admin), + CommandHandler(COMMAND_DICT['TOPIC_START'][0], create_or_connect_topic, + filter_admin), + CommandHandler(COMMAND_DICT['UP_T_DATA'][0], update_ticket_data, + filter_admin), + CommandHandler(COMMAND_DICT['UP_S_DATA'][0], update_show_data, + filter_admin), + CommandHandler(COMMAND_DICT['UP_BD_PRICE'][0], update_bd_price, + filter_admin), + CommandHandler(COMMAND_DICT['UP_SPEC_PRICE'][0], + update_special_ticket_price, + filter_admin), + CommandHandler(COMMAND_DICT['ADM_INFO'][0], + update_admin_info, + filter_admin), + ]) + + application.add_handler( + CommandHandler('rcl', request_contact_location)) + application.add_handler(MessageHandler(filters.LOCATION, get_location)) + application.add_handler(MessageHandler(filters.CONTACT, get_contact)) + + application.add_handler(MessageHandler( + filters.ChatType.PRIVATE & (filters.TEXT | + filters.ATTACHMENT | + filters.VIDEO | + filters.PHOTO | + filters.FORWARDED | + filters.Document.IMAGE | + filters.Document.PDF), + main_hl.feedback_send_msg), + ) + application.add_handler(WebhookHandler) + + application.add_error_handler(error_handler) + + set_handlers_logger.info('Всё готово к поллингу') \ No newline at end of file From 69d11634a44781567a31f18c7ac4e0c22a9a7891 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 21:56:33 +0300 Subject: [PATCH 42/71] =?UTF-8?q?feat:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20=D1=81=D0=BB=D0=BE=D0=B2=D0=B0=D1=80=D1=8C?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20?= =?UTF-8?q?=D1=81=20=D0=B0=D1=82=D1=80=D0=B8=D0=B1=D1=83=D1=82=D0=B0=D0=BC?= =?UTF-8?q?=D0=B8=20=D1=81=20=D0=B0=D1=82=D1=80=D0=B8=D0=B1=D1=83=D1=82?= =?UTF-8?q?=D0=B0=D0=BC=D0=B8=20=D1=80=D0=B0=D1=81=D0=BF=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utilities/schemas/schedule_event.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/utilities/schemas/schedule_event.py diff --git a/src/utilities/schemas/schedule_event.py b/src/utilities/schemas/schedule_event.py new file mode 100644 index 0000000..cc27267 --- /dev/null +++ b/src/utilities/schemas/schedule_event.py @@ -0,0 +1,12 @@ +kv_name_attr_schedule_event = { + 'type_event_id': 'id типа мероприятия', + 'theater_events_id': 'id репертуара', + 'flag_turn_in_bot': 'Вкл/Выкл в боте', + 'datetime_event': 'Дата и время', + 'qty_child': 'Кол-во детских мест', + 'qty_adult': 'Кол-во взрослых мест', + 'flag_gift': 'Подарок', + 'flag_christmas_tree': 'Елка', + 'flag_santa': 'Дед Мороз', + 'ticket_price_type': 'Назначение стоимости', +} From 40a5e44370758bf7e073262ba0a429d97598665c Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 21:58:01 +0300 Subject: [PATCH 43/71] =?UTF-8?q?refactor:=20=D0=A0=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=B0=20=D0=BD=D0=B0=D0=B4=20=D1=81=D1=82=D1=80=D1=83?= =?UTF-8?q?=D0=BA=D1=82=D1=83=D1=80=D0=BE=D0=B9=20=D0=B8=D0=BC=D0=BF=D0=BE?= =?UTF-8?q?=D1=80=D1=82=D0=BE=D0=B2.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Убраны лишние элементы. Приведение к единому стилю --- src/handlers/reserve_admin_hl.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/handlers/reserve_admin_hl.py b/src/handlers/reserve_admin_hl.py index ff1c399..e92dda0 100644 --- a/src/handlers/reserve_admin_hl.py +++ b/src/handlers/reserve_admin_hl.py @@ -1,14 +1,13 @@ import logging from telegram import Update, InlineKeyboardMarkup, InlineKeyboardButton -from telegram.constants import ParseMode from telegram.ext import ContextTypes from api.googlesheets import get_quality_of_seats, write_data_for_reserve from db.db_googlesheets import load_show_info, load_list_show from handlers import init_conv_hl_dialog -from handlers.sub_hl import get_chose_ticket_and_price, \ - get_emoji_and_options_for_event +from handlers.sub_hl import ( + get_chose_ticket_and_price, get_emoji_and_options_for_event) from settings.settings import DICT_OF_EMOJI_FOR_BUTTON from utilities.utl_func import add_btn_back_and_cancel, set_back_context From ab615d8fbce48733284309ee4a03406eba1de8c2 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 21:59:32 +0300 Subject: [PATCH 44/71] =?UTF-8?q?refactor:=20=D0=92=D0=BD=D0=B5=D1=81?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BF=D1=80=D0=B0=D0=B2=D0=BE=D0=BA?= =?UTF-8?q?=20=D0=B2=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=83=20c=20payment?= =?UTF-8?q?=5Fdata?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/handlers/reserve_admin_hl.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/handlers/reserve_admin_hl.py b/src/handlers/reserve_admin_hl.py index e92dda0..e74ca6a 100644 --- a/src/handlers/reserve_admin_hl.py +++ b/src/handlers/reserve_admin_hl.py @@ -153,11 +153,9 @@ async def choice_option_of_reserve( flag_indiv_cost = item['flag_indiv_cost'] choose_event_info['flag_indiv_cost'] = flag_indiv_cost - reserve_admin_data: dict = context.user_data['reserve_admin_data'] - payment_id = reserve_admin_data['payment_id'] - reserve_admin_hl_logger.info(f'Бронирование: {payment_id}') - reserve_admin_data[payment_id] = {} - reserve_admin_data[payment_id]['event_id'] = event_id + payment_data = context.user_data['reserve_admin_data']['payment_data'] + reserve_admin_hl_logger.info(f'Бронирование: {payment_data}') + payment_data['event_id'] = event_id state = 'TICKET' context.user_data['STATE'] = state @@ -186,9 +184,9 @@ async def start_forma_info( ) reserve_user_data['chose_price'] = price - payment_id = reserve_admin_data['payment_id'] - reserve_admin_data[payment_id]['chose_ticket'] = chose_ticket - event_id = reserve_admin_data[payment_id]['event_id'] + payment_data = reserve_admin_data['payment_data'] + payment_data['chose_ticket'] = chose_ticket + event_id = payment_data['event_id'] list_of_name_colum = [ 'qty_child_free_seat', From 12e20d2cde6291deab05eb60ebe5c3a4656288f3 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 22:00:05 +0300 Subject: [PATCH 45/71] =?UTF-8?q?refactor:=20=D0=BF=D0=BE=D0=B2=D1=8B?= =?UTF-8?q?=D1=88=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=87=D0=B8=D1=82=D0=B0=D0=B5?= =?UTF-8?q?=D0=BC=D0=BE=D1=81=D1=82=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/conv_hl/reserve_admin_conv_hl.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/conv_hl/reserve_admin_conv_hl.py b/src/conv_hl/reserve_admin_conv_hl.py index 58de8c2..b8b35e0 100644 --- a/src/conv_hl/reserve_admin_conv_hl.py +++ b/src/conv_hl/reserve_admin_conv_hl.py @@ -13,9 +13,8 @@ from conv_hl import base_handlers F_text_and_no_command = filters.TEXT & ~filters.COMMAND -cancel_callback_handler = CallbackQueryHandler(main_hl.cancel, - pattern='^Отменить') -back_callback_handler = CallbackQueryHandler(main_hl.back, pattern='^Назад') +cancel_callback_handler = CallbackQueryHandler(main_hl.cancel, '^Отменить') +back_callback_handler = CallbackQueryHandler(main_hl.back, '^Назад') states: Dict[object, List[BaseHandler]] = { 1: [ cancel_callback_handler, From c0e07e5ee38b7ed530953f93380a5e014bd85d03 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 22:02:36 +0300 Subject: [PATCH 46/71] =?UTF-8?q?build:=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=B7=D0=B0=D0=B2=D0=B8=D1=81=D0=B8?= =?UTF-8?q?=D0=BC=D0=BE=D1=81=D1=82=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- requirements.txt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 97dd22e..95c0ef9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,10 +6,16 @@ google-auth-oauthlib psycopg psycopg-binary pydantic +pydantic_core pydantic-settings pytest -python-telegram-bot[ext]~=20.7 +python-telegram-bot[ext]~=20.8 PyYAML requests sqlalchemy -typing_extensions \ No newline at end of file +typing_extensions +yookassa~=3.0.1 +uvicorn +fastapi +faststream +nats-py==2.6.0 From 4ed8903c741a8cf8f39a2d28c8d92efd5e2d9791 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 22:04:28 +0300 Subject: [PATCH 47/71] =?UTF-8?q?chore:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=B8=D0=BD=D1=84=D0=B0=20=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=20alembic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 417853b..74b5475 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,10 @@ ___ `docker image pull mikieremiki/baby_domik_bot` +Для создания автомиграции +`alembic -c 'config/alembic.ini' revision +--autogenerate -m 'init'` + Для запуска миграции `docker compose --profile migration up -d` @@ -32,6 +36,9 @@ ___ `alembic -c 'config/alembic.ini' downgrade base` +`.\.venv\Scripts\alembic -c 'config/alembic.ini' upgrade +1` +- запуск из PS + Альтернативные варианты: - можно подключится к контейнеру бота и из под него сделать миграции From 9ddca49236d8647c2284e1b3d798103ed68fe479 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 22:05:02 +0300 Subject: [PATCH 48/71] =?UTF-8?q?refactor:=20=D0=94=D0=BE=D1=80=D0=B0?= =?UTF-8?q?=D0=B1=D0=BE=D1=82=D0=B0=D0=BD=D1=8B=20=D0=BC=D0=BE=D0=B4=D0=B5?= =?UTF-8?q?=D0=BB=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/models.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/db/models.py b/src/db/models.py index 100a69b..3127817 100644 --- a/src/db/models.py +++ b/src/db/models.py @@ -30,11 +30,11 @@ class Person(BaseModelTimed): name: Mapped[Optional[str]] age_type: Mapped[AgeType] - user_id: Mapped[int] = mapped_column( - BigInteger, ForeignKey('users.id', ondelete='CASCADE')) + user_id: Mapped[Optional[int]] = mapped_column( + BigInteger, ForeignKey('users.user_id', ondelete='CASCADE')) - children: Mapped[List['Person']] = relationship(lazy='selectin') - adults: Mapped[List['Person']] = relationship(lazy='selectin') + children: Mapped[List['Child']] = relationship(lazy='selectin') + adults: Mapped[List['Adult']] = relationship(lazy='selectin') tickets: Mapped[List['Ticket']] = relationship( back_populates='people', secondary='people_tickets', lazy='selectin') @@ -64,7 +64,7 @@ class UserTicket(BaseModelTimed): __tablename__ = 'users_tickets' user_id: Mapped[int] = mapped_column( - ForeignKey('users.id'), primary_key=True) + ForeignKey('users.user_id'), primary_key=True) ticket_id: Mapped[int] = mapped_column( ForeignKey('tickets.id'), primary_key=True) @@ -89,8 +89,8 @@ class Ticket(BaseModelTimed): status: Mapped[TicketStatus] notes: Mapped[Optional[str]] - payment_id: Mapped[Optional[str]] - idempotency_id: Mapped[Optional[str]] + payment_id: Mapped[Optional[str]] = mapped_column(unique=True) + idempotency_id: Mapped[Optional[str]] = mapped_column(unique=True) schedule_event_id: Mapped[Optional[int]] = mapped_column( ForeignKey('schedule_events.id')) From de0cbdcfc39cbddeecfda4bd423b78a48987b246 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 22:05:33 +0300 Subject: [PATCH 49/71] =?UTF-8?q?refactor:=20=D0=92=D0=BD=D0=B5=D1=81?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BF=D1=80=D0=B0=D0=B2=D0=BE=D0=BA?= =?UTF-8?q?=20=D0=B2=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=83=20c=20payment?= =?UTF-8?q?=5Fdata?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utilities/hlp_func.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/utilities/hlp_func.py b/src/utilities/hlp_func.py index bced920..d4b520e 100644 --- a/src/utilities/hlp_func.py +++ b/src/utilities/hlp_func.py @@ -196,21 +196,20 @@ def check_phone_number(phone): def create_approve_and_reject_replay( callback_name, chat_id, - message_id, - payment_id=None + message_id ): keyboard = [] button_approve = InlineKeyboardButton( "Подтвердить", callback_data=f'confirm-{callback_name}|' - f'{chat_id} {message_id} {payment_id}' + f'{chat_id} {message_id}' ) button_cancel = InlineKeyboardButton( "Отклонить", callback_data=f'reject-{callback_name}|' - f'{chat_id} {message_id} {payment_id}' + f'{chat_id} {message_id}' ) keyboard.append([button_approve, button_cancel]) return InlineKeyboardMarkup(keyboard) From e11501cf07043a41a23d205e6f564d74e194b4a5 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 22:05:46 +0300 Subject: [PATCH 50/71] =?UTF-8?q?refactor:=20=D0=92=D0=BD=D0=B5=D1=81?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BF=D1=80=D0=B0=D0=B2=D0=BE=D0=BA?= =?UTF-8?q?=20=D0=B2=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=83=20c=20payment?= =?UTF-8?q?=5Fdata?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/handlers/error_hl.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/handlers/error_hl.py b/src/handlers/error_hl.py index 483ef32..8782c14 100644 --- a/src/handlers/error_hl.py +++ b/src/handlers/error_hl.py @@ -5,12 +5,10 @@ from pprint import pformat from telegram import Update -from telegram.constants import ParseMode from telegram.ext import ContextTypes from handlers.sub_hl import write_old_seat_info from settings.settings import CHAT_ID_MIKIEREMIKI -from utilities.schemas.ticket import BaseTicket from utilities.utl_func import clean_context, split_message error_hl_logger = logging.getLogger('bot.error_hl') @@ -62,9 +60,7 @@ async def error_handler(update: Update, message_id=context.user_data['common_data']['message_id_buy_info'] ) - reserve_admin_data = context.user_data['reserve_admin_data'] - payment_id = reserve_admin_data['payment_id'] - payment_data = reserve_admin_data[payment_id] + payment_data = context.user_data['reserve_admin_data']['payment_data'] chose_ticket = payment_data['chose_ticket'] event_id = payment_data['event_id'] From d670a793f2a6c5d00ebd640ba440d3311f503ab3 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 22:07:09 +0300 Subject: [PATCH 51/71] =?UTF-8?q?build:=20=D0=92=D1=8B=D0=BD=D0=B5=D1=81?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20cmd=20=D0=B2=20compose=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index d629570..c7579bd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,5 +8,4 @@ WORKDIR /app/src RUN --mount=type=bind,source=requirements.txt,target=requirements.txt \ python -m pip install -r requirements.txt -COPY /src . -CMD python3 -m bot run \ No newline at end of file +COPY /src . \ No newline at end of file From 709ecf8c2789256498777726339b0ab5ba175af3 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 22:08:22 +0300 Subject: [PATCH 52/71] =?UTF-8?q?build:=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BF=D0=BE=D1=80=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/fastapi_nats.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/api/fastapi_nats.py b/src/api/fastapi_nats.py index d649a46..0efa451 100644 --- a/src/api/fastapi_nats.py +++ b/src/api/fastapi_nats.py @@ -40,9 +40,7 @@ async def main(): config=uvicorn.Config( app=app, host='0.0.0.0', - port=443, - ssl_keyfile='server.key', - ssl_certfile='server.crt' + port=8443, ) ) await webserver.serve() From 70584309582fe9f7e5461a751fa6d2797a694897 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 22:14:01 +0300 Subject: [PATCH 53/71] =?UTF-8?q?build:=20=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20compose=20=D1=84=D0=B0=D0=B9?= =?UTF-8?q?=D0=BB=D0=B0=20+=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=BD=D0=BE=D0=B2=D1=8B=D1=85=20=D1=81?= =?UTF-8?q?=D0=B5=D1=80=D0=B2=D0=B8=D1=81=D0=BE=D0=B2:=20nginx=20nats=20na?= =?UTF-8?q?ts-cli=20fastapi=5Fserver?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yaml | 72 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 65 insertions(+), 7 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index a7fcfcc..811c942 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -8,9 +8,9 @@ services: context: . image: baby_domik:v1 restart: always -# depends_on: -# postgres: -# condition: service_healthy + depends_on: + postgres: + condition: service_healthy networks: - network environment: @@ -20,6 +20,8 @@ services: - ./config/settings.py:/app/src/settings/settings.py - ./src/db/data:/app/src/db/data - ./src/log/archive:/app/src/log/archive + command: faststream run bot:fast_stream + postgres: profiles: [ "all", "db" ] container_name: postgres @@ -34,11 +36,11 @@ services: networks: - network healthcheck: - test: [ "CMD", "pg_isready", "-U", "$POSTGRES_USER", "-d", - "$POSTGRES_DB" ] + test: [ "CMD", "pg_isready", "-U", "$POSTGRES_USER", "-d", "$POSTGRES_DB" ] interval: 10s timeout: 5s retries: 5 + migration: profiles: [ "migration" ] container_name: migration @@ -57,8 +59,9 @@ services: environment: - CONFIG_PATH=${CONFIG_PATH:-config/} working_dir: /app - command: [ "alembic", "-c", "config/alembic.ini", "upgrade", "head" ] -# command: [ "alembic", "-c", "config/alembic.ini", "downgrade", "base" ] + command: [ "alembic", "-c", "config/alembic.ini", "upgrade", "+1" ] +# command: [ "alembic", "-c", "config/alembic.ini", "downgrade", "-1" ] + pgadmin: profiles: [ "pgadmin" ] container_name: pgadmin @@ -74,9 +77,64 @@ services: networks: - network + nats: + profiles: [ "nats", "1" ] + container_name: nats + image: nats:2.10.11-alpine + restart: unless-stopped + ports: + - '4222:4222' + - '6222:6222' + - '8222:8222' + volumes: + - nats:/nats + - ./config/server.conf:/etc/nats/server.conf + networks: + - network + command: 'nats-server -c /etc/nats/server.conf' + + nats-cli: + profiles: [ "nats-cli" ] + container_name: nats-cli + image: natsio/nats-box + restart: unless-stopped + networks: + - network + command: [ "tail", "-f", "/dev/null" ] + + fastapi_server: + profiles: [ "fastapi", "1" ] + container_name: fastapi_server + build: + context: . + dockerfile: Dockerfile-server + image: baby_domik_fastapi:v1 + restart: unless-stopped + networks: + - network + depends_on: + - nats + + nginx: + profiles: [ "nginx", "1" ] + container_name: nginx + image: nginx:latest + restart: unless-stopped + ports: + - "443:443" + volumes: + - ./config/nginx.conf:/etc/nginx/nginx.conf + - ./config/server.crt:/etc/nginx/server.crt + - ./config/server.key:/etc/nginx/server.key + networks: + - network + depends_on: + - fastapi_server + volumes: db: pgadmin: + nats: networks: network: From 36bf964b1d8a0943147b5b0ef16e88e528333502 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 22:15:15 +0300 Subject: [PATCH 54/71] =?UTF-8?q?feat:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=B4=D0=BB=D1=8F=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=D0=BA=D0=B8=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2?= =?UTF-8?q?=D0=B0=D1=82=D0=B5=D0=BB=D1=8F=20=D0=B2=20=D0=91=D0=94=20=D0=B8?= =?UTF-8?q?=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8=D0=B8,=20=D0=B5?= =?UTF-8?q?=D1=81=D0=BB=D0=B8=20=D0=BD=D0=B5=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/handlers/__init__.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/handlers/__init__.py b/src/handlers/__init__.py index a6305ea..7de5fd0 100644 --- a/src/handlers/__init__.py +++ b/src/handlers/__init__.py @@ -1,3 +1,6 @@ +import logging + +from db import db_postgres from utilities.utl_func import clean_context, extract_command @@ -16,3 +19,19 @@ def init_conv_hl_dialog(update, context): context.user_data['reserve_admin_data']['payment_data'] = {} return state + +async def check_user_db(update, context): + logger = logging.getLogger(__name__) + res = await db_postgres.get_user(context.session, update.effective_user.id) + if not res: + res = await db_postgres.create_user( + context.session, + update.effective_user.id, + update.effective_chat.id, + username=update.effective_user.username + ) + if res: + logger.info( + f'Пользователь {res} начал общение с ботом') + else: + logger.info('Пользователь уже в есть в базе') From 09e0d1f92a60f3bb68d91538b3e1e10a0fa73850 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 22:16:23 +0300 Subject: [PATCH 55/71] =?UTF-8?q?refactor:=20=D0=92=D1=8B=D0=BD=D0=B5?= =?UTF-8?q?=D1=81=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BE=D0=B1=D1=89=D0=B8=D1=85?= =?UTF-8?q?=20=D1=85=D1=8D=D0=BD=D0=B4=D0=BB=D0=B5=D1=80=D0=BE=D0=B2=20?= =?UTF-8?q?=D0=B2=20=D1=81=D0=B2=D0=BE=D0=B9=20=D1=84=D0=B0=D0=B9=D0=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/conv_hl/__init__.py | 42 +++++++++-------------------------------- 1 file changed, 9 insertions(+), 33 deletions(-) diff --git a/src/conv_hl/__init__.py b/src/conv_hl/__init__.py index c946fba..71049d4 100644 --- a/src/conv_hl/__init__.py +++ b/src/conv_hl/__init__.py @@ -1,33 +1,9 @@ -from warnings import filterwarnings - -from telegram.warnings import PTBUserWarning -from telegram.ext import CallbackQueryHandler - -from handlers import main_hl, reserve_hl - -base_handlers = { - 'MONTH': [ - CallbackQueryHandler(main_hl.cancel, pattern='^Отменить'), - CallbackQueryHandler(reserve_hl.choice_show_or_date), - ], - 'SHOW': [ - CallbackQueryHandler(main_hl.cancel, pattern='^Отменить'), - CallbackQueryHandler(main_hl.back, pattern='^Назад-MONTH'), - CallbackQueryHandler(reserve_hl.choice_date), - ], - 'DATE': [ - CallbackQueryHandler(main_hl.cancel, pattern='^Отменить'), - CallbackQueryHandler(main_hl.back, pattern='^Назад-MONTH'), - CallbackQueryHandler(main_hl.back, pattern='^Назад-SHOW'), - CallbackQueryHandler(reserve_hl.choice_time), - ], - 'TIME': [ - CallbackQueryHandler(main_hl.cancel, pattern='^Отменить'), - CallbackQueryHandler(main_hl.back, pattern='^Назад-DATE'), - CallbackQueryHandler(reserve_hl.choice_option_of_reserve), - ], -} - -filterwarnings(action="ignore", - message=r".*CallbackQueryHandler", - category=PTBUserWarning) +from .common_hl import (base_handlers, F_text_and_no_command, + cancel_callback_handler, back_callback_handler) + +from .reserve_conv_hl import reserve_conv_hl +from .reserve_admin_conv_hl import reserve_admin_conv_hl +from .list_wait_conv_hl import list_wait_conv_hl +from .birthday_conv_hl import birthday_conv_hl, birthday_paid_conv_hl +from .afisha_conv_hl import afisha_conv_hl +from .support_conv_hl import support_conv_hl From a6b5210551c90cc07e002a981398d6433c94f8c9 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 22:16:57 +0300 Subject: [PATCH 56/71] =?UTF-8?q?refactor:=20=D0=94=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BD=D0=BE=D0=B2=D1=8B?= =?UTF-8?q?=D1=85=20=D1=82=D0=B8=D0=BF=D0=BE=D0=B2=20=D0=B2=20=5F=5Finit?= =?UTF-8?q?=5F=5F.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/__init__.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/db/__init__.py b/src/db/__init__.py index f3f2701..d79d2e6 100644 --- a/src/db/__init__.py +++ b/src/db/__init__.py @@ -1,6 +1,7 @@ from .base import BaseModel, BaseModelTimed -from .database import middleware_db_add_handlers -from .enum_types import TicketStatusEnum, TicketPriceTypeEnum, PriceTypeEnum -from .models import User, Child, Ticket, TypeEvent, TheaterEvent, ScheduleEvent +from .database import middleware_db_add_handlers, create_sessionmaker_and_engine +from .enum import (TicketStatusEnum, TicketPriceTypeEnum, PriceTypeEnum, + AgeTypeEnum) +from .models import (User, Person, Child, Ticket, TypeEvent, TheaterEvent, + ScheduleEvent, Adult) from .pickle_persistence import pickle_persistence -from .db_postgres import AsyncORM From ab7975f616813e80c32284ea90d357f83ca0cce6 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 22:17:56 +0300 Subject: [PATCH 57/71] =?UTF-8?q?refactor:=20=D0=9E=D0=BF=D1=82=D0=B8?= =?UTF-8?q?=D0=BC=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20=D0=B8=D0=BC=D0=BF?= =?UTF-8?q?=D0=BE=D1=80=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/handlers/main_hl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/handlers/main_hl.py b/src/handlers/main_hl.py index 52fcab0..93e3ca2 100644 --- a/src/handlers/main_hl.py +++ b/src/handlers/main_hl.py @@ -2,9 +2,10 @@ from telegram.ext import ContextTypes, ConversationHandler from telegram import Update, ReplyKeyboardRemove -from telegram.constants import ParseMode, ChatType +from telegram.constants import ChatType from telegram.error import BadRequest +from handlers import check_user_db from handlers.sub_hl import write_old_seat_info, remove_inline_button from settings.settings import ( COMMAND_DICT, ADMIN_GROUP, FEEDBACK_THREAD_ID_GROUP_ADMIN @@ -16,7 +17,6 @@ is_admin, get_back_context, clean_context, clean_context_on_end_handler, utilites_logger ) -from db import AsyncORM main_handlers_logger = logging.getLogger('bot.main_handlers') From 895232e0e0547762db3efeb23907531a71d60fba Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 22:20:45 +0300 Subject: [PATCH 58/71] =?UTF-8?q?refactor:=20=D0=97=D0=B0=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BE=D0=B4=D0=BD=D0=BE=D0=B9=20=D1=84=D1=83?= =?UTF-8?q?=D0=BD=D0=BA=D1=86=D0=B8=D0=B5=D0=B9,=20=D0=BF=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=B5=D1=80=D0=BA=D0=B8=20=D0=B8=20=D1=81=D0=BE=D0=B7?= =?UTF-8?q?=D0=B4=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/handlers/main_hl.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/handlers/main_hl.py b/src/handlers/main_hl.py index 93e3ca2..8a58e28 100644 --- a/src/handlers/main_hl.py +++ b/src/handlers/main_hl.py @@ -26,13 +26,7 @@ async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): Приветственная команда при первом запуске бота, при перезапуске бота или при использовании команды start """ - if not await AsyncORM.get_user(update, context.session): - res = await AsyncORM.create_user(update, context.session) - if res: - main_handlers_logger.info( - f'Пользователь {res} начал общение с ботом') - else: - main_handlers_logger.info('Пользователь уже в есть в базе') + await check_user_db(update, context) clean_context(context) context.user_data['user'] = update.effective_user From 2d2410f6118e74e84d8d0b6029fcb5c7a7f3dbb9 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 22:21:47 +0300 Subject: [PATCH 59/71] =?UTF-8?q?refactor:=20=D0=92=D0=BD=D0=B5=D1=81?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BF=D1=80=D0=B0=D0=B2=D0=BE=D0=BA?= =?UTF-8?q?=20=D0=B2=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=83=20c=20payment?= =?UTF-8?q?=5Fdata?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/handlers/main_hl.py | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/src/handlers/main_hl.py b/src/handlers/main_hl.py index 8a58e28..f53925f 100644 --- a/src/handlers/main_hl.py +++ b/src/handlers/main_hl.py @@ -65,12 +65,11 @@ async def confirm_reserve(update: Update, context: ContextTypes.DEFAULT_TYPE): query = await remove_inline_button(update) chat_id = query.data.split('|')[1].split()[0] - payment_id = int(query.data.split('|')[1].split()[2]) user_data = context.application.user_data.get(int(chat_id)) user = user_data['user'] try: - payment_data = user_data['reserve_admin_data'][payment_id] + payment_data = user_data['reserve_admin_data']['payment_data'] event_id = payment_data['event_id'] chose_ticket = payment_data['chose_ticket'] @@ -184,12 +183,11 @@ async def reject_reserve(update: Update, context: ContextTypes.DEFAULT_TYPE): query = await remove_inline_button(update) chat_id = query.data.split('|')[1].split()[0] - payment_id = int(query.data.split('|')[1].split()[2]) user_data = context.application.user_data.get(int(chat_id)) user = user_data['user'] try: - payment_data = user_data['reserve_admin_data'][payment_id] + payment_data = user_data['reserve_admin_data']['payment_data'] event_id = payment_data['event_id'] chose_ticket = payment_data['chose_ticket'] @@ -454,17 +452,9 @@ async def cancel(update: Update, context: ContextTypes.DEFAULT_TYPE): ) if '|' in query.data: - chat_id = query.data.split('|')[1].split()[0] - message_id = query.data.split('|')[1].split()[1] - await context.bot.delete_message( - chat_id=chat_id, - message_id=message_id - ) - - reserve_admin_data = context.user_data['reserve_admin_data'] - payment_id = reserve_admin_data['payment_id'] - chose_ticket = reserve_admin_data[payment_id]['chose_ticket'] - event_id = reserve_admin_data[payment_id]['event_id'] + payment_data = context.user_data['reserve_admin_data']['payment_data'] + chose_ticket = payment_data['chose_ticket'] + event_id = payment_data['event_id'] await write_old_seat_info(user, event_id, chose_ticket) case 'bd': From 74675da87a800349fb99479b36e9b37688a2e83e Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 22:24:59 +0300 Subject: [PATCH 60/71] =?UTF-8?q?feat:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=B4=D0=BB=D1=8F=20=D0=BE=D1=82=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=BA=D0=B8=20=D0=BF=D0=BE=D0=B4=D1=82=D0=B2=D0=B5=D1=80?= =?UTF-8?q?=D0=B6=D0=B4=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BE=D1=82=D0=B4=D0=B5?= =?UTF-8?q?=D0=BB=D1=8C=D0=BD=D1=8B=D0=BC=20=D1=85=D1=8D=D0=BD=D0=B4=D0=BB?= =?UTF-8?q?=D0=B5=D1=80=D0=BE=D0=BC=20=D0=B8=20=D0=B2=D1=8B=D0=BD=D0=B5?= =?UTF-8?q?=D1=81=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BE=D0=B1=D1=89=D0=B5=D0=B3?= =?UTF-8?q?=D0=BE=20=D0=BA=D0=BE=D0=B4=D0=B0=20=D0=B2=20=D0=BE=D1=82=D0=B4?= =?UTF-8?q?=D0=B5=D0=BB=D1=8C=D0=BD=D1=83=D1=8E=20=D1=84=D1=83=D0=BD=D0=BA?= =?UTF-8?q?=D1=86=D0=B8=D1=8E=20=D0=BF=D0=BE=20=D0=BE=D1=82=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=BA=D0=B5=20=D0=BF=D0=BE=D0=B4=D1=82=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=D0=B6=D0=B4=D0=B0=D1=8E=D1=89=D0=B5=D0=B3=D0=BE=20=D1=81?= =?UTF-8?q?=D0=BE=D0=BE=D0=B1=D1=89=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/handlers/main_hl.py | 54 ++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/src/handlers/main_hl.py b/src/handlers/main_hl.py index f53925f..c44291a 100644 --- a/src/handlers/main_hl.py +++ b/src/handlers/main_hl.py @@ -52,6 +52,13 @@ async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): ) +async def send_approve_msg(update: Update, context: ContextTypes.DEFAULT_TYPE): + chat_id = context.args[0] + await send_approve_message(chat_id, context) + await update.effective_message.reply_text( + 'Подтверждение успешно отправлено') + + async def confirm_reserve(update: Update, context: ContextTypes.DEFAULT_TYPE): """ Отправляет оповещение о подтверждении в бронировании, удаляет сообщение @@ -128,25 +135,11 @@ async def confirm_reserve(update: Update, context: ContextTypes.DEFAULT_TYPE): chat_id = query.data.split('|')[1].split()[0] message_id = query.data.split('|')[1].split()[1] - text = ( - 'Ваша бронь подтверждена, ждем вас на спектакле.\n' - 'Адрес: Малая Покровская, д18, 2 этаж\n\n' - '❗️ВОЗВРАТ ДЕНЕЖНЫХ СРЕДСТВ ИЛИ ПЕРЕНОС ВОЗМОЖЕН НЕ МЕНЕЕ, ЧЕМ ЗА 24 ЧАСА❗\n' - 'Ссылка ВКонтакте на нашу группу\n' - 'В ней более подробно описаны:\n' - '- Бронь билетов\n' - '- Репертуар\n' - '- Фотографии\n' - '- Команда и жизнь театра\n' - '- Ответы на часто задаваемые вопросы\n' - '- Как нас найти\n\n' - 'Задать любые интересующие вас вопросы вы можете через сообщения группы\n\n' - 'Для продолжения работы используйте команды:\n' - f'/{COMMAND_DICT['RESERVE'][0]} - выбрать и оплатить билет на спектакль ' - ) - await context.bot.send_message( - text=text, - chat_id=chat_id, + await send_approve_message(chat_id, context) + + await query.edit_message_text( + text=f'Пользователю @{user.username} {user.full_name} ' + f'подтверждена бронь' ) # TODO Добавить галочку подтверждения в клиентскую базу @@ -171,6 +164,29 @@ async def confirm_reserve(update: Update, context: ContextTypes.DEFAULT_TYPE): )) +async def send_approve_message(chat_id, context): + text = ( + 'Ваша бронь подтверждена, ждем вас на спектакле.\n' + 'Адрес: Малая Покровская, д18, 2 этаж\n\n' + '❗️ВОЗВРАТ ДЕНЕЖНЫХ СРЕДСТВ ИЛИ ПЕРЕНОС ВОЗМОЖЕН НЕ МЕНЕЕ, ЧЕМ ЗА 24 ЧАСА❗\n' + 'Ссылка ВКонтакте на нашу группу\n' + 'В ней более подробно описаны:\n' + '- Бронь билетов\n' + '- Репертуар\n' + '- Фотографии\n' + '- Команда и жизнь театра\n' + '- Ответы на часто задаваемые вопросы\n' + '- Как нас найти\n\n' + 'Задать любые интересующие вас вопросы вы можете через сообщения группы\n\n' + 'Для продолжения работы используйте команды:\n' + f'/{COMMAND_DICT['RESERVE'][0]} - выбрать и оплатить билет на спектакль ' + ) + await context.bot.send_message( + text=text, + chat_id=chat_id, + ) + + async def reject_reserve(update: Update, context: ContextTypes.DEFAULT_TYPE): """ Отправляет оповещение об отказе в бронировании, удаляет сообщение From 58e80b9793c88ba0241d4b5250cd161f5c7c387c Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 22:27:28 +0300 Subject: [PATCH 61/71] =?UTF-8?q?feat:=20=D0=9F=D0=B5=D1=80=D0=B5=D1=81?= =?UTF-8?q?=D1=82=D0=B0=D0=BD=D0=BE=D0=B2=D0=BA=D0=B0=20try=20=D1=81=20?= =?UTF-8?q?=D0=B7=D0=B0=D1=85=D0=B2=D0=B0=D1=82=D0=BE=D0=BC=20=D1=84=D1=83?= =?UTF-8?q?=D0=BD=D0=BA=D1=86=D0=B8=D0=B8=20=D0=BE=D0=B1=D1=80=D0=B0=D1=89?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BA=20=D0=BA=D0=BE=D0=BB-=D0=B2?= =?UTF-8?q?=D1=83=20=D0=BC=D0=B5=D1=81=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Плюс добавлены доп информационные сообщения --- src/handlers/main_hl.py | 52 ++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/src/handlers/main_hl.py b/src/handlers/main_hl.py index c44291a..fa182dd 100644 --- a/src/handlers/main_hl.py +++ b/src/handlers/main_hl.py @@ -85,23 +85,28 @@ async def confirm_reserve(update: Update, context: ContextTypes.DEFAULT_TYPE): 'qty_child_nonconfirm_seat', 'qty_adult_nonconfirm_seat' ] - (qty_child_nonconfirm_seat_now, - qty_adult_nonconfirm_seat_now - ) = get_quality_of_seats(event_id, - list_of_name_colum) - - qty_child_nonconfirm_seat_new = int( - qty_child_nonconfirm_seat_now) - int(chose_ticket.quality_of_children) - qty_adult_nonconfirm_seat_new = int( - qty_adult_nonconfirm_seat_now) - int(chose_ticket.quality_of_adult + - chose_ticket.quality_of_add_adult) - - numbers = [ - qty_child_nonconfirm_seat_new, - qty_adult_nonconfirm_seat_new - ] try: + (qty_child_nonconfirm_seat_now, + qty_adult_nonconfirm_seat_now + ) = get_quality_of_seats(event_id, + list_of_name_colum) + main_handlers_logger.info('Загружаем кол-во мест') + + qty_child_nonconfirm_seat_new = int( + qty_child_nonconfirm_seat_now) - int(chose_ticket.quality_of_children) + qty_adult_nonconfirm_seat_new = int( + qty_adult_nonconfirm_seat_now) - int(chose_ticket.quality_of_adult + + chose_ticket.quality_of_add_adult) + + numbers = [ + qty_child_nonconfirm_seat_new, + qty_adult_nonconfirm_seat_new + ] write_data_for_reserve(event_id, numbers, 2) + await query.edit_message_text( + text=f'Пользователю @{user.username} {user.full_name} ' + f'списаны неподтвержденные места' + ) main_handlers_logger.info(": ".join( [ @@ -126,12 +131,19 @@ async def confirm_reserve(update: Update, context: ContextTypes.DEFAULT_TYPE): event_id, ] )) + except ConnectionError: + await query.edit_message_text( + text=f'Пользователю @{user.username} {user.full_name} ' + f'не списаны неподтвержденные места\n' + f'Номер строки для обновления: {event_id}\n' + f'user_id {user.id}' + ) await query.edit_message_text( text=f'Пользователю @{user.username} {user.full_name} ' - f'подтверждена бронь' + f'отправляем сообщение о подтверждении бронирования' + f'user_id {user.id}' ) - chat_id = query.data.split('|')[1].split()[0] message_id = query.data.split('|')[1].split()[1] @@ -150,11 +162,13 @@ async def confirm_reserve(update: Update, context: ContextTypes.DEFAULT_TYPE): chat_id=chat_id, message_id=message_id ) - except BadRequest: + except BadRequest as e: + main_handlers_logger.error(e) main_handlers_logger.info( f'Cообщение уже удалено' ) - except BadRequest: + except BadRequest as e: + main_handlers_logger.error(e) main_handlers_logger.info(": ".join( [ 'Пользователь', From 99e245b25c4d6d961704de1db1cba0bf1a5dde97 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 22:27:52 +0300 Subject: [PATCH 62/71] =?UTF-8?q?feat:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=BA=D0=B0=20=D0=BA=D0=BD=D0=BE=D0=BF=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=BE=D1=82=D0=BC=D0=B5=D0=BD=D1=8B=20=D0=B4=D0=BB=D1=8F=20?= =?UTF-8?q?=D0=BD=D0=B0=D1=81=D1=82=D1=80=D0=BE=D0=B5=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/handlers/main_hl.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/handlers/main_hl.py b/src/handlers/main_hl.py index fa182dd..354b76d 100644 --- a/src/handlers/main_hl.py +++ b/src/handlers/main_hl.py @@ -508,6 +508,12 @@ async def cancel(update: Update, context: ContextTypes.DEFAULT_TYPE): f'записи без подтверждения', message_thread_id=query.message.message_thread_id ) + case 'settings': + await query.delete_message() + await update.effective_chat.send_message( + text='Вы выбрали отмену', + message_thread_id=query.message.message_thread_id + ) try: main_handlers_logger.info(f'Для пользователя {user}') From 685aac1c61362df6bbe1cec995126db1cef1c73b Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 22:30:41 +0300 Subject: [PATCH 63/71] =?UTF-8?q?feat:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=B4=D0=BE=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B8=D1=82=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B5=20state=20?= =?UTF-8?q?=D0=B8=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=87=D0=B8?= =?UTF-8?q?=D0=BA=D0=B8=20=D0=B4=D0=BB=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D1=8B=20=D1=81=20=D0=AE=D0=BA=D0=B0=D1=81=D1=81=D0=BE?= =?UTF-8?q?=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/conv_hl/reserve_conv_hl.py | 36 +++++++++++++--------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/src/conv_hl/reserve_conv_hl.py b/src/conv_hl/reserve_conv_hl.py index e9eda6e..8e38bab 100644 --- a/src/conv_hl/reserve_conv_hl.py +++ b/src/conv_hl/reserve_conv_hl.py @@ -7,39 +7,28 @@ ) from handlers import reserve_hl, main_hl +from conv_hl import base_handlers from settings.settings import COMMAND_DICT, RESERVE_TIMEOUT F_text_and_no_command = filters.TEXT & ~filters.COMMAND -cancel_callback_handler = CallbackQueryHandler(main_hl.cancel, - pattern='^Отменить') +cancel_callback_handler = CallbackQueryHandler(main_hl.cancel, '^Отменить') +back_callback_handler = CallbackQueryHandler(main_hl.back, '^Назад') states: Dict[object, List[BaseHandler]] = { - 'MONTH': [ + 'EMAIL': [ cancel_callback_handler, - CallbackQueryHandler(reserve_hl.choice_show_or_date), - ], - 'SHOW': [ - cancel_callback_handler, - CallbackQueryHandler(main_hl.back, pattern='^Назад-MONTH'), - CallbackQueryHandler(reserve_hl.choice_date), - ], - 'DATE': [ - cancel_callback_handler, - CallbackQueryHandler(main_hl.back, pattern='^Назад-MONTH'), - CallbackQueryHandler(main_hl.back, pattern='^Назад-SHOW'), - CallbackQueryHandler(reserve_hl.choice_time), - ], - 'TIME': [ - cancel_callback_handler, - CallbackQueryHandler(main_hl.back, pattern='^Назад-DATE'), - CallbackQueryHandler(reserve_hl.choice_option_of_reserve), + CallbackQueryHandler(main_hl.back, pattern='^Назад-TIME'), + CallbackQueryHandler(reserve_hl.get_email), ], 'ORDER': [ cancel_callback_handler, CallbackQueryHandler(main_hl.back, pattern='^Назад-TIME'), - CallbackQueryHandler(reserve_hl.check_and_send_buy_info), + MessageHandler(F_text_and_no_command, + reserve_hl.check_and_send_buy_info), ], 'PAID': [ cancel_callback_handler, + CallbackQueryHandler(reserve_hl.processing_successful_notification, + pattern='Next'), MessageHandler( filters.PHOTO | filters.ATTACHMENT, reserve_hl.forward_photo_or_file @@ -59,7 +48,7 @@ ], 'LIST': [ cancel_callback_handler, - CallbackQueryHandler(main_hl.back, pattern='^Назад'), + back_callback_handler, CallbackQueryHandler(reserve_hl.send_clients_data), ], 'CHOOSING': [ @@ -78,6 +67,9 @@ ], } +for key in base_handlers.keys(): + states[key] = base_handlers[key] + for key in states.keys(): states[key].append(CommandHandler('reset', main_hl.reset)) states[ConversationHandler.TIMEOUT] = [reserve_hl.TIMEOUT_HANDLER] From daab44acaad21307fce274f2b6176b2bb4884761 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 22:31:51 +0300 Subject: [PATCH 64/71] =?UTF-8?q?refactor:=20=D0=A0=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=B0=20=D0=BD=D0=B0=D0=B4=20=D0=BF=D0=BE=D0=B2=D1=8B?= =?UTF-8?q?=D1=88=D0=B5=D0=BD=D0=B8=D0=B5=D0=BC=20=D1=87=D0=B8=D1=82=D0=B0?= =?UTF-8?q?=D0=B5=D0=BC=D0=BE=D1=81=D1=82=D0=B8=20=D0=B8=D0=BC=D0=BF=D0=BE?= =?UTF-8?q?=D1=80=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/handlers/reserve_hl.py | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/src/handlers/reserve_hl.py b/src/handlers/reserve_hl.py index 0e37781..2c47a38 100644 --- a/src/handlers/reserve_hl.py +++ b/src/handlers/reserve_hl.py @@ -16,38 +16,30 @@ request_phone_number, send_and_del_message_to_remove_kb, write_old_seat_info, get_chose_ticket_and_price, get_emoji_and_options_for_event, + send_breaf_message, send_approve_reject_message_to_admin, + remove_button_from_last_message, update_ticket_status, ) from db.db_googlesheets import ( - load_clients_data, - load_show_data, - load_list_show, load_show_info, load_special_ticket_price, + load_clients_data, load_show_data, load_list_show, load_show_info, + load_special_ticket_price, ) from api.googlesheets import ( - write_data_for_reserve, - write_client, - write_client_list_waiting, + write_data_for_reserve, write_client, write_client_list_waiting, get_quality_of_seats, ) from utilities.utl_func import ( - extract_phone_number_from_text, - add_btn_back_and_cancel, - send_message_to_admin, - set_back_context, get_back_context, + extract_phone_number_from_text, add_btn_back_and_cancel, + send_message_to_admin, set_back_context, get_back_context, check_email ) from utilities.hlp_func import ( check_phone_number, create_replay_markup_for_list_of_shows, - create_approve_and_reject_replay, enum_current_show_by_month, add_text_of_show_and_numerate ) from settings.settings import ( - ADMIN_GROUP, - COMMAND_DICT, - DICT_OF_EMOJI_FOR_BUTTON, - DICT_CONVERT_MONTH_NUMBER_TO_STR, - SUPPORT_DATA, - RESERVE_TIMEOUT, + ADMIN_GROUP, COMMAND_DICT, SUPPORT_DATA, RESERVE_TIMEOUT, OFFER, + DICT_OF_EMOJI_FOR_BUTTON, DICT_CONVERT_MONTH_NUMBER_TO_STR, FILE_ID_RULES, ) reserve_hl_logger = logging.getLogger('bot.reserve_hl') From 761eeaadab94118b3745e8f52184e3cae559e44e Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 22:35:27 +0300 Subject: [PATCH 65/71] =?UTF-8?q?refactor:=20=D0=94=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=98=D0=BC=D0=BF=D0=BE=D1=80?= =?UTF-8?q?=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/handlers/reserve_hl.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/handlers/reserve_hl.py b/src/handlers/reserve_hl.py index 2c47a38..a9a9824 100644 --- a/src/handlers/reserve_hl.py +++ b/src/handlers/reserve_hl.py @@ -1,6 +1,7 @@ import logging import pprint import re +import uuid from datetime import datetime from telegram.ext import ContextTypes, ConversationHandler, TypeHandler @@ -9,9 +10,13 @@ InlineKeyboardButton, InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ) -from telegram.constants import ParseMode, ChatType, ChatAction +from telegram.constants import ChatType, ChatAction +from yookassa import Payment -from handlers import init_conv_hl_dialog +from api.yookassa_connect import create_param_payment +from db import db_postgres +from db.enum import TicketStatus +from handlers import init_conv_hl_dialog, check_user_db from handlers.sub_hl import ( request_phone_number, send_and_del_message_to_remove_kb, write_old_seat_info, @@ -59,6 +64,7 @@ async def choice_month(update: Update, context: ContextTypes.DEFAULT_TYPE): await query.delete_message() else: state = init_conv_hl_dialog(update, context) + await check_user_db(update, context) user = context.user_data.setdefault('user', update.effective_user) From 291897e827ccfabc15a64c690d5cbe354728a88a Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 22:38:06 +0300 Subject: [PATCH 66/71] =?UTF-8?q?refactor:=20=D0=92=D0=BD=D0=B5=D1=81?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BF=D1=80=D0=B0=D0=B2=D0=BE=D0=BA?= =?UTF-8?q?=20=D0=B2=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=83=20c=20payment?= =?UTF-8?q?=5Fdata?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/handlers/reserve_hl.py | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/src/handlers/reserve_hl.py b/src/handlers/reserve_hl.py index a9a9824..d34d152 100644 --- a/src/handlers/reserve_hl.py +++ b/src/handlers/reserve_hl.py @@ -498,11 +498,9 @@ async def choice_option_of_reserve( choose_event_info = reserve_user_data['choose_event_info'] choose_event_info['time_show'] = time - reserve_admin_data: dict = context.user_data['reserve_admin_data'] - payment_id = reserve_admin_data['payment_id'] - reserve_hl_logger.info(f'Бронирование: {payment_id}') - reserve_admin_data[payment_id] = {} - reserve_admin_data[payment_id]['event_id'] = event_id + payment_data = context.user_data['reserve_admin_data']['payment_data'] + reserve_hl_logger.info(f'Бронирование: {payment_data}') + payment_data['event_id'] = event_id dict_of_shows = context.user_data['common_data']['dict_of_shows'] event = dict_of_shows[int(event_id)] @@ -795,9 +793,8 @@ async def check_and_send_buy_info( 'Проверяю наличие свободных мест...') await update.effective_chat.send_action(ChatAction.TYPING) - reserve_admin_data = context.user_data['reserve_admin_data'] - payment_id = reserve_admin_data['payment_id'] - event_id = reserve_admin_data[payment_id]['event_id'] + payment_data = context.user_data['reserve_admin_data']['payment_data'] + event_id = payment_data['event_id'] # Обновляем кол-во доступных мест list_of_name_colum = [ 'qty_child_free_seat', @@ -961,9 +958,8 @@ async def check_and_send_buy_info( reserve_user_data['dict_of_date_show'].clear() reserve_user_data['back'].clear() - reserve_admin_data = context.user_data['reserve_admin_data'] - payment_id = reserve_admin_data['payment_id'] - reserve_admin_data[payment_id]['chose_ticket'] = chose_ticket + payment_data = context.user_data['reserve_admin_data']['payment_data'] + payment_data['chose_ticket'] = chose_ticket state = 'PAID' context.user_data['STATE'] = state @@ -1142,9 +1138,7 @@ async def get_name_children( list_message_text.append(message_text) try: - reserve_admin_data = context.user_data['reserve_admin_data'] - payment_id = reserve_admin_data['payment_id'] - payment_data = reserve_admin_data[payment_id] + payment_data = context.user_data['reserve_admin_data']['payment_data'] chose_ticket = payment_data['chose_ticket'] except KeyError as e: reserve_hl_logger.error(e) @@ -1193,7 +1187,7 @@ async def get_name_children( reserve_hl_logger.info(client_data) chose_price = reserve_user_data['chose_price'] - event_id = reserve_admin_data[payment_id]['event_id'] + event_id = payment_data['event_id'] record_id = write_client( client_data, event_id, @@ -1294,9 +1288,7 @@ async def conversation_timeout( f'{context.bot_data['admin']['contacts']}' ) reserve_hl_logger.info(pprint.pformat(context.user_data)) - reserve_admin_data = context.user_data['reserve_admin_data'] - payment_id = reserve_admin_data['payment_id'] - payment_data = reserve_admin_data[payment_id] + payment_data = context.user_data['reserve_admin_data']['payment_data'] chose_ticket = payment_data['chose_ticket'] event_id = payment_data['event_id'] From 9c2ad6b058478e1a01cabf747ba1c9a153186b02 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 22:40:39 +0300 Subject: [PATCH 67/71] =?UTF-8?q?feat:=20=D0=92=D0=BD=D0=B5=D1=81=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=BF=D1=80=D0=B0=D0=B2=D0=BE=D0=BA=20?= =?UTF-8?q?=D0=B2=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=83=20c=20payment=5Fd?= =?UTF-8?q?ata?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Большая переработка reserve_hl.py --- src/handlers/reserve_hl.py | 271 +++++++++++++++++++++++-------------- 1 file changed, 168 insertions(+), 103 deletions(-) diff --git a/src/handlers/reserve_hl.py b/src/handlers/reserve_hl.py index d34d152..9e5da47 100644 --- a/src/handlers/reserve_hl.py +++ b/src/handlers/reserve_hl.py @@ -673,33 +673,24 @@ async def choice_option_of_reserve( '2. Дождитесь ответа\n' '3. Оплатите билет со скидкой 10% от цены, которая указана выше') - await query.message.edit_text( + await query.edit_message_text( text=text, reply_markup=reply_markup ) - state = 'ORDER' + state = 'EMAIL' if context.user_data.get('command', False) == 'reserve_admin': state = 'TICKET' context.user_data['STATE'] = state return state -async def check_and_send_buy_info( +async def get_email( update: Update, context: ContextTypes.DEFAULT_TYPE ): - """ - Проверяет кол-во доступных мест, для выбранного варианта пользователем и - отправляет сообщение об оплате - - :return: - возвращает state PAID, - если проверка не пройдена, то state ORDER - """ query = update.callback_query await query.answer() - await query.edit_message_text('Готовлю информацию об оплате...') user = context.user_data['user'] reserve_hl_logger.info(": ".join( @@ -712,7 +703,8 @@ async def check_and_send_buy_info( )) try: - key_option_for_reserve = int(query.data) + context.user_data['reserve_user_data'][ + 'key_option_for_reserve'] = int(query.data) except ValueError as e: reserve_hl_logger.error(e) state = 'TIME' @@ -727,6 +719,82 @@ async def check_and_send_buy_info( context.user_data['STATE'] = state return state + text = (f'{OFFER}\n\n' + 'Если вы согласны напишите email, на него вам будет направлен чек ' + 'после оплаты') + keyboard = [add_btn_back_and_cancel(postfix_for_cancel='res', + postfix_for_back='TIME')] + reply_markup = InlineKeyboardMarkup(keyboard) + message = await query.edit_message_text( + text=text, + reply_markup=reply_markup + ) + + context.user_data['reserve_user_data']['message_id'] = message.message_id + + state = 'ORDER' + context.user_data['STATE'] = state + return state + + +async def check_and_send_buy_info( + update: Update, + context: ContextTypes.DEFAULT_TYPE +): + """ + Проверяет кол-во доступных мест, для выбранного варианта пользователем и + отправляет сообщение об оплате + + :return: + возвращает state PAID, + если проверка не пройдена, то state ORDER + """ + await context.bot.edit_message_reply_markup( + chat_id=update.effective_chat.id, + message_id=context.user_data['reserve_user_data']['message_id'] + ) + email = update.effective_message.text + if not check_email(email): + keyboard = [add_btn_back_and_cancel(postfix_for_cancel='res', + postfix_for_back='TIME')] + reply_markup = InlineKeyboardMarkup(keyboard) + await update.effective_chat.send_message( + text=f'Вы написали: {email}\n' + f'Пожалуйста проверьте и введите почту еще раз.', + reply_markup=reply_markup + ) + state = 'ORDER' + context.user_data['STATE'] = state + return state + + await db_postgres.update_user( + session=context.session, + user_id=update.effective_user.id, + email=email + ) + + await context.bot.send_message( + chat_id=update.effective_chat.id, + text=f'Ваше подтверждение получено.\n' + f'Перехожу к формированию ссылки на оплату.' + ) + message = await context.bot.send_message( + text='Готовлю информацию об оплате...', + chat_id=update.effective_chat.id, + ) + + key_option_for_reserve = context.user_data['reserve_user_data'][ + 'key_option_for_reserve'] + user = context.user_data['user'] + reserve_hl_logger.info(": ".join( + [ + 'Пользователь', + f'{user}', + 'выбрал', + str(key_option_for_reserve), + ] + )) + reserve_user_data = context.user_data['reserve_user_data'] choose_event_info = reserve_user_data['choose_event_info'] dict_show_data = context.bot_data['dict_show_data'] @@ -742,7 +810,6 @@ async def check_and_send_buy_info( reserve_user_data ) - user = context.user_data['user'] reserve_hl_logger.info(": ".join( [ 'Пользователь', @@ -762,7 +829,7 @@ async def check_and_send_buy_info( if chose_ticket.flag_individual: text = ('Для оформления данного варианта обратитесь к Администратору:\n' f'{context.bot_data['admin']['contacts']}') - await query.message.edit_text( + await message.edit_text( text=text ) @@ -786,7 +853,7 @@ async def check_and_send_buy_info( context.user_data['common_data']['text_for_notification_massage'] = text - await query.message.edit_text( + await message.edit_text( text=text, ) message = await update.effective_chat.send_message( @@ -825,7 +892,7 @@ async def check_and_send_buy_info( ] )) - await query.message.delete() + await message.delete() reserve_user_data['event_info_for_list_waiting'] = text_select_show text = ('К сожалению места уже забронировали и свободных мест для\n' f'{name_show}\n' @@ -886,6 +953,14 @@ async def check_and_send_buy_info( try: write_data_for_reserve(event_id, numbers) + await db_postgres.update_schedule_event( + context.session, + int(event_id), + qty_child_free_seat_new=qty_child_free_seat_new, + qty_child_nonconfirm_seat_new=qty_child_nonconfirm_seat_new, + qty_adult_free_seat_new=qty_adult_free_seat_new, + qty_adult_nonconfirm_seat_new=qty_adult_nonconfirm_seat_new, + ) except TimeoutError: reserve_hl_logger.error(": ".join( [ @@ -903,7 +978,7 @@ async def check_and_send_buy_info( 'Нажмите "Назад" и выберите время повторно.\n' 'Если ошибка повторяется свяжитесь с Администратором:\n' f'{context.bot_data['admin']['contacts']}') - await query.message.edit_text( + await message.edit_text( text=text, reply_markup=reply_markup ) @@ -911,18 +986,33 @@ async def check_and_send_buy_info( context.user_data['STATE'] = state return state + idempotency_id = uuid.uuid4() + payment = Payment.create( + create_param_payment( + price, + chose_ticket.name, + payment_method_type='yoo_money', + chat_id=update.effective_chat.id, + message_id=message.message_id, + ), + idempotency_id) + keyboard = [] + button_payment = InlineKeyboardButton( + 'Оплатить', + callback_data=f'payment|{payment.id}', + url=payment.confirmation.confirmation_url + ) button_cancel = InlineKeyboardButton( 'Отменить', callback_data=f'Отменить-res|' - f'{query.message.chat_id} {query.message.message_id}' + f'{update.effective_chat.id} {message.message_id}' ) + keyboard.append([button_payment]) keyboard.append([button_cancel]) reply_markup = InlineKeyboardMarkup(keyboard) - await message.delete() - message = await context.bot.send_message( - chat_id=update.effective_chat.id, + await message.edit_text( text=f"""Бронь билета осуществляется по 100% оплате. ❗️ВОЗВРАТ ДЕНЕЖНЫХ СРЕДСТВ ИЛИ ПЕРЕНОС ВОЗМОЖЕН НЕ МЕНЕЕ ЧЕМ ЗА 24 ЧАСА❗️ ❗️ПЕРЕНОС ВОЗМОЖЕН ТОЛЬКО 1 РАЗ❗️ @@ -930,18 +1020,8 @@ async def check_and_send_buy_info( Если вы согласны с правилами, то переходите к оплате. Если вам нужно подумать, нажмите кнопку Отменить под сообщением. - - К оплате {price} руб - -Оплатить можно: - - Переводом на карту Сбербанка по номеру телефона - +79159383529 Татьяна Александровна Б. - -ВАЖНО! Прислать сюда электронный чек/квитанцию об оплате (файл или скриншот) -Необходимо отправить чек в течении {RESERVE_TIMEOUT} мин или бронь будет -ОТМЕНЕНА! __________ -После отправки чека необходимо: +После оплаты необходимо: 1. Заполнить анкету (она придет автоматически) 2. Дождаться подтверждения""", reply_markup=reply_markup, @@ -961,9 +1041,21 @@ async def check_and_send_buy_info( payment_data = context.user_data['reserve_admin_data']['payment_data'] payment_data['chose_ticket'] = chose_ticket - state = 'PAID' - context.user_data['STATE'] = state - return state + ticket_id = await db_postgres.create_ticket( + context.session, + base_ticket_id=chose_ticket.base_ticket_id, + price=price, + schedule_event_id=event_id, + status=TicketStatus.CREATED, + payment_id=payment.id, + idempotency_id=idempotency_id, + ) + + payment_data['ticket_id'] = ticket_id + + state = 'PAID' + context.user_data['STATE'] = state + return state async def forward_photo_or_file( @@ -972,77 +1064,39 @@ async def forward_photo_or_file( ): """ Пересылает картинку или файл. - Запускает цепочку вопросов для клиентской базы, если пользователь нажал - кнопку подтвердить. + Запускает цепочку вопросов для клиентской базы, + если пользователь прислал подтверждающий документ. """ - message_id = context.user_data['common_data']['message_id_buy_info'] - chat_id = update.effective_chat.id + await update_ticket_status(context, TicketStatus.PAID) + await remove_button_from_last_message(update, context) + await send_breaf_message(update) + message = await send_approve_reject_message_to_admin(update, context) - # Убираем у старого сообщения кнопки - await context.bot.edit_message_reply_markup( - chat_id=chat_id, - message_id=message_id - ) - - user = context.user_data['user'] - text = context.user_data['common_data']['text_for_notification_massage'] + context.user_data['common_data'][ + 'message_id_for_admin'] = message.message_id - thread_id = (context.bot_data['dict_topics_name'] - .get('Бронирование спектаклей', None)) - res = await context.bot.send_message( - chat_id=ADMIN_GROUP, - text=f'#Бронирование\n' - f'Квитанция пользователя @{user.username} {user.full_name}\n', - message_thread_id=thread_id - ) - await update.effective_message.forward( - chat_id=ADMIN_GROUP, - message_thread_id=thread_id - ) - message_id_for_admin = res.message_id - await send_message_to_admin(ADMIN_GROUP, - text, - message_id_for_admin, - context, - thread_id) - - # Сообщение для опроса - text_brief = ( - 'Для подтверждения брони заполните, пожалуйста, анкету.\n' - 'Вход на мероприятие ведется по спискам.\n' - '__________\n' - 'Пожалуйста, не пишите лишней информации/дополнительных слов в ' - 'сообщении.\n' - 'Вопросы будут приходить последовательно (их будет всего 3)' - ) - await update.effective_chat.send_message( - text=text_brief, - ) - await update.effective_chat.send_message( - 'Напишите фамилию и имя (взрослого)', - ) + state = 'FORMA' + context.user_data['STATE'] = state + return state - # Сообщение для администратора - payment_id = context.user_data['reserve_admin_data']['payment_id'] - reply_markup = create_approve_and_reject_replay( - 'reserve', - update.effective_user.id, - message_id, - payment_id - ) - chose_price = context.user_data['reserve_user_data']['chose_price'] +async def processing_successful_notification( + update: Update, + context: ContextTypes.DEFAULT_TYPE +): + """ + Отрабатывает в случае успешной оплаты и нажатии пользователем кнопки далее. + Запускает цепочку вопросов для клиентской базы, + если пользователь прислал подтверждающий документ. + """ + query = update.callback_query + await query.answer() + await query.edit_message_reply_markup() - message = await context.bot.send_message( - chat_id=ADMIN_GROUP, - text=f'Пользователь @{user.username} {user.full_name}\n' - f'Запросил подтверждение брони на сумму {chose_price} руб\n' - f'Ждем заполнения анкеты, если всё хорошо, то только после ' - f'нажимаем подтвердить', - reply_markup=reply_markup, - message_thread_id=thread_id - ) + await update_ticket_status(context, TicketStatus.PAID) + await send_breaf_message(update) + message = await send_approve_reject_message_to_admin(update, context) context.user_data['common_data'][ 'message_id_for_admin'] = message.message_id @@ -1194,6 +1248,17 @@ async def get_name_children( chose_ticket, chose_price, ) + ticket_id = context.user_data['reserve_admin_data']['payment_data'][ + 'ticket_id'] + + people = await db_postgres.create_people(context.session, + update.effective_user.id, + client_data) + + await db_postgres.attach_user_and_people_to_ticket(context.session, + ticket_id, + update.effective_user.id, + people) text = '\n'.join([ client_data['name_adult'], @@ -1232,9 +1297,6 @@ async def get_name_children( thread_id=thread_id ) - # TODO Переставить сразу после оплаты (чтобы можно было подтвердить) - reserve_admin_data['payment_id'] += 1 - reserve_hl_logger.info(f'Для пользователя {user}') reserve_hl_logger.info( f'Обработчик завершился на этапе {context.user_data['STATE']}') @@ -1261,6 +1323,9 @@ async def send_by_ticket_info(update, context): ) await message.pin() + await update.effective_chat.send_photo(photo=FILE_ID_RULES, + caption='Правила театра') + async def conversation_timeout( update: Update, From 498e38928e0a9258a20796df7c18f412e43bd6c9 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 22:44:03 +0300 Subject: [PATCH 68/71] =?UTF-8?q?feat:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20init=20=D1=84=D0=B0=D0=B9?= =?UTF-8?q?=D0=BB=D0=B0=20=D0=BC=D0=B8=D0=B3=D1=80=D0=B0=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../20240224-1851_31c46999db1a_init.py | 293 ++++++++++++++++++ 1 file changed, 293 insertions(+) create mode 100644 src/db/migrations/versions/20240224-1851_31c46999db1a_init.py diff --git a/src/db/migrations/versions/20240224-1851_31c46999db1a_init.py b/src/db/migrations/versions/20240224-1851_31c46999db1a_init.py new file mode 100644 index 0000000..f555e80 --- /dev/null +++ b/src/db/migrations/versions/20240224-1851_31c46999db1a_init.py @@ -0,0 +1,293 @@ +"""init + +Revision ID: 31c46999db1a +Revises: +Create Date: 2024-02-24 18:51:15.996266 + +""" +from alembic import op +import sqlalchemy as sa + +from db import TicketStatusEnum, TicketPriceTypeEnum, PriceTypeEnum, AgeTypeEnum + + +# revision identifiers, used by Alembic. +revision = "31c46999db1a" +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade() -> None: + TicketStatusEnum.create(op.get_bind(), checkfirst=True) + TicketPriceTypeEnum.create(op.get_bind(), checkfirst=True) + PriceTypeEnum.create(op.get_bind(), checkfirst=True) + AgeTypeEnum.create(op.get_bind(), checkfirst=True) + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "users", + sa.Column( + "user_id", sa.BigInteger(), autoincrement=False, nullable=False), + sa.Column( + "chat_id", sa.BigInteger(), nullable=False), + sa.Column("username", sa.String()), + sa.Column("email", sa.String()), + sa.Column( + "created_at", + sa.TIMESTAMP(timezone=True), + server_default=sa.text("now()"), + nullable=False, + ), + sa.Column( + "updated_at", + sa.TIMESTAMP(timezone=True), + server_default=sa.text("now()"), + nullable=False, + ), + sa.PrimaryKeyConstraint("user_id", name=op.f("pk__users")), + ) + op.create_table( + "people", + sa.Column("id", sa.BIGINT(), nullable=False), + sa.Column("name", sa.String()), + sa.Column("age_type", AgeTypeEnum, nullable=False), + sa.Column("user_id", sa.BigInteger()), + sa.Column( + "created_at", + sa.TIMESTAMP(timezone=True), + server_default=sa.text("now()"), + nullable=False, + ), + sa.Column( + "updated_at", + sa.TIMESTAMP(timezone=True), + server_default=sa.text("now()"), + nullable=False, + ), + sa.ForeignKeyConstraint( + ["user_id"], + ["users.user_id"], + name=op.f("fk__people__user_id__users"), + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("id", name=op.f("pk__people")), + ) + op.create_table( + "adults", + sa.Column("id", sa.BIGINT(), nullable=False), + sa.Column("phone", sa.String()), + sa.Column("person_id", sa.BIGINT(), nullable=False), + sa.ForeignKeyConstraint( + ["person_id"], + ["people.id"], + name=op.f("fk__adults__person_id__people"), + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("id", name=op.f("pk__adults")), + ) + op.create_table( + "theater_events", + sa.Column("id", sa.BIGINT(), nullable=False), + sa.Column("name", sa.String(), nullable=False), + sa.Column("flag_premier", sa.Boolean(), nullable=False), + sa.Column("min_age_child", sa.BIGINT(), nullable=False), + sa.Column("max_age_child", sa.BIGINT()), + sa.Column("show_emoji", sa.String()), + sa.Column("flag_active_repertoire", sa.Boolean(), nullable=False), + sa.Column("flag_active_bd", sa.Boolean(), nullable=False), + sa.Column("max_num_child_bd", sa.BIGINT(), nullable=False), + sa.Column("max_num_adult_bd", sa.BIGINT(), nullable=False), + sa.Column("flag_indiv_cost", sa.Boolean(), nullable=False), + sa.Column("price_type", PriceTypeEnum, nullable=False), + sa.PrimaryKeyConstraint("id", name=op.f("pk__theater_events")), + ) + op.create_table( + "type_events", + sa.Column("id", sa.BIGINT(), nullable=False), + sa.Column("name", sa.String(), nullable=False), + sa.Column("name_alias", sa.String(), nullable=False), + sa.Column("base_price_gift", sa.BIGINT()), + sa.Column("notes", sa.String()), + sa.PrimaryKeyConstraint("id", name=op.f("pk__type_events")), + ) + op.create_table( + "children", + sa.Column("id", sa.BIGINT(), nullable=False), + sa.Column("age", sa.Float(), nullable=False), + sa.Column("birthdate", sa.Date()), + sa.Column("person_id", sa.BIGINT(), nullable=False), + sa.ForeignKeyConstraint( + ["person_id"], + ["people.id"], + name=op.f("fk__children__person_id__people"), + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("id", name=op.f("pk__children")), + ) + op.create_table( + "schedule_events", + sa.Column("id", sa.BIGINT(), nullable=False), + sa.Column("type_event_id", sa.BIGINT(), nullable=False), + sa.Column("theater_events_id", sa.BIGINT(), nullable=False), + sa.Column("flag_turn_in_bot", sa.Boolean(), nullable=False), + sa.Column( + "datetime_event", sa.TIMESTAMP(timezone=True), nullable=False), + sa.Column("qty_child", sa.BIGINT(), nullable=False), + sa.Column("qty_child_free_seat", sa.BIGINT(), nullable=False), + sa.Column("qty_child_nonconfirm_seat", sa.BIGINT(), nullable=False), + sa.Column("qty_adult", sa.BIGINT(), nullable=False), + sa.Column("qty_adult_free_seat", sa.BIGINT(), nullable=False), + sa.Column("qty_adult_nonconfirm_seat", sa.BIGINT(), nullable=False), + sa.Column("flag_gift", sa.Boolean(), nullable=False), + sa.Column("flag_christmas_tree", sa.Boolean(), nullable=False), + sa.Column("flag_santa", sa.Boolean(), nullable=False), + sa.Column("ticket_price_type", TicketPriceTypeEnum, nullable=False), + sa.Column( + "created_at", + sa.TIMESTAMP(timezone=True), + server_default=sa.text("now()"), + nullable=False, + ), + sa.Column( + "updated_at", + sa.TIMESTAMP(timezone=True), + server_default=sa.text("now()"), + nullable=False, + ), + sa.ForeignKeyConstraint( + ["type_event_id"], + ["type_events.id"], + name=op.f("fk__schedule_events__type_event_id__type_events"), + ), + sa.ForeignKeyConstraint( + ["theater_events_id"], + ["theater_events.id"], + name=op.f("fk__schedule_events__theater_events_id__theater_events"), + ), + sa.PrimaryKeyConstraint("id", name=op.f("pk__schedule_events")), + ) + op.create_table( + "tickets", + sa.Column("id", sa.BIGINT(), nullable=False), + sa.Column("base_ticket_id", sa.BIGINT(), nullable=False), + sa.Column("price", sa.BIGINT(), nullable=False), + sa.Column("exclude", sa.Boolean(), nullable=False), + sa.Column("status", TicketStatusEnum, nullable=False), + sa.Column("schedule_event_id", sa.BIGINT()), + sa.Column("notes", sa.String()), + sa.Column("payment_id", sa.String()), + sa.Column("idempotency_id", sa.String()), + sa.Column( + "created_at", + sa.TIMESTAMP(timezone=True), + server_default=sa.text("now()"), + nullable=False, + ), + sa.Column( + "updated_at", + sa.TIMESTAMP(timezone=True), + server_default=sa.text("now()"), + nullable=False, + ), + sa.UniqueConstraint("payment_id", + name=op.f("uq__tickets__payment_id")), + sa.UniqueConstraint("idempotency_id", + name=op.f("uq__tickets__idempotency_id")), + sa.ForeignKeyConstraint( + ["schedule_event_id"], + ["schedule_events.id"], + name=op.f("fk__tickets__schedule_event_id__schedule_events"), + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("id", name=op.f("pk__tickets")), + ) + op.create_table( + "people_tickets", + sa.Column("person_id", sa.BIGINT(), nullable=False), + sa.Column("ticket_id", sa.BIGINT(), nullable=False), + sa.Column( + "created_at", + sa.TIMESTAMP(timezone=True), + server_default=sa.text("now()"), + nullable=False, + ), + sa.Column( + "updated_at", + sa.TIMESTAMP(timezone=True), + server_default=sa.text("now()"), + nullable=False, + ), + sa.ForeignKeyConstraint( + ["person_id"], + ["people.id"], + name=op.f("fk__people_tickets__person_id__people"), + ), + sa.ForeignKeyConstraint( + ["ticket_id"], + ["tickets.id"], + name=op.f("fk__people_tickets__ticket_id__tickets"), + ), + sa.PrimaryKeyConstraint( + "person_id", "ticket_id", name=op.f("pk__people_tickets") + ), + ) + op.create_table( + "users_tickets", + sa.Column("user_id", sa.BigInteger(), nullable=False), + sa.Column("ticket_id", sa.BIGINT(), nullable=False), + sa.Column( + "created_at", + sa.TIMESTAMP(timezone=True), + server_default=sa.text("now()"), + nullable=False, + ), + sa.Column( + "updated_at", + sa.TIMESTAMP(timezone=True), + server_default=sa.text("now()"), + nullable=False, + ), + sa.ForeignKeyConstraint( + ["ticket_id"], + ["tickets.id"], + name=op.f("fk__users_tickets__ticket_id__tickets"), + ), + sa.ForeignKeyConstraint( + ["user_id"], + ["users.user_id"], + name=op.f("fk__users_tickets__user_id__users"), + ), + sa.PrimaryKeyConstraint( + "user_id", "ticket_id", name=op.f("pk__users_tickets") + ), + ) + + path = r'/src/db/data/sql_files' + schedule_events = path + r"/schedule_events.sql" + theater_events = path + r"/theater_events.sql" + type_events = path + r"/type_events.sql" + items = [type_events, theater_events, schedule_events] + for file in items: + with open(file, encoding='utf-8') as f: + query = sa.text(f.read()) + op.execute(query) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table("users_tickets") + op.drop_table("people_tickets") + op.drop_table("tickets") + op.drop_table("schedule_events") + op.drop_table("adults") + op.drop_table("children") + op.drop_table("people") + op.drop_table("users") + op.drop_table("type_events") + op.drop_table("theater_events") + TicketStatusEnum.drop(op.get_bind(), checkfirst=True) + TicketPriceTypeEnum.drop(op.get_bind(), checkfirst=True) + PriceTypeEnum.drop(op.get_bind(), checkfirst=True) + AgeTypeEnum.drop(op.get_bind(), checkfirst=True) + # ### end Alembic commands ### From 65ab2bce21afef9107115bd36e29cf367075a945 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 22:50:03 +0300 Subject: [PATCH 69/71] =?UTF-8?q?feat:=20=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20gitignore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 7b19036..931ea41 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ settings.py /src/utilities/pickle/ud_ids/ /src/utilities/pickle/user_ids **/sql_files/ +docker-compose-* +/src/utilities/db_import_export/ From 2b6eb8d72b23803b3c8ff8193f2168a5c75d72fc Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 22:55:11 +0300 Subject: [PATCH 70/71] =?UTF-8?q?feat:=20=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20gitignore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 931ea41..81e0654 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,3 @@ settings.py /src/utilities/pickle/user_ids **/sql_files/ docker-compose-* -/src/utilities/db_import_export/ From 8d98065faed995cb9e6d0561d2f2efc3d7125ce1 Mon Sep 17 00:00:00 2001 From: MikiEremiki Date: Thu, 14 Mar 2024 23:07:03 +0300 Subject: [PATCH 71/71] =?UTF-8?q?build:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20action=20=D0=BF=D0=BE=20=D1=81?= =?UTF-8?q?=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8=D1=8E=20fastapi=20=D0=BE?= =?UTF-8?q?=D0=B1=D1=80=D0=B0=D0=B7=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/fastapi_image.yml | 31 +++++++++++++++++++++++++++++ Dockerfile-server | 3 +-- 2 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/fastapi_image.yml diff --git a/.github/workflows/fastapi_image.yml b/.github/workflows/fastapi_image.yml new file mode 100644 index 0000000..5b31765 --- /dev/null +++ b/.github/workflows/fastapi_image.yml @@ -0,0 +1,31 @@ +name: ci + +on: + push: + branches: + - main + +jobs: + build: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v4 + - + name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - + name: Build and push + uses: docker/build-push-action@v5 + with: + context: . + dockerfile: Dockerfile-server + push: true + tags: ${{ secrets.DOCKER_USERNAME }}/baby_domik_fastapi:latest \ No newline at end of file diff --git a/Dockerfile-server b/Dockerfile-server index 4907922..bda4b32 100644 --- a/Dockerfile-server +++ b/Dockerfile-server @@ -8,5 +8,4 @@ WORKDIR /app RUN --mount=type=bind,source=requirements-server.txt,target=requirements-server.txt \ python -m pip install -r requirements-server.txt -COPY /src/api/fastapi_nats.py . -CMD python3 -m fastapi_nats run \ No newline at end of file +COPY /src/api/fastapi_nats.py . \ No newline at end of file