Untitled

4ae4d avatar
4ae4d
plain_text
04/17/2026 9:24 AM
66.7 KB
8
Indexable
diff --git a/services/bot-new/core/handlers/moderation.py b/services/bot-new/core/handlers/moderation.py
index 60f7876..edbe4fb 100644
--- a/services/bot-new/core/handlers/moderation.py
+++ b/services/bot-new/core/handlers/moderation.py
@@ -1,1170 +1,1176 @@
 import re
 from datetime import datetime
 from uuid import UUID
 
 from dialog_bot_sdk.entities.messaging import UpdateInteractiveMediaEvent, UpdateMessage
 from dialog_bot_sdk.interactive_media import Button
 
 from core.bot_kit.fsm import FSMContext, State, StatesGroup
 from core.bot_kit.router import Router
 from core.config import bot
 from core.handlers.events_ui import (
     build_back_to_event_keyboard,
     send_event_card,
     send_event_cards_page,
     send_my_events_page,
     split_event_value,
 )
 from core.markups.event import event_actions_keyboard, format_event_details
 from core.markups.tags import choices_from_backend_tags, tags_toggle_keyboard
 from core.schemas.event import EventSchema
 from core.schemas.user import UserSchema
 from core.services.event import EventService
 from core.services.report import ReportService
 from core.services.tag import TagService
 from core.utils import delete_prev_message, delete_prev_message_by_peer
 from core.utils.events_filter import clear_context_keep_events_filters
 from core.utils.logger import logger
 
 from ..markups import (
     back_to_moderation_keyboard,
     event_edit_fields_keyboard,
     moderation_menu_keyboard,
 )
 
 TAGS_MEDIA_ID = "event_tags_toggle"
 TAGS_TOGGLE_PREFIX = "toggle:"
 TAGS_DONE_VALUE = "done"
-DEFAULT_CITY_UUID = "be9da3ba-d-42f9-9ae2-9875c8625083"  # SPB
 # TODO убрать хардкод
 
 
 events_rt = Router()
 
 
 class EventEditTagsState(StatesGroup):  # untested
     tags = State()
 
 
 class EventEditState(StatesGroup):  # untested
     wait_value = State()
 
 
 class EventEnterCodeState(StatesGroup):  # untested
     wait_code = State()
 
 
 class EventViewState(StatesGroup):  # untested
     wait_event_id = State()
 
 
 class EventCreateState(StatesGroup):  # untested
     name = State()
     date = State()
     time = State()
     # location удалён
-    gosp = State()        
-    project = State()     
+    gosp = State()
+    project = State()
     description = State()
     points = State()
     tags = State()
     code = State()
 
 
 @bot.di
 def moderation_menu_handler(event: UpdateInteractiveMediaEvent, context: FSMContext):  # untested
     clear_context_keep_events_filters(context)
     delete_prev_message_by_peer(bot, event.peer)
 
     bot.messaging.send_message(
         peer=event.peer,
         text=("Ты в доме модерации!\n\nПожалуйста, выбери, что хочешь сделать 👇"),
         interactive_media_groups=moderation_menu_keyboard(),
     )
 
 
 @bot.di
 def all_events_handler(  # untested
     event: UpdateInteractiveMediaEvent,
     context: FSMContext,
     event_service: EventService,
     user: UserSchema | None,
 ):
     delete_prev_message_by_peer(bot, event.peer)
     clear_context_keep_events_filters(context)
     send_event_cards_page(
         event.peer,
         offset=0,
         event_service=event_service,
         ctx_name="moderation",
         context=context,
     )
 
 
 @bot.di
 def my_events_handler(  # untested
     event: UpdateInteractiveMediaEvent,
     context: FSMContext,
     event_service: EventService,
     user: UserSchema | None,
 ):
     delete_prev_message_by_peer(bot, event.peer)
     clear_context_keep_events_filters(context)
 
     send_my_events_page(
         event.peer,
         offset=0,
         event_service=event_service,
         requester_messenger_id=event.peer.id,
         context=context,
     )
 
 
 @bot.di
 def my_events_page_handler(  # untested
     event: UpdateInteractiveMediaEvent,
     context: FSMContext,
     event_service: EventService,
     user: UserSchema | None,
 ):
     delete_prev_message_by_peer(bot, event.peer)
     clear_context_keep_events_filters(context)
 
     try:
         offset = int(event.data.value)
         if offset < 0:
             offset = 0
     except Exception:
         offset = 0
 
     send_my_events_page(
         event.peer,
         offset=offset,
         event_service=event_service,
         requester_messenger_id=event.peer.id,
         context=context,
     )
 
 
 @bot.di
 def my_events_open_handler(  # untested
     event: UpdateInteractiveMediaEvent,
     context: FSMContext,
     event_service: EventService,
     report_service: ReportService,
     user: UserSchema | None,
 ):
     delete_prev_message_by_peer(bot, event.peer)
     context.set_state(None)
 
     event_id = str(event.data.value or "").strip()
     send_event_card(
         event.peer,
         event_id=event_id,
         event_service=event_service,
         user=user,
         ctx_name="my_events",
         report_service=report_service,
     )
 
 
 @bot.di
 def all_events_page_handler(  # untested
     event: UpdateInteractiveMediaEvent,
     context: FSMContext,
     event_service: EventService,
     user: UserSchema | None,
 ):
     delete_prev_message_by_peer(bot, event.peer)
     clear_context_keep_events_filters(context)
 
     try:
         offset = int(event.data.value)
         if offset < 0:
             offset = 0
     except Exception:
         offset = 0
 
     send_event_cards_page(
         event.peer,
         offset=offset,
         event_service=event_service,
         ctx_name="moderation",
         context=context,
     )
 
 
 @bot.di
 def all_events_open_handler(  # untested
     event: UpdateInteractiveMediaEvent,
     context: FSMContext,
     event_service: EventService,
     report_service: ReportService,
     user: UserSchema | None,
 ):
     delete_prev_message_by_peer(bot, event.peer)
     context.set_state(None)
 
     event_id, ctx = split_event_value(event.data.value)
     ctx_name = ctx or "moderation"
     send_event_card(
         event.peer,
         event_id=event_id,
         event_service=event_service,
         user=user,
         ctx_name=ctx_name,
         report_service=report_service,
     )
 
 
 @bot.di
-def create_event_start_handler(  # untested
+def create_event_start_handler(
     event: UpdateInteractiveMediaEvent,
     context: FSMContext,
 ):
     """старт мастера создания мероприятия"""
     delete_prev_message_by_peer(bot, event.peer)
 
     context.clear()
     bot.messaging.send_message(
         peer=event.peer,
         text=("Создание мероприятия.\n\nШаг 1/9: введи, пожалуйста, название мероприятия:"),
         interactive_media_groups=back_to_moderation_keyboard(),
     )
     context.set_state(EventCreateState.name)
 
 
 @events_rt.message(state=EventCreateState.name)
 @bot.di
-def event_create_name_step(message: UpdateMessage, context: FSMContext):  # untested
+def event_create_name_step(message: UpdateMessage, context: FSMContext):
     delete_prev_message(bot, message)
 
     name = message.message.text_message.text.strip()
     context.update_data({"event_name": name})
 
     bot.messaging.send_message(
         peer=message.peer,
         text=("Шаг 2/9: введи, пожалуйста, дату мероприятия (dd.mm.yyyy)\nНапример: `20.02.2002`:"),
         interactive_media_groups=back_to_moderation_keyboard(),
     )
     context.set_state(EventCreateState.date)
 
 
 @events_rt.message(state=EventCreateState.date)
 @bot.di
-def event_create_date_step(message: UpdateMessage, context: FSMContext):  # untested
+def event_create_date_step(message: UpdateMessage, context: FSMContext):
     delete_prev_message(bot, message)
 
     date = message.message.text_message.text.strip()
     try:
         date_obj = datetime.strptime(date, "%d.%m.%Y").date()
+        # TODO make it try to recognize a lot of patterns
+        if date_obj <= datetime.now().date():
+            raise RuntimeError
     except ValueError:
         bot.messaging.send_message(
             peer=message.peer,
             text=(
                 "⚠️ Я не смог распознать дату.\n"
                 "Пожалуйста, введи в формате `dd.mm.yyyy`, например: `20.02.2002`:"
             ),
             interactive_media_groups=back_to_moderation_keyboard(),
         )
         return
+    except RuntimeError:
+        bot.messaging.send_message(
+            peer=message.peer,
+            text=(
+                "⚠️ Ты не можешь создать мероприятие раньше завтрашнего дня.\n"
+                "Пожалуйста, введи в формате `dd.mm.yyyy`, например: `20.02.2002`:"
+            ),
+            interactive_media_groups=back_to_moderation_keyboard(),
+        )
+        return
 
     context.update_data({"event_date": date_obj})
 
     bot.messaging.send_message(
         peer=message.peer,
         text=("Шаг 3/9: введи время мероприятия в формате `HH:MM`\nНапример: `10:30`:"),
         interactive_media_groups=back_to_moderation_keyboard(),
     )
     context.set_state(EventCreateState.time)
 
 
 @events_rt.message(state=EventCreateState.time)
 @bot.di
-def event_create_time_step(message: UpdateMessage, context: FSMContext):  # untested
+def event_create_time_step(message: UpdateMessage, context: FSMContext):
     delete_prev_message(bot, message)
 
     time = message.message.text_message.text.strip()
     try:
         time_obj = datetime.strptime(time, "%H:%M").time()
+        # TODO make it try to recognize a lot of patterns
     except ValueError:
         bot.messaging.send_message(
             peer=message.peer,
             text=(
                 "⚠️ Я не смог распознать время.\n"
                 "Пожалуйста, введи в формате `HH:MM`, например: `10:30`:"
             ),
             interactive_media_groups=back_to_moderation_keyboard(),
         )
         return
 
     context.update_data({"event_time": time_obj})
 
-    # Пропускаем локацию, сразу переходим к ГОСБ
     bot.messaging.send_message(
         peer=message.peer,
-        text="Шаг 4/9: Введи ГОСБ (UUID или текст для отладки):\nНапример: `be9da3ba-d-42f9-9ae2-9875c8625083`",
+        text="Шаг 4/9: Введи айди ГОСБ'а:\nНапример: `gosb_spb_1`",
         interactive_media_groups=back_to_moderation_keyboard(),
     )
     context.set_state(EventCreateState.gosp)
 
 
 @events_rt.message(state=EventCreateState.gosp)
 @bot.di
-def event_create_gosp_step(message: UpdateMessage, context: FSMContext):  # untested
+def event_create_gosp_step(message: UpdateMessage, context: FSMContext):
     delete_prev_message(bot, message)
 
     gosp = message.message.text_message.text.strip()
     context.update_data({"gosp_id": gosp})
 
     bot.messaging.send_message(
         peer=message.peer,
-        text="Шаг 5/9: Введи id проекта (UUID или текст для отладки, можно пропустить отправив `-`):",
+        text=(
+            "Шаг 5/9: Введи id проекта, если хочешь создать мероприятие в проекте\n"
+            "Введи `-`, если хочешь создать событийное мероприятие - мероприятие мне проекта."
+        ),
         interactive_media_groups=back_to_moderation_keyboard(),
     )
     context.set_state(EventCreateState.project)
 
 
 @events_rt.message(state=EventCreateState.project)
 @bot.di
-def event_create_project_step(message: UpdateMessage, context: FSMContext):  # untested
+def event_create_project_step(message: UpdateMessage, context: FSMContext):
     delete_prev_message(bot, message)
 
     raw = message.message.text_message.text.strip()
     project_id = "" if raw == "-" else raw
     context.update_data({"project_id": project_id})
 
     bot.messaging.send_message(
         peer=message.peer,
-        text='Шаг 6/9: Введи описание:',
+        text="Шаг 6/9: Введи описание:",
         interactive_media_groups=back_to_moderation_keyboard(),
     )
     context.set_state(EventCreateState.description)
 
 
 @events_rt.message(state=EventCreateState.description)
 @bot.di
-def event_create_description_step(message: UpdateMessage, context: FSMContext):  # untested
+def event_create_description_step(message: UpdateMessage, context: FSMContext):
     delete_prev_message(bot, message)
 
     description = message.message.text_message.text.strip()
     context.update_data({"event_description": description})
     bot.messaging.send_message(
         peer=message.peer,
-        text="Шаг 7/9: Введи количество очков начисляемых за мероприятие:",
+        text="Шаг 7/9: Введи количество волонтёрских часов, начисляемых за мероприятие:",
         interactive_media_groups=back_to_moderation_keyboard(),
     )
     context.set_state(EventCreateState.points)
 
 
 @events_rt.message(state=EventCreateState.points)
 @bot.di
-def event_create_points_step(
-    message: UpdateMessage, context: FSMContext, tag_service: TagService
-):  # untested
+def event_create_points_step(message: UpdateMessage, context: FSMContext):
     delete_prev_message(bot, message)
 
     text = message.message.text_message.text.strip()
     try:
         points = int(text)
         if points <= 0 or points >= 2**31:
             raise ValueError
     except ValueError:
         bot.messaging.send_message(
             peer=message.peer,
             text=f"Количество баллов должно быть натуральным числом, меньшим {2**31}. Попробуй ещё раз.",
             interactive_media_groups=back_to_moderation_keyboard(),
         )
         return
 
     context.update_data({"event_points": points})
 
     bot.messaging.send_message(
         peer=message.peer,
         text=(
-            "Шаг 8/9: Введи теги мероприятия через пробел (UUID):\n"
-            "Например: `a1b2c3d4-e5f6-7890-abcd-ef1234567890 f9e8d7c6-b5a4-3210-fedc-ba0987654321`\n"
+            "Шаг 8/9: Введи id тегов мероприятия через пробел:\n"
+            "Например: `tag_eco_1 tag_soc_1`\n"
             "Можно пропустить, отправив `-`:"
         ),
         interactive_media_groups=back_to_moderation_keyboard(),
     )
     context.set_state(EventCreateState.tags)
 
 
 @events_rt.message(state=EventCreateState.tags)
 @bot.di
-def event_create_tags_step(message: UpdateMessage, context: FSMContext):  # untested
+def event_create_tags_step(message: UpdateMessage, context: FSMContext):
     delete_prev_message(bot, message)
 
     raw = message.message.text_message.text.strip()
-    
-    if raw == "-":
-        tag_ids = []
-    else:
-        tag_ids = [t.strip() for t in raw.split() if t.strip()]
-        for tag_id in tag_ids:
-            try:
-                UUID(tag_id)
-            except Exception:
-                bot.messaging.send_message(
-                    peer=message.peer,
-                    text=(
-                        "⚠️ Один из тегов не является валидным UUID.\n"
-                        "Пожалуйста, введи теги через пробел, например:\n"
-                        "`a1b2c3d4-e5f6-7890-abcd-ef1234567890 f9e8d7c6-b5a4-3210-fedc-ba0987654321`\n"
-                        "Или отправь `-`, чтобы пропустить:"
-                    ),
-                    interactive_media_groups=back_to_moderation_keyboard(),
-                )
-                return
+    tag_ids = [] if raw == "-" else [t.strip() for t in raw.split() if t.strip()]
 
     context.update_data({"event_app_tag_ids": tag_ids})
 
     bot.messaging.send_message(
         peer=message.peer,
         text=(
             "Шаг 9/9: введи код мероприятия:\n"
             "Без пробелов, не больше 16 символов. Например: WELCOME2025"
         ),
         interactive_media_groups=back_to_moderation_keyboard(),
     )
     context.set_state(EventCreateState.code)
 
 
 @bot.di
-def event_tags_toggle_handler(  # half-tested
+def event_tags_toggle_handler(  # not in use, tag ui choosing is turned off for now
     event: UpdateInteractiveMediaEvent,
     context: FSMContext,
     tag_service: TagService,
     event_service: EventService,
 ):
     state = context.get_state()
     is_create = False  # интерфейс ввода тегов временно отключён
     is_edit = state == EventEditTagsState.tags or str(state) == str(EventEditTagsState.tags)
     if not (is_create or is_edit):
         return
 
     raw = str(event.data.value or "")
     data = context.get_data()
 
     key_field = "event_tag_keys" if is_create else "edit_tag_keys"
     selected = set(data.get(key_field) or [])
 
     all_tags = data.get("event_all_tags") or data.get("edit_all_tags")
     if not all_tags:
         tags = tag_service.get_all_tags()
         all_tags = [{"id": t.id, "title": t.title} for t in tags]
     else:
         norm = []
         for t in all_tags:
             if isinstance(t, dict):
                 norm.append(t)
             else:
                 norm.append({"id": getattr(t, "id", None), "title": getattr(t, "title", None)})
         all_tags = norm
 
     choices = choices_from_backend_tags(all_tags)
 
     if raw == TAGS_DONE_VALUE:
         title_to_id = {t["title"]: t["id"] for t in all_tags if t.get("title") and t.get("id")}
         app_tag_ids = [title_to_id[n] for n in selected if n in title_to_id]
 
         delete_prev_message_by_peer(bot, event.peer)
 
         if is_create:
             context.update_data({"event_app_tag_ids": app_tag_ids})
             bot.messaging.send_message(
                 peer=event.peer,
-                text=("Шаг 9/9: введи код мероприятия:\nБез пробелов, не больше 16 символов. Например: WELCOME2025"),
+                text=(
+                    "Шаг 9/9: введи код мероприятия:\nБез пробелов, не больше 16 символов. Например: WELCOME2025"
+                ),
                 interactive_media_groups=back_to_moderation_keyboard(),
             )
             context.set_state(EventCreateState.code)
             return
 
         event_id = data.get("edit_event_id")
         if not event_id:
             context.set_state(None)
             bot.messaging.send_message(
                 peer=event.peer, text="Сессия редактирования сброшена. Открой мероприятие заново."
             )
             return
 
         updated = event_service.update_event(
             event_id,
             {"app_tag_ids": app_tag_ids},
             requester_messenger_id=event.peer.id,
         )
         context.set_state(None)
 
         if not updated:
             _send_event_edit_menu(
                 event.peer,
                 event_id=event_id,
                 event_service=event_service,
                 note="⚠️ Не удалось обновить теги.",
                 event_obj=event_service.get_event_by_id(event_id),
                 show_organizer=True,
             )
             return
 
         _send_event_edit_menu(
             event.peer,
             event_id=event_id,
             event_service=event_service,
             note="✅ Теги обновлены.",
             event_obj=event_service.get_event_by_id(event_id),
             show_organizer=True,
         )
         return
 
     if raw.startswith(TAGS_TOGGLE_PREFIX):
         key = raw[len(TAGS_TOGGLE_PREFIX) :].strip()
         if key in selected:
             selected.remove(key)
         else:
             selected.add(key)
 
         context.update_data({key_field: list(selected)})
         delete_prev_message_by_peer(bot, event.peer)
 
         event_id = data.get("edit_event_id") or ""
         footer = [
             Button(
                 media_id="event_menu_edit",
                 value=event_id,
                 label="⬅️ К редактированию мероприятия",
             )
         ]
         title = "Редактирование тегов: выбери теги, затем нажми «Готово»:"
 
         _send_tags_toggle_ui(
             peer=event.peer,
             title=title,
             choices=choices,
             selected_keys=set(selected),
             footer_buttons=footer,
         )
         return
 
 
 @events_rt.message(state=EventCreateState.code)
 @bot.di
 def event_create_code_step(  # untested
     message: UpdateMessage,
     context: FSMContext,
     event_service: EventService,
-    report_service: ReportService,
 ):
     delete_prev_message(bot, message)
 
     code = message.message.text_message.text.strip()
     if not re.fullmatch(r"^[A-Za-zА-Яа-я0-9]{1,16}$", code):
         bot.messaging.send_message(
             peer=message.peer,
             text="⚠️ Код должен быть комбинацией русских и латинских букв и цифр, длиной не больше 16 символов. Попробуй еще раз:",
             interactive_media_groups=back_to_moderation_keyboard(),
         )
         return
 
     context.update_data({"event_bonus_code": code})
+    context.update_data({"hours": 0})  # TODO make a step for hours
     data = context.get_data()
     try:
         created = event_service.create_from_wizard(data, message.peer)
     except Exception as e:
+        logger.error("Error while creating an event via wizard: %s", e)
         bot.messaging.send_message(
             peer=message.peer,
             text="⚠️ Не удалось создать мероприятие.",
             interactive_media_groups=back_to_moderation_keyboard(),
         )
         context.clear()
         raise e
 
     context.clear()
 
-    note = "✅ Мероприятие успешно создано."
+    note = {
+        "REQUEST": "✅ Заявка на мероприятие успешно создана.",
+        "COMPLETELY": "✅ Мероприятие успешно создано.",
+        "FAILED": "⚠️ Произошла ошибка: не удалось создать заявку/мероприятие.",
+    }.get(
+        created.get("TYPE", ""),
+        "⚠️ Произошла ошибка: не получить статус заявки/мероприятия. Проверь результат в 'Своих заявках' или в 'Своих мероприятиях'.",
+    )
+
+    formatted_data = str(data).strip('{}').replace('), ', '),\n').replace('], ', '],\n').replace('\', ', '\',\n')
     bot.messaging.send_message(
         peer=message.peer,
-        text=f"{note}\n```\n{data}\n```",
+        text=f"{note}\n{formatted_data}",
         interactive_media_groups=moderation_menu_keyboard(),
     )
 
 
 @bot.di
 def event_view_by_id_start(event: UpdateInteractiveMediaEvent, context: FSMContext):  # untested
     """старт диалога - просим у модератора UUID мероприятия"""
     delete_prev_message_by_peer(bot, event.peer)
 
     bot.messaging.send_message(
         peer=event.peer,
         text="Введи UUID мероприятия, которое хочешь посмотреть.\n\n",
         interactive_media_groups=back_to_moderation_keyboard(),
     )
     context.set_state(EventViewState.wait_event_id)
 
 
 @events_rt.message(state=EventViewState.wait_event_id)
 @bot.di
 def event_view_by_id_step(  # untested
     message: UpdateMessage, context: FSMContext, event_service: EventService, user: UserSchema
 ):
     """читаем UUID, тянем мероприятие из бэкенда и показываем информацию"""
     delete_prev_message(bot, message)
     raw_id = message.message.text_message.text.strip()
     event = event_service.get_event_by_id(raw_id)
 
     if event is None:
         bot.messaging.send_message(
             peer=message.peer,
             text="⚠️ Мероприятие с таким UUID не найдено.\nПопробуй ещё раз. Пример UUID: `932cf1cb-1ee7-4a45-bc96-41a232f3f538`",
             interactive_media_groups=back_to_moderation_keyboard(),
         )
         return
 
     context.update_data({"current_event_id": event.id})
     context.set_state(None)
 
     send_event_card(
         message.peer,
         event_id=event.id,
         event_service=event_service,
         user=user,
         ctx_name="moderation_menu",
     )
 
 
 def _update_event_card(  # untested
     peer,
     event_service: EventService,
     event_id: str,
     can_manage: bool,
     show_organizer: bool,
 ):  # deprecated?
     refreshed = event_service.get_event_by_id(event_id)
     part = event_service.get_participation_status(event_id, peer.id)
     is_participant = part is not None
 
     prev = bot.messaging.load_message_history_sync(peer=peer, limit=1)[0]
     bot.messaging.update_message_sync(
         prev,
         text=format_event_details(refreshed, show_code=True, show_organizer=show_organizer)
         if refreshed
         else "⚠️ Мероприятие не найдено",
         interactive_media_groups=event_actions_keyboard(
             event_id,
             is_participant=is_participant,
             can_manage=can_manage,
             back_value="moderation",
             back_label="⬅️ В меню модерации",
             enter_code_media_id="event_menu_enter_code",
             sign_up_media_id="event_menu_sign_up",
             sign_out_media_id="event_menu_sign_out",
             ctx="moderation_menu",
         ),
     )
 
 
 @bot.di
 def event_menu_sign_up_handler(  # untested
     event: UpdateInteractiveMediaEvent,
     context: FSMContext,
     event_service: EventService,
     user: UserSchema | None,
 ):
     delete_prev_message_by_peer(bot, event.peer)
     context.set_state(None)
 
     event_id, ctx = split_event_value(event.data.value)
     ctx_name = ctx or "moderation"
     ok = event_service.sign_up(event_id, event.peer.id)
     if not ok:
         note = "⚠️ Не удалось записаться на мероприятие."
     else:
         note = "✅ Запись на мероприятие прошла успешно!"
 
     send_event_card(
         event.peer,
         event_id=event_id,
         event_service=event_service,
         user=user,
         ctx_name=ctx_name,
         note=note,
     )
 
 
 @bot.di
 def event_menu_sign_out_handler(  # untested
     event: UpdateInteractiveMediaEvent,
     context: FSMContext,
     event_service: EventService,
     user: UserSchema | None,
 ):
     delete_prev_message_by_peer(bot, event.peer)
     context.set_state(None)
 
     event_id, ctx = split_event_value(event.data.value)
     ctx_name = ctx or "moderation"
     status = event_service.get_participation_status(event_id, event.peer.id)
 
     if not status:
         note = "⚠️ Не удалось отписаться: ты не записан на это мероприятие."
     elif status.get("is_claimed"):
         note = "⚠️ Нельзя отписаться: баллы уже получен за это участие."
     else:
         ok = event_service.sign_out_by_event(event_id, event.peer.id)
         if ok:
             note = "✅ Ты успешно отписался от мероприятия."
         else:
             note = "⚠️ Не удалось отписаться. Попробуй ещё раз."
 
     send_event_card(
         event.peer,
         event_id=event_id,
         event_service=event_service,
         user=user,
         ctx_name=ctx_name,
         note=note,
     )
 
 
 @bot.di
 def event_menu_enter_code_start_handler(
     event: UpdateInteractiveMediaEvent, context: FSMContext
 ):  # untested
     delete_prev_message_by_peer(bot, event.peer)
     event_id, ctx = split_event_value(event.data.value)
     ctx_name = ctx or "moderation"
     context.update_data({"current_event_id": event_id, "current_event_ctx": ctx_name})
     context.set_state(EventEnterCodeState.wait_code)
 
     bot.messaging.send_message(
         peer=event.peer,
         text="🎟️ Введи бонус-код мероприятия (без пробелов):",
         interactive_media_groups=build_back_to_event_keyboard(event_id, ctx_name),
     )
 
 
 @events_rt.message(state=EventEnterCodeState.wait_code)
 @bot.di
 def event_menu_enter_code_step(  # untested
     message: UpdateMessage, context: FSMContext, event_service: EventService, user: UserSchema
 ):
     delete_prev_message(bot, message)
 
     event_id = context.get_data().get("current_event_id")
     code = message.message.text_message.text.strip()
     note = None
     status = event_service.get_participation_status(event_id, message.peer.id)
     if not status:
         note = "⚠️ Код не принят, сначала нужно записаться на мероприятие"
     elif status.get("is_claimed"):
         note = "⚠️ Код не принят, баллы уже получены"
     else:
         result = event_service.enter_code_by_event(event_id, message.peer.id, code)
         if not result:
             note = "⚠️ Код не принят, проверь правильность кода"
         else:
             note = f"✅ Код принят. Текущие баллы: {result.get('points', '-')}"
 
     data = context.get_data()
     ctx_name = data.get("current_event_ctx") or "moderation"
     context.set_state(None)
 
     send_event_card(
         message.peer,
         event_id=event_id,
         event_service=event_service,
         user=user,
         ctx_name=ctx_name,
         note=note,
     )
 
 
 @bot.di
 def event_menu_delete_handler(  # untested
     event: UpdateInteractiveMediaEvent,
     context: FSMContext,
     event_service: EventService,
     user: UserSchema | None,
 ):
     delete_prev_message_by_peer(bot, event.peer)
     context.set_state(None)
 
     event_id, ctx = split_event_value(event.data.value)
     ctx_name = ctx or "moderation"
     if ctx_name not in ("moderation", "moderation_menu"):
         ctx_name = "moderation"
 
     try:
         ok = event_service.delete_event(
             event_id,
             requester_messenger_id=event.peer.id,
         )
     except Exception:
         ok = False
 
     if ok:
         context.clear()
         if ctx_name == "moderation":
             send_event_cards_page(
                 event.peer,
                 offset=0,
                 event_service=event_service,
                 ctx_name="moderation",
                 note="Мероприятие удалено.",
             )
         else:
             bot.messaging.send_message(
                 peer=event.peer,
                 text="Мероприятие удалено.\n\nТы в доме модерации",
                 interactive_media_groups=moderation_menu_keyboard(),
             )
         return
 
     send_event_card(
         event.peer,
         event_id=event_id,
         event_service=event_service,
         user=user,
         ctx_name=ctx_name,
         note="⚠️ Не удалось удалить мероприятие",
     )
 
 
 def _send_event_edit_menu(  # untested
     peer,
     *,
     event_id: str,
     event_service: EventService,
     note: str | None = None,
     event_obj: EventSchema | None = None,
     show_organizer: bool,
 ):
     refreshed = event_obj or event_service.get_event_by_id(event_id)
     base = (
         format_event_details(refreshed, show_code=True, show_organizer=show_organizer)
         if refreshed
         else "⚠️ Мероприятие не найдено"
     )
 
     prefix = f"{note}\n\n" if note else ""
     bot.messaging.send_message(
         peer=peer,
         text=(f"{prefix}Редактирование мероприятия\n\n{base}\n\nВыбери поле для Редактирования:"),
         interactive_media_groups=event_edit_fields_keyboard(event_id),
     )
 
 
 @bot.di
 def event_menu_edit_handler(  # untested
     event: UpdateInteractiveMediaEvent, context: FSMContext, event_service: EventService
 ):
     delete_prev_message_by_peer(bot, event.peer)
     context.set_state(None)
     event_id, _ctx = split_event_value(event.data.value)
 
     _send_event_edit_menu(
         event.peer,
         event_id=event_id,
         event_service=event_service,
         show_organizer=True,
     )
 
 
 def _get_field_label(field: str) -> str:  # untested
     return {
         "name": "Название",
         "date": "Дата",
         "time": "Время",
         "location": "Локация",
         "tags": "Теги",
         "creator_id": "Организатор",
         "points": "Баллы",
         "description": "Описание",
         "bonus_code": "Код",
     }.get(field, field)
 
 
 def _get_current_value(event: EventSchema, field: str) -> str:  # untested
     val = getattr(event, field, None)
     if val is None or val == "":
         return "-"
     if field == "date":
         try:
             return event.date.strftime("%d.%m.%Y")
         except Exception:
             return str(val)
     if field == "time":
         try:
             return event.date.strftime("%H:%M")
         except Exception:
             return str(val)
 
     return str(val)
 
 
 def _send_tags_toggle_ui(  # untested
     *, peer, title: str, choices, selected_keys: set[str], footer_buttons: list[Button]
 ):
     bot.messaging.send_message(
         peer=peer,
         text=title,
         interactive_media_groups=tags_toggle_keyboard(
             choices=choices,
             selected_keys=selected_keys,
             media_id=TAGS_MEDIA_ID,
             toggle_value_prefix=TAGS_TOGGLE_PREFIX,
             done_value=TAGS_DONE_VALUE,
             done_label="✅ Готово",
             extra_footer=footer_buttons,
         ),
     )
 
 
 @bot.di
 def event_menu_edit_field_handler(  # untested
     event: UpdateInteractiveMediaEvent,
     context: FSMContext,
     event_service: EventService,
     tag_service: TagService,
 ):
     delete_prev_message_by_peer(bot, event.peer)
     raw = str(event.data.value or "")
     if "|" not in raw:
         bot.messaging.send_message(peer=event.peer, text="⚠️ Некорректная команда редактирования.")
         return
 
     event_id, field = raw.split("|", 1)
     field = field.strip()
 
     refreshed = event_service.get_event_by_id(event_id)
     if not refreshed:
         bot.messaging.send_message(peer=event.peer, text="⚠️ Мероприятие не найдено.")
         return
 
     if field == "tags":
         # редактируем теги через toggle UI
         all_tags = tag_service.get_all_tags()
         all_tags_dump = [{"id": t.id, "name": t.name} for t in all_tags]
         choices = choices_from_backend_tags(all_tags)
 
         selected_names: set[str] = set()
         for t in refreshed.tags or []:
             if isinstance(t, dict) and t.get("name"):
                 selected_names.add(str(t["name"]))
             else:
                 name = getattr(t, "name", None)
                 if name:
                     selected_names.add(str(name))
 
         context.update_data(
             {
                 "edit_event_id": event_id,
                 "edit_all_tags": all_tags_dump,
                 "edit_tag_keys": list(selected_names),
             }
         )
         context.set_state(EventEditTagsState.tags)
 
         _send_tags_toggle_ui(
             peer=event.peer,
             title="Редактирование тегов: выбери теги, затем нажми «Готово»:",
             choices=choices,
             selected_keys=set(selected_names),
             footer_buttons=[
                 Button(
                     media_id="event_menu_edit",
                     value=event_id,
                     label="⬅️ К редактированию мероприятия",
                 )
             ],
         )
         return
 
     context.update_data({"edit_event_id": event_id, "edit_field": field})
     context.set_state(EventEditState.wait_value)
 
     label = _get_field_label(field)
     cur = _get_current_value(refreshed, field)
 
     if field == "creator_id":
         hint = 'Введи UUID организатора или отправь минус "-", чтобы поставить организатором себя.'
     elif field == "date":
         hint = "Введи дату в формате `dd.mm.yyyy`, например: `20.02.2002`"
     elif field == "time":
         hint = "Введи время в формате `HH:MM`, например: `10:30`"
     else:
         hint = 'Введи новое значение. Чтобы очистить поле - отправь: "-"'
 
     bot.messaging.send_message(
         peer=event.peer, text=(f"Редактирование: {label}\nТекущее значение: {cur}\n{hint}")
     )
 
 
 @events_rt.message(state=EventEditState.wait_value)
 @bot.di
 def event_edit_value_step(  # untested
     message: UpdateMessage,
     context: FSMContext,
     event_service: EventService,
     user: UserSchema | None,
 ):
     delete_prev_message(bot, message)
 
     data = context.get_data()
     event_id = data.get("edit_event_id")
     field = data.get("edit_field")
 
     if not event_id or not field:
         context.set_state(None)
         bot.messaging.send_message(
             peer=message.peer, text="Сессия редактирования сброшена. Открой мероприятие заново."
         )
         return
 
     raw = message.message.text_message.text.strip()
     value: object = None if raw == "-" else raw
 
     if field == "creator_id":
         if raw == "-":
             if not user:
                 bot.messaging.send_message(
                     peer=message.peer, text="⚠️ Не удалось определить ваш user."
                 )
                 return
             if not user.id:
                 bot.messaging.send_message(
                     peer=message.peer, text="⚠️ Не удалось определить ваш user.id."
                 )
                 return
             value = user.id
         else:
             try:
                 value = str(UUID(raw))
             except Exception:
                 bot.messaging.send_message(
                     peer=message.peer,
                     text=(
                         'Организатор должен быть UUID пользователя или "-". Попробуй ещё раз.\n'
                         "Пример UUID: `d0a794d0-a128-47b7-b5f3-da53d19bfb07`"
                     ),
                 )
                 return
 
     if field == "points":
         if raw == "-":
             value = None
         else:
             try:
                 value = int(raw)
                 if value <= 0 or value >= 2**31:
                     raise ValueError
             except ValueError:
                 bot.messaging.send_message(
                     peer=message.peer,
                     text=(
                         "⚠️ Баллы должны быть натуральным числом,"
                         f" не большим {2**31}, попробуй еще раз."
                     ),
                 )
                 return
 
     if (
         field == "bonus_code"
         and value is not None
         and not re.fullmatch(r"^[A-Za-zА-Яа-я0-9]{1,16}$", str(value))
     ):
         bot.messaging.send_message(
             peer=message.peer,
             text=(
                 "Код должен быть до 16 символов: латинские и кириллические"
                 " буквы и цифры без пробелов до 16 символов."
             ),
         )
         return
 
     if field == "date" and value is not None:
         try:
             value = datetime.strptime(value, "%d.%m.%Y").date()
         except ValueError:
             bot.messaging.send_message(
                 peer=message.peer,
                 text=(
                     "⚠️ Я не смог распознать дату.\n"
                     "Пожалуйста, введи в формате `dd.mm.yyyy`, например: `20.02.2002`:"
                 ),
                 interactive_media_groups=back_to_moderation_keyboard(),
             )
             return
 
     if field == "time" and value is not None:
         try:
             value = datetime.strptime(value, "%H:%M").time()
         except ValueError:
             bot.messaging.send_message(
                 peer=message.peer,
                 text=(
                     "⚠️ Я не смог распознать время.\n"
                     "Пожалуйста, введи в формате `HH:MM`, например: `10:30`:"
                 ),
                 interactive_media_groups=back_to_moderation_keyboard(),
             )
             return
 
     if field in ("date", "time"):
         current = event_service.get_event_by_id(event_id)
         if not current:
             context.set_state(None)
             bot.messaging.send_message(peer=message.peer, text="⚠️ Мероприятие не найдено.")
             return
 
         current_dt = current.date
         tz = getattr(current_dt, "tzinfo", None)
 
         if value is None:
             bot.messaging.send_message(
                 peer=message.peer, text="⚠️ Дату/время нельзя очистить. Выбери другое значение."
             )
             return
 
         if field == "date":
             new_dt = datetime.combine(value, current_dt.time())
         else:
             new_dt = datetime.combine(current_dt.date(), value)
 
         if tz is not None:
             new_dt = new_dt.replace(tzinfo=tz)
 
         iso = new_dt.isoformat()
         if iso.endswith("+00:00"):
             iso = iso.replace("+00:00", "Z")
 
         payload = {"date": iso}
     else:
         payload = {field: value}
 
     updated = event_service.update_event(
         event_id,
         payload,
         requester_messenger_id=message.peer.id,
     )
     context.set_state(None)
 
     if not updated:
         _send_event_edit_menu(
             message.peer,
             event_id=event_id,
             event_service=event_service,
             note="⚠️ Не удалось обновить поле.",
             event_obj=event_service.get_event_by_id(event_id),
             show_organizer=True,
         )
         return
 
     _send_event_edit_menu(
         message.peer,
         event_id=event_id,
         event_service=event_service,
         note="✅ Поле обновлено.",
         event_obj=event_service.get_event_by_id(event_id),
         show_organizer=True,
     )
diff --git a/services/bot-new/core/services/event.py b/services/bot-new/core/services/event.py
index b3274fe..78eb817 100644
--- a/services/bot-new/core/services/event.py
+++ b/services/bot-new/core/services/event.py
@@ -1,159 +1,161 @@
 from datetime import datetime, timezone
 from typing import Any
 
 from requests import HTTPError
 
 from core.clients.event import EventClient
 from core.schemas.event import EventCardSchema
 from core.services.base import BaseService
 from core.services.registry import BaseServiceRegistry
-from core.utils.logger import logger
 
 
 class EventService(BaseService):  # untested
     """
     сервис мероприятий на стороне бота.
 
     на данный момент:
     - ходит в backend через EventClient
     - умеет получать список мероприятий по городу
     - умеет получать одно мероприятие по id
     - создает мероприятие через клиент используя мастера создания
     """
 
     def __init__(self, registry: BaseServiceRegistry, client: EventClient):  # untested
         # DI может вызвать клиент без аргументов.
         super().__init__(registry)
         self.client = client
 
     def get_event_cards_page(
         self,
         *,
         requester_messenger_id: int,
         page: int = 1,
         limit: int = 10,
         title_str: str | None = None,
         tag_ids: list[str] | None = None,
     ) -> tuple[list[EventCardSchema], int, int]:  # меняем тип возвращаемого списка
         resp = self.client.get_event_cards_page(
             requester_messenger_id=requester_messenger_id,
             page=page,
             limit=limit,
             title_str=title_str,
             tag_ids=tag_ids,
         )
 
         match resp.status_code:
             case 200:
                 pass
             case 400:
                 raise HTTPError("Error while getting event cards list: invalid parameters")
             case 401:
                 raise HTTPError("Error while getting event cards list: unauthorized")
             case 500:
                 raise HTTPError("Error while getting event cards list: internal server error")
             case _:
                 raise HTTPError(
                     f"Error while getting event cards list: unexpected status {resp.status_code}"
                 )
 
         body = resp.json() or {}
         items = body.get("event_cards") or []
         page_meta = body.get("page_meta") or {}
 
         result: list[EventCardSchema] = []
         for item in items:
             if not isinstance(item, dict):
                 continue
             result.append(self._map_event_card(item))
 
         current_page = int(page_meta.get("current_page") or page)
         total_pages = int(page_meta.get("total_pages") or current_page)
 
         return result, current_page, total_pages
 
     def sign_up(self, event_id: str, messenger_id: int) -> bool:  # untested
         """messenger_id= = peer.id (как на бэкенде: user.get_by_messenger_id)"""
         resp = self.client.sign_up(event_id=event_id, messenger_id=messenger_id)
         return resp.status_code == 200
 
     def create_event(  # untested
         self,
         *,
         title: str,
         description: str,
         date_obj: datetime,
         gosp_id: str,
         project_id: str,
         verification_code: str,
         hours: int,
         requester_messenger_id: int,
         tags: list[str],
     ):
         """
         создает мероприятие через backend и возвращает dict из backend'a
         - ожидает, что все данные уже провалидированы и разобраны
         - метод не мапит ответ backend'a в EventSchema и не делает никакой обработки, а возвращает ровно тот json, который вернул backend (dect)
         - может бросить Exception, если status_code не 201/200
         """
         date_iso = date_obj.isoformat()
 
         resp = self.client.create_event(
             title=title,
             description=description,
             date_time=date_iso,
             gosp_id=gosp_id,
             project_id=project_id,
             verification_code=verification_code,
             hours=hours,
             requester_messenger_id=requester_messenger_id,
             tags=tags,
         )
 
         if resp.status_code not in [200, 201]:
             raise Exception(f"Error while creating event: {resp.status_code}\n{resp.text}")
 
         return resp.json()
 
     def create_from_wizard(self, data: dict[str, Any], peer) -> dict[str, Any]:
         title = data.get("event_name", "")
         description = data.get("event_description", "")
         date_obj = data.get("event_date", "")
         time_obj = data.get("event_time", "")
-        # надо додел
-        gosp_id = data.get("gosp_id", "666666666666")
-        # пока не делаем пусть отдыхает
+        gosp_id = data.get("gosp_id", "")
         project_id = data.get("project_id", "")
         hours = data.get("hours", "")
 
         verification_code = data.get("event_bonus_code", "")
-        # Теги: массив UUID, уже сформированный в хендлере # TODO not uuid
+        # Теги: массив ID, сформированный в хендлере
         tags: list[str] = data.get("event_app_tag_ids") or []
         # === Формирование RFC3339-совместимой строки ===
         event_dt = datetime.combine(date_obj, time_obj).replace(tzinfo=timezone.utc)
         date_time = event_dt.isoformat().replace("+00:00", "Z")
 
         resp = self.client.create_event(
             title=title,
             description=description,
             date_time=date_time,
             gosp_id=gosp_id,
             project_id=project_id,
             verification_code=verification_code,
             hours=hours,
             requester_messenger_id=peer.id,
             tags=tags if tags else None,
         )
 
-        logger.info("TYPE OF CREATED ENTITY: %s", resp.json().get("code"))
+        match resp.status_code:
+            case 200:
+                pass
+            case 401:
+                raise HTTPError("Error while getting user: unauthorized")
+            case 500:
+                raise HTTPError("Error while getting user: internal server error")
+            case _:
+                raise HTTPError("Error while getting user: unexpected status {resp.status_code}")
 
-        if resp.status_code not in [200, 201]:
-            raise HTTPError(f"Error while creating event: {resp.status_code}\n{resp.text}")
-        # подключить когда сделают карточку мероприятия
-        return {}
+        return {"TYPE": resp.json().get("code")}  # показывать карточку мероприятия
 
     def _map_event_card(self, body: dict[str, Any]) -> EventCardSchema:
         """преобразует json event card в EventCardSchema"""
         from core.schemas.event import EventCardSchema
 
         return EventCardSchema.from_dict(body)
Editor is loading...
Leave a Comment