Untitled
4ae4d
plain_text
21 days ago
108 kB
5
Indexable
f=services/bot/core/services/event.py
f=services/bot/core/clients/event.py
f=services/bot/core/schemas/event.py
f=services/bot/core/utils/events_filter.py
f=services/bot/core/utils/events_filters.py
f=services/bot/core/utils/__init__.py
f=services/bot/core/handlers/events_filters.py
f=services/bot/core/markups/event.py
f=services/bot/core/markups/__init__.py
f=services/bot/core/handlers/moderation_events.py
f=services/bot/core/handlers/my_events.py
f=services/bot/core/handlers/points.py
f=services/bot/core/handlers/__init__.py
--- services/bot/core/services/event.py ---
from datetime import datetime, timezone
from typing import Any
from core.clients import EventClient
from core.schemas import EventCardSchema, ParticipationSchema
from core.services.base import BaseService
from core.services.registry import BaseServiceRegistry
class EventService(BaseService):
"""
сервис мероприятий на стороне бота.
на данный момент:
- ходит в backend через EventClient
- умеет получать список мероприятий с фильтрацией по ТБ и тегам
- умеет получать одно мероприятие по id
- создает мероприятие через клиент используя мастера создания
"""
def __init__(self, registry: BaseServiceRegistry, client: EventClient):
# DI может вызвать клиент без аргументов.
super().__init__(registry)
self.client = client
def get_event_cards_page(
self,
*,
requester_messenger_id: str,
page: int = 1,
limit: int = 10,
title_str: str | None = None,
tag_ids: list[str] | None = None,
mine: bool = False,
) -> tuple[list[EventCardSchema], int, int]:
resp = (
self.client.get_my_event_cards_page(
requester_messenger_id=requester_messenger_id,
page=page,
limit=limit,
title_str=title_str,
tag_ids=tag_ids,
)
if mine
else self.client.get_event_cards_page(
requester_messenger_id=requester_messenger_id,
page=page,
limit=limit,
title_str=title_str,
tag_ids=tag_ids,
)
)
self._check_response(resp, ctx="getting event card list")
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):
raise ValueError("Invalid event card contract: item must be dict")
result.append(EventCardSchema.from_dict(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: str) -> str | None:
"""messenger_id = peer.id (как на бэкенде: user.get_by_messenger_id)"""
resp = self.client.sign_up(event_id=event_id, messenger_id=messenger_id)
if resp.status_code == 409:
return resp.json()["error"]
self._check_response(resp, ctx="signing up")
return None
def get_event_by_id(
self,
event_id: str,
requester_messenger_id: str,
) -> EventCardSchema | None:
resp = self.client.get_event_info(
event_id=event_id,
requester_messenger_id=requester_messenger_id,
)
if resp.status_code == 400:
return None
self._check_response(resp, ctx="getting event card")
return EventCardSchema.from_dict(resp.json() or {})
def sign_out_by_event(self, event_id: str, messenger_id: str) -> bool:
resp = self.client.sign_out(event_id=event_id, messenger_id=messenger_id)
return resp.status_code == 200
def enter_code_by_event(
self,
event_id: str,
messenger_id: str,
code: str,
) -> ParticipationSchema | None:
resp = self.client.confirm_participation(
event_id=event_id,
messenger_id=messenger_id,
code=code,
)
if resp.status_code in {400, 404}:
return None
self._check_response(resp, ctx="confirming participation")
return self.get_participation_status(
event_id=event_id,
requester_messenger_id=messenger_id,
)
def get_participation_status(self, event_id: str, requester_messenger_id: str):
resp = self.client.get_event_info(event_id, requester_messenger_id)
card = EventCardSchema.from_dict(resp.json() or {})
return card.participation
def add_tag(self, event_id: str, tag_id: str, messenger_id: str) -> bool:
resp = self.client.add_tag(event_id=event_id, tag_id=tag_id, messenger_id=messenger_id)
return resp.status_code == 200
def remove_tag(self, event_id: str, tag_id: str, messenger_id: str) -> bool:
resp = self.client.remove_tag(event_id=event_id, tag_id=tag_id, messenger_id=messenger_id)
return resp.status_code == 200
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", "")
gosb_id = data.get("gosb_id", "")
project_id = data.get("project_id", "")
hours = data.get("hours", "")
participation_limit = data.get("event_participation_limit", "")
verification_code = data.get("event_verification_code", "")
# Теги: массив 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,
gosb_id=gosb_id,
project_id=project_id,
verification_code=verification_code,
hours=hours,
participation_limit=participation_limit,
requester_messenger_id=peer.id,
tags=tags if tags else None,
)
self._check_response(resp, ctx="creating event")
body = resp.json() or {}
return {
"TYPE": resp.json().get("create_type"),
"event_id": body.get("event_id"),
"request_id": body.get("request_id"),
}
def update_event(self, event_id: str, messenger_id: str, payload: dict[str, int | str]) -> bool:
resp = self.client.update_event(
event_id=event_id,
messenger_id=messenger_id,
payload=payload,
)
self._check_response(resp, ctx="patching event")
return True
--- services/bot/core/clients/event.py ---
from core.clients.base import BaseApiClient
class EventClient(BaseApiClient):
def get_event_cards_page(
self,
*,
requester_messenger_id: str,
page: int = 1,
limit: int = 10,
title_str: str | None = None,
tag_ids: list[str] | None = None,
):
params: dict = {
"page": page,
"limit": limit,
}
if title_str:
params["title_str"] = title_str
if tag_ids:
params["tag_id"] = tag_ids
return self.get(
"/events/card/list",
headers={"Authorization": requester_messenger_id},
params=params,
)
def get_my_event_cards_page(
self,
*,
requester_messenger_id: str,
page: int = 1,
limit: int = 10,
title_str: str | None = None,
tag_ids: list[str] | None = None,
):
params: dict = {
"page": page,
"limit": limit,
}
if title_str:
params["title_str"] = title_str
if tag_ids:
params["tag_id"] = tag_ids
return self.get(
"/events/card/list/mine",
headers={"Authorization": requester_messenger_id},
params=params,
)
def get_event_info(self, event_id: str, requester_messenger_id: str):
"""GET /events/{event_id}/card"""
return self.get(
f"/events/{event_id}/card",
headers={"Authorization": requester_messenger_id},
)
def sign_up(self, *, event_id: str, messenger_id: str):
"""POST /events/{event_id}/participations/signup"""
return self.post(
f"/events/{event_id}/participations/signup",
headers={"Authorization": messenger_id},
)
def sign_out(self, *, event_id: str, messenger_id: str):
"""DELETE /events/{event_id}/participations/cancel"""
return self.delete(
f"/events/{event_id}/participations/cancel",
headers={"Authorization": messenger_id},
)
def confirm_participation(self, *, event_id: str, messenger_id: str, code: str):
"""POST /events/{event_id}/participations/confirm"""
return self.post(
f"/events/{event_id}/participations/confirm",
headers={"Authorization": messenger_id},
json={"code": code},
)
def add_tag(self, *, event_id: str, tag_id: str, messenger_id: str):
"""POST /events/{event_id}/tags"""
return self.post(
f"/events/{event_id}/tags",
headers={"Authorization": messenger_id},
json={"tag_id": tag_id},
)
def remove_tag(self, *, event_id: str, tag_id: str, messenger_id: str):
"""DELETE /events/{event_id}/tags"""
return self.delete(
f"/events/{event_id}/tags",
headers={"Authorization": messenger_id},
json={"tag_id": tag_id},
)
def create_event(
self,
*,
title: str,
description: str,
date_time: str,
gosb_id: str,
project_id: str = "",
verification_code: str,
hours: int,
participation_limit: int,
requester_messenger_id: str,
tags: list[str] | None = None,
):
"""POST /events"""
payload = {
"activation_code": verification_code,
"hours": hours,
"date_time": date_time,
"description": description,
"gosb_id": gosb_id,
"project_id": project_id,
"title": title,
"participation_limit": participation_limit,
}
if tags:
payload["selected_tags"] = tags
return self.post(
"/events",
headers={"Authorization": requester_messenger_id},
json=payload,
)
def update_event(self, event_id: str, messenger_id: str, payload: dict[str, int | str]):
"""PATCH /events/{event_id}"""
return self.patch(
f"/events/{event_id}",
headers={"Authorization": messenger_id},
json=payload,
)
--- services/bot/core/schemas/event.py ---
from datetime import datetime
from typing import Any
from pydantic import BaseModel, Field
from core.schemas.gosb import GosbSchema
from core.schemas.participation import ParticipationSchema
from core.schemas.tag import TagSchema
from core.schemas.terbank import TerbankSchema
from core.utils import logger, parse_datetime_value, require_datetime
class AllowedActionsSchema(BaseModel):
"""Схема доступных действий"""
can_view_code: bool
can_edit: bool
can_sign_up: bool
can_sign_out: bool
@classmethod
def from_dict(cls, data: dict[str, Any]) -> "AllowedActionsSchema":
return cls(
can_view_code=bool(data.get("can_view_code", False)),
can_edit=bool(data.get("can_edit", False)),
can_sign_up=bool(data.get("can_signup", False)),
can_sign_out=bool(data.get("can_signout", False)),
)
class EventCardSchema(BaseModel):
"""Схема карточки мероприятия из списка"""
event: "EventSchema"
tags: list[TagSchema] = Field(default_factory=list)
gosb: GosbSchema
terbank: TerbankSchema
participation: ParticipationSchema | None = None
allowed_actions: AllowedActionsSchema | None = None
@classmethod
def from_dict(cls, data: dict[str, Any]) -> "EventCardSchema":
gosb_data = data.get("gosb")
logger.info(f"DATA: {data}")
if not isinstance(gosb_data, dict):
logger.error(f"{gosb_data}, {type(gosb_data)}")
raise ValueError("Invalid event card contract: gosb is required")
gosb = GosbSchema.from_dict(gosb_data)
terbank_data = data.get("terbank")
if not isinstance(terbank_data, dict):
raise ValueError("Invalid event card contract: terbank is required")
terbank = TerbankSchema.from_dict(terbank_data)
participation_data = data.get("participation")
participation = None
if participation_data is not None:
if not isinstance(participation_data, dict):
raise ValueError("Invalid event card contract: participation must be dict or None")
participation = ParticipationSchema.from_dict(participation_data)
tags_data = data.get("tags") or []
tags = []
for tag_data in tags_data:
if not isinstance(tag_data, dict):
raise ValueError("Invalid event card contract: item in 'tags_data' must be dict")
tags.append(TagSchema.from_dict(tag_data))
allowed_actions_data = data.get("allowed_actions")
allowed_actions = None
if allowed_actions_data is not None:
if not isinstance(allowed_actions_data, dict):
raise ValueError(
"Invalid event card contract: allowed_actions must be dict or None"
)
allowed_actions = AllowedActionsSchema.from_dict(allowed_actions_data)
event_data = data.get("event")
if not isinstance(event_data, dict):
raise ValueError("Invalid event card contract: event is required")
event = EventSchema(
id=str(event_data.get("id", "")),
title=str(event_data.get("title", "")),
description=str(event_data.get("description", "")),
date_time=require_datetime(event_data.get("date_time"), "event.date_time"),
creator_id=str(event_data.get("creator_id", "")),
gosb_id=str(event_data.get("gosb_id", "")),
project_id=str(event_data.get("project_id", "")),
archived_at=parse_datetime_value(event_data.get("archived_at"))
if event_data.get("archived_at")
else None,
verification_code=str(event_data.get("verification_code"))
if event_data.get("verification_code")
else None,
deleted=bool(event_data.get("deleted", False)),
active=bool(event_data.get("active", True)),
created_at=parse_datetime_value(event_data.get("created_at"))
if event_data.get("created_at")
else None,
updated_at=parse_datetime_value(event_data.get("updated_at"))
if event_data.get("updated_at")
else None,
hours=int(event_data.get("hours", 0)),
participation_limit=int(event_data.get("participation_limit", 0)),
participation_count=int(event_data.get("participation_count", 0)),
)
return cls(
event=event,
tags=tags,
gosb=gosb,
terbank=terbank,
participation=participation,
allowed_actions=allowed_actions,
)
class EventSchema(BaseModel):
"""Полная схема мероприятия"""
id: str
title: str
description: str
date_time: datetime
creator_id: str
gosb_id: str
project_id: str
verification_code: str | None = None
archived_at: datetime | None = None
deleted: bool = False
active: bool = True
hours: int
participation_limit: int
participation_count: int
created_at: datetime | None = None
updated_at: datetime | None = None
class EventCardsPageSchema(BaseModel):
"""Схема страницы с карточками мероприятий"""
event_cards: list[EventCardSchema] # добавляем поле для карточек
current_page: int
total_pages: int
@classmethod
def from_response(cls, response_data: dict[str, Any]) -> "EventCardsPageSchema":
"""Создаёт схему из ответа бэкенда"""
cards_data = response_data.get("event_cards") or []
if not isinstance(cards_data, list):
raise ValueError("Invalid event card contract: cards_data must be list")
page_meta = response_data.get("page_meta") or {}
if not isinstance(page_meta, dict):
raise ValueError("Invalid event card contract: page_meta must be dict")
event_cards: list[EventCardSchema] = []
for card_data in cards_data:
if not isinstance(card_data, dict):
raise ValueError("Invalid event card contract: item must be dict")
event_cards.append(EventCardSchema.from_dict(card_data))
return cls(
event_cards=event_cards,
current_page=int(page_meta.get("current_page", 1)),
total_pages=int(page_meta.get("total_pages", 1)),
)
@property
def has_prev(self) -> bool:
return self.current_page > 1
@property
def has_next(self) -> bool:
return self.current_page < self.total_pages
--- services/bot/core/utils/events_filter.py ---
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any, Literal, cast
EventsMode = Literal["all", "participant", "organizer"]
FILTERS_CTX_KEY = "events_filters_v1"
@dataclass(slots=True)
class EventsFilter: # legacy
"""
фильтры списка карточек мероприятий на стороне бота.
текущий бэкенд поддерживает только фильтр по тегам.
"""
tag_ids: list[str] = field(default_factory=list)
tag_names: list[str] = field(default_factory=list)
mode: EventsMode = "all"
# UI-only (пока не поддержано backend)
date_from: str | None = None
date_to: str | None = None
@classmethod
def from_dict(cls, raw: Any) -> EventsFilter: # legacy
if not isinstance(raw, dict):
return cls()
mode_raw = raw.get("mode") or "all"
if mode_raw not in ("all", "participant", "organizer"):
mode_raw = "all"
mode: EventsMode = cast(EventsMode, mode_raw)
tag_ids_raw = raw.get("tag_ids") or []
if not isinstance(tag_ids_raw, list):
tag_ids_raw = []
tag_ids = [str(x) for x in tag_ids_raw if str(x).strip()]
tag_names_raw = raw.get("tag_names") or []
if not isinstance(tag_names_raw, list):
tag_names_raw = []
tag_names = [str(x) for x in tag_names_raw if str(x).strip()]
date_from = raw.get("date_from")
date_from = str(date_from) if date_from else None
date_to = raw.get("date_to")
date_to = str(date_to) if date_to else None
return cls(
tag_ids=tag_ids,
tag_names=tag_names,
mode=mode,
date_from=date_from,
date_to=date_to,
)
def to_dict(self) -> dict[str, Any]: # legacy
return {
"tag_ids": list(self.tag_ids or []),
"tag_names": list(self.tag_names or []),
"mode": self.mode,
"date_from": self.date_from,
"date_to": self.date_to,
}
@dataclass(frozen=True, slots=True)
class EventsSearchBase: # legacy
"""
базовые параметры конкретного списка (контекст):
- например points всегда only_claimed=True
- my_events (организатор) может фиксировать режим
"""
requester_messenger_id: str | None
only_claimed: bool = False
only_organizer: bool = False
only_participant: bool = False
has_report: bool | None = None
tag_ids: list[str] = field(default_factory=list)
# если True — mode из EventsFilter игнорируется
lock_mode: bool = False
def build_events_search_params( # legacy
*,
base: EventsSearchBase,
flt: EventsFilter | None,
offset: int,
limit: int,
) -> dict[str, Any]:
"""собираем kwargs для EventService.search_events_with_total(...)"""
f = flt or EventsFilter()
# tags: если в фильтре задано — переопределяет базу
tag_ids = f.tag_ids if f.tag_ids else (base.tag_ids or [])
only_claimed = bool(base.only_claimed)
only_organizer = bool(base.only_organizer)
only_participant = bool(base.only_participant)
# mode: работаем только с organizer/participant
if not base.lock_mode:
if f.mode == "organizer":
only_organizer = True
only_participant = False
elif f.mode == "participant":
only_participant = True
only_organizer = False
else:
# all
# оставляем только то, что задано базой
pass
params: dict[str, Any] = {
"offset": int(offset),
"limit": int(limit),
"only_claimed": bool(only_claimed),
"only_organizer": bool(only_organizer),
"only_participant": bool(only_participant),
}
if base.requester_messenger_id is not None:
params["requester_messenger_id"] = int(base.requester_messenger_id)
if base.has_report is not None:
params["has_report"] = bool(base.has_report)
if tag_ids:
params["tag_ids"] = list(tag_ids)
return params
def format_events_filter_summary( # legacy
flt: EventsFilter | None,
*,
locked_mode: EventsMode | None = None,
) -> str:
f = flt or EventsFilter()
parts: list[str] = []
eff_mode: EventsMode = (
locked_mode if locked_mode in ("all", "participant", "organizer") else f.mode
)
suffix = " (заблокирован)" if locked_mode in ("participant", "organizer") else ""
if f.tag_names:
names = [str(x).strip() for x in f.tag_names if str(x).strip()]
parts.append("теги: " + ", ".join(names))
elif f.tag_ids:
parts.append(f"теги: {len(f.tag_ids)}")
if eff_mode == "participant":
parts.append("режим: участвую" + suffix)
elif eff_mode == "organizer":
parts.append("режим: организую" + suffix)
if f.date_from or f.date_to:
a = f.date_from or "..."
b = f.date_to or "..."
parts.append(f"даты: {a} - {b}")
if not parts:
return "Фильтр\nбез доп. условий"
return "Фильтр\n" + "\n".join(parts)
def get_events_filter(context, *, ctx_name: str) -> EventsFilter: # legacy
data = (context.get_data() or {}).get(FILTERS_CTX_KEY) or {}
if not isinstance(data, dict):
return EventsFilter()
return EventsFilter.from_dict(data.get(str(ctx_name)))
def set_events_filter(context, *, ctx_name: str, flt: EventsFilter) -> None: # legacy
raw = (context.get_data() or {}).get(FILTERS_CTX_KEY) or {}
data = dict(raw) if isinstance(raw, dict) else {}
data[str(ctx_name)] = flt.to_dict()
context.update_data({FILTERS_CTX_KEY: data})
def reset_events_filter(context, *, ctx_name: str) -> None: # legacy
raw = (context.get_data() or {}).get(FILTERS_CTX_KEY) or {}
data = dict(raw) if isinstance(raw, dict) else {}
data.pop(str(ctx_name), None)
context.update_data({FILTERS_CTX_KEY: data})
def clear_context_keep_events_filters(context) -> None: # legacy
"""
нужен для мест, где раньше делали context.clear(),
но теперь фильтры должны переживать пагинацию
"""
data = context.get_data() or {}
keep = data.get(FILTERS_CTX_KEY)
context.clear()
if keep is not None:
context.update_data({FILTERS_CTX_KEY: keep})
Error: 'services/bot/core/utils/events_filters.py' is not a valid file
--- services/bot/core/utils/__init__.py ---
from .date_time import format_datetime, parse_datetime_value, require_datetime
from .events_filter import (
EventsFilter,
EventsMode,
clear_context_keep_events_filters,
format_events_filter_summary,
get_events_filter,
reset_events_filter,
set_events_filter,
)
from .logger import logger
from .messages import delete_prev_message, delete_prev_message_by_peer, patch_bot_messaging
from .text_wrap import wrap_text
__all__ = [
"EventsFilter",
"EventsMode",
"clear_context_keep_events_filters",
"delete_prev_message",
"delete_prev_message_by_peer",
"format_datetime",
"format_events_filter_summary",
"get_events_filter",
"logger",
"parse_datetime_value",
"patch_bot_messaging",
"require_datetime",
"reset_events_filter",
"set_events_filter",
"wrap_text",
]
--- services/bot/core/handlers/events_filters.py ---
from dialog_bot_sdk.entities.messaging import UpdateInteractiveMediaEvent
from core.bot_kit.fsm import FSMContext, State, StatesGroup
from core.config import bot
from core.handlers.events_ui import send_event_cards_page, send_my_events_page, send_points_page
from core.markups import TagChoice, events_filters_menu_keyboard, events_filters_mode_keyboard
from core.services import EventService, TagService
from core.utils import (
delete_prev_message_by_peer,
format_events_filter_summary,
get_events_filter,
reset_events_filter,
set_events_filter,
)
from core.utils.tags_toggle_ui import TagsToggleCfg, is_done, parse_toggle_key, send_tags_toggle_ui
class EventsFiltersTagsState(StatesGroup): # legacy
tags = State()
class EventsFiltersModeState(StatesGroup): # legacy
mode = State()
FILTERS_TAGS_CFG = TagsToggleCfg(
media_id="events_filters_tags_toggle",
toggle_prefix="toggle:",
done_value="done",
)
_CTX_KEY = "events_filters_edit_ctx"
_OFFSET_KEY = "events_filters_edit_offset"
_TAG_IDS_KEY = "events_filters_edit_tags_selected"
_MODE_KEY = "events_filters_mode_selected"
_MODE_LOCKED_KEY = "events_filters_mode_locked"
def _locked_mode_for_ctx(ctx: str) -> str | None: # legacy
if ctx == "my_events":
return "organizer"
if ctx in ("part_events", "points"):
return "participant"
return None
def _split_ctx_offset(raw: object) -> tuple[str, int]: # legacy
s = str(raw or "").strip()
if "|" not in s:
return (s or "events", 0)
a, b = s.split("|", 1)
ctx = (a or "events").strip()
try:
off = int(str(b).strip())
if off < 0:
off = 0
except Exception:
off = 0
return ctx, off
def _send_list( # legacy
*, peer, ctx_name: str, offset: int, context: FSMContext, event_service: EventService
):
# apply/back используют эту маршрутизацию
if ctx_name == "points":
return send_points_page(peer, offset=offset, event_service=event_service, context=context)
if ctx_name == "my_events":
return send_my_events_page(
peer,
offset=offset,
event_service=event_service,
requester_messenger_id=peer.id,
context=context,
)
# events / moderation
return send_event_cards_page(
peer,
offset=offset,
event_service=event_service,
ctx_name=ctx_name,
context=context,
)
@bot.di
def events_filters_open_handler( # legacy
event: UpdateInteractiveMediaEvent, context: FSMContext, event_service: EventService
):
delete_prev_message_by_peer(bot, event.peer)
context.set_state(None)
ctx_name, offset = _split_ctx_offset(event.data.value)
flt = get_events_filter(context, ctx_name=ctx_name)
summary = format_events_filter_summary(flt)
locked_mode = _locked_mode_for_ctx(ctx_name)
locked_note = ""
if locked_mode == "participant":
locked_note = "\n\n🔒 Режим зафиксирован: *участник*"
elif locked_mode == "organizer":
locked_note = "\n\n🔒 Режим зафиксирован: *организатор*"
bot.messaging.send_message(
peer=event.peer,
text=f"🧰 *Фильтры*\n\n{summary}{locked_note}\n\nВыбери, что изменить:",
interactive_media_groups=events_filters_menu_keyboard(ctx_name=ctx_name, offset=offset),
)
@bot.di
def events_filters_reset_handler( # legacy
event: UpdateInteractiveMediaEvent, context: FSMContext, event_service: EventService
):
delete_prev_message_by_peer(bot, event.peer)
context.set_state(None)
ctx_name, offset = _split_ctx_offset(event.data.value)
reset_events_filter(context, ctx_name=ctx_name)
# остаёмся в меню, чтобы пользователь видел что “сбросилось”
flt = get_events_filter(context, ctx_name=ctx_name)
summary = format_events_filter_summary(flt)
locked_mode = _locked_mode_for_ctx(ctx_name)
locked_note = ""
if locked_mode == "participant":
locked_note = "\n\n🔒 Режим зафиксирован: *участник*"
elif locked_mode == "organizer":
locked_note = "\n\n🔒 Режим зафиксирован: *организатор*"
bot.messaging.send_message(
peer=event.peer,
text=f"🧰 *Фильтры*\n\n{summary}{locked_note}\n\nВыбери, что изменить:",
interactive_media_groups=events_filters_menu_keyboard(ctx_name=ctx_name, offset=offset),
)
@bot.di
def events_filters_apply_handler( # legacy
event: UpdateInteractiveMediaEvent, context: FSMContext, event_service: EventService
):
delete_prev_message_by_peer(bot, event.peer)
context.set_state(None)
ctx_name, _offset = _split_ctx_offset(event.data.value)
# безопаснее возвращать на первую страницу (фильтр мог сузить выдачу)
_send_list(
peer=event.peer, ctx_name=ctx_name, offset=0, context=context, event_service=event_service
)
@bot.di
def events_filters_back_handler( # legacy
event: UpdateInteractiveMediaEvent, context: FSMContext, event_service: EventService
):
delete_prev_message_by_peer(bot, event.peer)
context.set_state(None)
ctx_name, offset = _split_ctx_offset(event.data.value)
_send_list(
peer=event.peer,
ctx_name=ctx_name,
offset=offset,
context=context,
event_service=event_service,
)
@bot.di
def events_filters_not_implemented_handler(
event: UpdateInteractiveMediaEvent, context: FSMContext
): # legacy
# handlers для tags/date/mode до следующих коммитов
delete_prev_message_by_peer(bot, event.peer)
context.set_state(None)
ctx_name, offset = _split_ctx_offset(event.data.value)
flt = get_events_filter(context, ctx_name=ctx_name)
summary = format_events_filter_summary(flt)
bot.messaging.send_message(
peer=event.peer,
text=f"⚠️ Пока не реализовано в этом коммите.\n\n{summary}\n\nВыбери, что изменить:",
interactive_media_groups=events_filters_menu_keyboard(ctx_name=ctx_name, offset=offset),
)
def _tag_choices_from_backend(all_tags) -> list[TagChoice]: # legacy
out: list[TagChoice] = []
for t in all_tags or []:
tid = getattr(t, "id", None)
name = getattr(t, "name", None)
if tid and name:
out.append(TagChoice(key=str(tid), label=str(name)))
return out
@bot.di
def events_filters_tags_open_handler( # legacy
event: UpdateInteractiveMediaEvent,
context: FSMContext,
event_service: EventService,
tag_service: TagService,
):
delete_prev_message_by_peer(bot, event.peer)
context.set_state(None)
ctx_name, offset = _split_ctx_offset(event.data.value)
flt = get_events_filter(context, ctx_name=ctx_name)
selected_ids = set(flt.tag_ids or [])
all_tags = tag_service.get_all_tags()
choices = _tag_choices_from_backend(all_tags)
# сохраняем "куда вернуться"
context.update_data(
{
_CTX_KEY: ctx_name,
_OFFSET_KEY: int(offset),
_TAG_IDS_KEY: list(selected_ids),
}
)
context.set_state(EventsFiltersTagsState.tags)
send_tags_toggle_ui(
peer=event.peer,
title="🏷️ *Фильтры: теги*\n\nВыбери теги, затем нажми «Готово»:",
choices=choices,
selected_keys=set(selected_ids),
cfg=FILTERS_TAGS_CFG,
done_label="✅ Готово",
)
@bot.di
def events_filters_tags_toggle_handler( # legacy
event: UpdateInteractiveMediaEvent,
context: FSMContext,
event_service: EventService,
tag_service: TagService,
):
state = context.get_state()
if not (state == EventsFiltersTagsState.tags or str(state) == str(EventsFiltersTagsState.tags)):
return
delete_prev_message_by_peer(bot, event.peer)
raw = str(event.data.value or "")
data = context.get_data() or {}
ctx_name = str(data.get(_CTX_KEY) or "events")
offset = int(data.get(_OFFSET_KEY) or 0)
selected = set(data.get(_TAG_IDS_KEY) or [])
# DONE -> сохранить filter.tag_ids и вернуться в меню фильтров
if is_done(raw, FILTERS_TAGS_CFG):
flt = get_events_filter(context, ctx_name=ctx_name)
flt.tag_ids = sorted([str(x) for x in selected if str(x).strip()])
all_tags = tag_service.get_all_tags()
id_to_name = {
str(getattr(t, "id", "")).strip(): str(getattr(t, "name", "")).strip()
for t in (all_tags or [])
if str(getattr(t, "id", "")).strip() and str(getattr(t, "name", "")).strip()
}
flt.tag_names = [id_to_name[i] for i in flt.tag_ids if i in id_to_name]
set_events_filter(context, ctx_name=ctx_name, flt=flt)
context.set_state(None)
# чистим только "служебные" ключи экрана выбора тегов
context.update_data({_CTX_KEY: None, _OFFSET_KEY: None, _TAG_IDS_KEY: None})
summary = format_events_filter_summary(get_events_filter(context, ctx_name=ctx_name))
bot.messaging.send_message(
peer=event.peer,
text=f"🧰 *Фильтры*\n\n{summary}\n\nВыбери, что изменить:",
interactive_media_groups=events_filters_menu_keyboard(ctx_name=ctx_name, offset=offset),
)
return
# TOGGLE
key = parse_toggle_key(raw, FILTERS_TAGS_CFG)
if not key:
return
if key in selected:
selected.remove(key)
else:
selected.add(key)
context.update_data({_TAG_IDS_KEY: list(selected)})
all_tags = tag_service.get_all_tags()
choices = _tag_choices_from_backend(all_tags)
send_tags_toggle_ui(
peer=event.peer,
title="🏷️ *Фильтры: теги*\n\nВыбери теги, затем нажми «Готово»:",
choices=choices,
selected_keys=set(selected),
cfg=FILTERS_TAGS_CFG,
done_label="✅ Готово",
)
def _safe_int(value, default: int = 0) -> int: # legacy
try:
if value is None:
return default
n = int(value)
return n if n >= 0 else default
except Exception:
return default
def _send_mode_picker(
*, peer, ctx_name: str, back_offset: int, context: FSMContext
) -> None: # legacy
data = context.get_data() or {}
locked_mode = str(data.get(_MODE_LOCKED_KEY) or "").strip() or None
if locked_mode not in ("all", "participant", "organizer"):
locked_mode = None
sel = str(data.get(_MODE_KEY) or "all").strip()
if sel not in ("all", "participant", "organizer"):
sel = "all"
note = ""
if locked_mode == "organizer":
note = "\n\nℹ️ Для этой вкладки режим зафиксирован: *организатор*."
if locked_mode == "participant":
note = "\n\nℹ️ Для этой вкладки режим зафиксирован: *участник*."
bot.messaging.send_message(
peer=peer,
text="🎛️ *Фильтры: режим*\n\nВыбери режим и нажми «Готово»:" + note,
interactive_media_groups=events_filters_mode_keyboard(
ctx_name=ctx_name,
back_offset=back_offset,
selected_mode=sel, # type: ignore[arg-type]
locked_mode=locked_mode,
),
)
@bot.di
def events_filters_mode_open_handler( # legacy
event: UpdateInteractiveMediaEvent,
context: FSMContext,
event_service: EventService,
):
delete_prev_message_by_peer(bot, event.peer)
context.set_state(None)
ctx_name, back_offset = _split_ctx_offset(event.data.value)
flt = get_events_filter(context, ctx_name=ctx_name)
locked_mode = _locked_mode_for_ctx(ctx_name)
context.update_data(
{
_CTX_KEY: ctx_name,
_OFFSET_KEY: int(back_offset),
_MODE_KEY: flt.mode,
_MODE_LOCKED_KEY: locked_mode,
}
)
context.set_state(EventsFiltersModeState.mode)
_send_mode_picker(peer=event.peer, ctx_name=ctx_name, back_offset=back_offset, context=context)
@bot.di
def events_filters_mode_select_handler( # legacy
event: UpdateInteractiveMediaEvent,
context: FSMContext,
event_service: EventService,
):
state = context.get_state()
if not (state == EventsFiltersModeState.mode or str(state) == str(EventsFiltersModeState.mode)):
return
delete_prev_message_by_peer(bot, event.peer)
raw = str(event.data.value or "").strip()
data = context.get_data() or {}
ctx_name = str(data.get(_CTX_KEY) or "events")
back_offset = _safe_int(data.get(_OFFSET_KEY), 0)
locked_mode = str(data.get(_MODE_LOCKED_KEY) or "").strip() or None
if locked_mode not in ("all", "participant", "organizer"):
locked_mode = None
if raw.startswith("back:"):
_ctx, _off = _split_ctx_offset(raw[len("back:") :])
summary = format_events_filter_summary(get_events_filter(context, ctx_name=_ctx))
context.set_state(None)
context.update_data(
{_CTX_KEY: None, _OFFSET_KEY: None, _MODE_KEY: None, _MODE_LOCKED_KEY: None}
)
bot.messaging.send_message(
peer=event.peer,
text=f"🧰 *Фильтры*\n\n{summary}\n\nВыбери, что изменить:",
interactive_media_groups=events_filters_menu_keyboard(ctx_name=_ctx, offset=_off),
)
return
if raw.startswith("done:"):
_ctx, _off = _split_ctx_offset(raw[len("done:") :])
flt = get_events_filter(context, ctx_name=_ctx)
if locked_mode in ("participant", "organizer"):
flt.mode = locked_mode # type: ignore[assignment]
else:
sel = str((context.get_data() or {}).get(_MODE_KEY) or "all").strip()
if sel in ("all", "participant", "organizer"):
flt.mode = sel # type: ignore[assignment]
set_events_filter(context, ctx_name=_ctx, flt=flt)
summary = format_events_filter_summary(get_events_filter(context, ctx_name=_ctx))
context.set_state(None)
context.update_data(
{_CTX_KEY: None, _OFFSET_KEY: None, _MODE_KEY: None, _MODE_LOCKED_KEY: None}
)
bot.messaging.send_message(
peer=event.peer,
text=f"🧰 *Фильтры*\n\n{summary}\n\nВыбери, что изменить:",
interactive_media_groups=events_filters_menu_keyboard(ctx_name=_ctx, offset=_off),
)
return
if raw.startswith("set:"):
if locked_mode in ("participant", "organizer"):
# показываем тот же экран с подсказкой, ничего не меняем
_send_mode_picker(
peer=event.peer, ctx_name=ctx_name, back_offset=back_offset, context=context
)
return
sel = raw[len("set:") :].strip()
if sel not in ("all", "participant", "organizer"):
return
context.update_data({_MODE_KEY: sel})
_send_mode_picker(
peer=event.peer, ctx_name=ctx_name, back_offset=back_offset, context=context
)
return
--- services/bot/core/markups/event.py ---
from dialog_bot_sdk.interactive_media import Button, InteractiveMediaGroup, MediaGroupBuilder
from core.markups.pagination import pagination_keyboard
from core.schemas import EventCardSchema
from core.utils import format_datetime, wrap_text
def format_event_details(
card: EventCardSchema,
*,
show_code: bool,
show_organizer: bool,
show_report_status: bool = False,
report_exists: bool | None = None,
show_active: bool = False,
make_frame: bool = False,
wrap: bool = False,
) -> str:
"""формирует читаемое описание из EventCardSchema"""
def make_bold(string: str) -> str:
return f"*{string}*" if not make_frame else string
def make_block(string: str) -> str:
return f"`{string}`" if not make_frame else string
def make_title(string: str) -> str:
return f"{string}\n" if make_frame else f"{string}\n\n"
lines: list[str] = [
f"📣 {make_bold('Название')}: {card.event.title}",
f"⏰ {make_bold('Когда')}: {format_datetime(card.event.date_time)}",
f"🏷️ {make_bold('Теги')}: {_format_tags(card.tags)}",
f"🏆 {make_bold('Часы')}: {card.event.hours}",
f"🏛️ {make_bold('ГОСБ')}: {card.gosb.name}",
f"👥 {make_bold('Участники')}: {card.event.participation_count}/{card.event.participation_limit}",
]
if show_code:
lines.append(f"🎟️ {make_bold('Код')}: {card.event.verification_code or '—'}")
lines.extend(
[
f"📝 {make_bold('Описание')}: {wrap_text(card.event.description or '—')}"
if wrap
else f"📝 {make_bold('Описание')}: {card.event.description or '—'}",
]
)
if show_active:
lines.append("")
lines.append("✅ Согласовано" if card.event.active else "⏳ На согласовании")
lines.extend(["", "🔧 Техническая информация:"])
if show_report_status:
status = "✅ Создан" if report_exists else "Ещё не создан"
lines.append(f"Отчёт: {make_block(status)}")
if show_organizer:
lines.append(f"Creator id: {make_block(card.event.creator_id or '—')}")
lines.append(f"Project ID: {make_block(card.event.project_id or '—')}")
lines.append(f"ID: {make_block(card.event.id)}")
result = "\n".join(lines)
return make_title("📅 *Мероприятие*") + (
"```plain\n" + result + "\n```" if make_frame else result
)
def _format_tags(
tags: list | None,
) -> str: # TODO add type to 'tags' and simplify func depending on type
if not tags:
return "—"
try:
titles = [str(getattr(t, "title", "")).strip() for t in tags]
titles = [t for t in titles if t]
return ", ".join(titles) if titles else "—"
except Exception:
return str(tags)
def event_actions_keyboard( # legacy
event_id: str,
*,
is_participant: bool,
can_edit: bool,
back_value: str | None,
back_label: str,
enter_code_media_id: str,
sign_up_media_id: str,
sign_out_media_id: str,
edit_media_id: str = "event_menu_edit",
delete_media_id: str = "event_menu_delete",
can_create_report: bool = False,
can_view_report: bool = False,
can_sign_up: bool = False,
ctx: str | None = None,
) -> list[InteractiveMediaGroup]:
actions = []
v = f"{event_id}|{ctx}" if ctx else event_id
actions.append(Button(media_id=enter_code_media_id, value=v, label="🏅 Ввести код"))
if is_participant:
actions.append(Button(media_id=sign_out_media_id, value=v, label="❌ Отписаться"))
elif can_sign_up:
actions.append(Button(media_id=sign_up_media_id, value=v, label="✅ Записаться"))
else:
actions.append(Button(media_id=sign_up_media_id, value=v, label="⚠️ Записаться"))
if can_edit:
actions.append(Button(media_id=edit_media_id, value=v, label="✏️ Редактировать"))
# TODO: separate permissions
actions.append(Button(media_id="event_participants_open", value=v, label="👥 Участники"))
actions.append(Button(media_id=delete_media_id, value=v, label="🗑 Удалить"))
if can_create_report:
actions.append(Button(media_id="event_report_create", value=v, label="📄 Создать отчёт"))
if can_view_report:
actions.append(Button(media_id="event_report_view", value=v, label="📄 Посмотреть отчёт"))
if back_value is not None:
actions.append(Button(media_id=back_value, value="", label=back_label))
return MediaGroupBuilder(actions).build()
def event_edit_fields_keyboard( # legacy
event_id: str,
*,
leave_media_id: str = "leave",
leave_value: str = "moderation",
leave_label: str = "⬅️ В мастерскую",
) -> list[InteractiveMediaGroup]:
group_builder_1 = MediaGroupBuilder(
[
Button(
media_id="event_menu_edit_name",
value=f"{event_id}|title",
label="📣 Название",
),
Button(
media_id="event_menu_edit_description",
value=f"{event_id}|description",
label="📝 Описание",
),
Button(
media_id="event_menu_edit_date",
value=f"{event_id}|date",
label="📅 Дата",
),
Button(
media_id="event_menu_edit_time",
value=f"{event_id}|time",
label="⏰ Время",
),
Button(
media_id="event_menu_edit_tags",
value=f"{event_id}|tags",
label="🏷️ Теги",
),
Button(
media_id="event_menu_edit_code",
value=f"{event_id}|code",
label="🎟️ Код",
),
]
)
group_builder_2 = MediaGroupBuilder(
[
Button(
media_id="event_menu_edit_organizers",
value=f"{event_id}|event_organizers",
label="🤓 Организаторы",
),
Button(
media_id="event_menu_edit_hours",
value=f"{event_id}|hours",
label="🏆 Часы",
),
Button(
media_id="event_menu_edit_gosb",
value=f"{event_id}|gosb_id",
label="🏛️ ГОСБ",
),
Button(
media_id="event_menu_edit_participation_limit",
value=f"{event_id}|participation_limit",
label="👥 Кол-во волонтеров",
),
Button(
media_id=leave_media_id,
value=leave_value,
label=leave_label,
),
]
)
return group_builder_1.merge([group_builder_2])
def user_events_pagination_keyboard( # legacy
*, offset: int, limit: int, has_prev: bool, has_next: bool
) -> list[InteractiveMediaGroup]:
return pagination_keyboard(
offset=offset,
limit=limit,
has_prev=has_prev,
has_next=has_next,
prev_media_id="user_events_prev",
next_media_id="user_events_next",
leave_media_id="volunteer_home",
leave_label="🏡 В дом волонтёра",
keep_layout=True,
add_view_by_uuid_button=True,
view_by_uuid_value="events",
)
def part_events_pagination_keyboard( # legacy
*, offset: int, limit: int, has_prev: bool, has_next: bool
) -> list[InteractiveMediaGroup]:
return pagination_keyboard(
offset=offset,
limit=limit,
has_prev=has_prev,
has_next=has_next,
prev_media_id="part_events_prev",
next_media_id="part_events_next",
leave_media_id="volunteer_home",
leave_label="🏡 В дом волонтёра",
keep_layout=True,
add_view_by_uuid_button=True,
view_by_uuid_value="part_events",
)
def my_events_pagination_keyboard( # legacy
*, offset: int, limit: int, has_prev: bool, has_next: bool
) -> list[InteractiveMediaGroup]:
return pagination_keyboard(
offset=offset,
limit=limit,
has_prev=has_prev,
has_next=has_next,
prev_media_id="my_events_prev",
next_media_id="my_events_next",
leave_media_id="moderation",
leave_label="🛡️ В мастерскую",
keep_layout=True,
add_view_by_uuid_button=True,
view_by_uuid_value="my_events",
)
def _event_button_label(event_card: EventCardSchema, ctx: str | None = None) -> str:
"""Формирует подпись для кнопки мероприятия"""
ev = event_card.event
participation = event_card.participation
date_str = format_datetime(getattr(ev, "date_time", None))
title = getattr(ev, "title", "—")
if ctx == "my_events":
status = "на согласовании ⏳"
if event_card.event.active:
status = "согласовано ✅"
return f"{title} | {date_str} — {status}"
participation_emoji = "⚪"
if participation:
state = getattr(participation, "state", "")
if state == "signed_up":
participation_emoji = "📝" # записан
elif state == "confirmed":
participation_emoji = "✅" # подтверждено
elif state == "cancelled":
participation_emoji = "❌" # отменено (не пришел)
return f"{title} | {date_str} {participation_emoji}"
def events_select_keyboard(
events_cards: list[EventCardSchema],
*,
open_media_id: str,
start_index: int = 1,
per_row: int = 1,
label_max: int = 120,
ctx: str | None = None,
) -> list[InteractiveMediaGroup]:
"""
строит список кнопок по мероприятиям
нажатие вызывает handler по _id = open_media_id, value = event.id
"""
groups: list[InteractiveMediaGroup] = []
row: list[Button] = []
idx = start_index
for event_card in events_cards:
label = _event_button_label(event_card, ctx)
row.append(
Button(
media_id=open_media_id, value=str(getattr(event_card.event, "id", "")), label=label
)
)
idx += 1
if len(row) >= per_row:
groups.extend(MediaGroupBuilder(row).build())
row = []
if row:
groups.extend(MediaGroupBuilder(row).build())
return groups
def view_by_uuid_keyboard(*, media_id: str, label: str) -> list[InteractiveMediaGroup]: # legacy
return MediaGroupBuilder([Button(media_id=media_id, value="", label=label)]).build()
def all_events_pagination_keyboard(
*, offset: int, limit: int, has_prev: bool, has_next: bool
): # legacy
return pagination_keyboard(
offset=offset,
limit=limit,
has_prev=has_prev,
has_next=has_next,
prev_media_id="all_events_prev",
next_media_id="all_events_next",
leave_media_id="moderation",
leave_label="🛡️ В мастерскую",
keep_layout=True,
add_view_by_uuid_button=True,
view_by_uuid_value="moderation",
)
--- services/bot/core/markups/__init__.py ---
from .admin import admin_menu_keyboard, back_to_admin_menu_keyboard
from .ai import build_event_buttons_keyboard
from .event import (
all_events_pagination_keyboard,
event_actions_keyboard,
event_edit_fields_keyboard,
events_select_keyboard,
format_event_details,
my_events_pagination_keyboard,
part_events_pagination_keyboard,
user_events_pagination_keyboard,
)
from .events_filters import (
events_filters_button_keyboard,
events_filters_menu_keyboard,
events_filters_mode_keyboard,
)
from .gosb import gosb_select_keyboard
from .main import back_to_main_menu_keyboard, main_keyboard
from .moderation import (
back_to_moderation_and_skip_keyboard,
back_to_moderation_keyboard,
moderation_menu_keyboard,
)
from .participation import event_participants_keyboard, event_participants_pagination_keyboard
from .points import (
back_to_points_menu,
leaderboard_keyboard,
points_events_pagination_keyboard,
points_menu_keyboard,
)
from .profile import profile_keyboard
from .project import (
format_project_details,
project_actions_keyboard,
project_select_keyboard,
user_projects_pagination_keyboard,
)
from .reports import back_to_reports_export_keyboard, reports_export_keyboard
from .request import format_request_details, requests_pagination_keyboard, requests_select_keyboard
from .tags import TagChoice, choices_from_backend_tags, tags_toggle_keyboard
from .terbank import tb_select_keyboard
from .volunteer_home import back_to_volunteer_home_keyboard, volunteer_home_keyboard
__all__ = [
"TagChoice",
"admin_menu_keyboard",
"all_events_pagination_keyboard",
"back_to_admin_menu_keyboard",
"back_to_main_menu_keyboard",
"back_to_moderation_and_skip_keyboard",
"back_to_moderation_keyboard",
"back_to_points_menu",
"back_to_reports_export_keyboard",
"back_to_volunteer_home_keyboard",
"build_event_buttons_keyboard",
"choices_from_backend_tags",
"event_actions_keyboard",
"event_edit_fields_keyboard",
"event_participants_keyboard",
"event_participants_pagination_keyboard",
"events_filters_button_keyboard",
"events_filters_menu_keyboard",
"events_filters_mode_keyboard",
"events_select_keyboard",
"format_event_details",
"format_project_details",
"format_request_details",
"gosb_select_keyboard",
"leaderboard_keyboard",
"main_keyboard",
"moderation_menu_keyboard",
"my_events_pagination_keyboard",
"part_events_pagination_keyboard",
"points_events_pagination_keyboard",
"points_menu_keyboard",
"profile_keyboard",
"project_actions_keyboard",
"project_select_keyboard",
"reports_export_keyboard",
"requests_pagination_keyboard",
"requests_select_keyboard",
"tags_toggle_keyboard",
"tb_select_keyboard",
"user_events_pagination_keyboard",
"user_projects_pagination_keyboard",
"volunteer_home_keyboard",
]
Error: 'services/bot/core/handlers/moderation_events.py' is not a valid file
Error: 'services/bot/core/handlers/my_events.py' is not a valid file
--- services/bot/core/handlers/points.py ---
from dialog_bot_sdk.entities.messaging import UpdateInteractiveMediaEvent
from core.bot_kit.fsm import FSMContext
from core.bot_kit.router import Router
from core.config import bot
from core.handlers.events_ui import send_event_card, send_points_page
from core.schemas import UserSchema
from core.services import EventService
from core.utils import clear_context_keep_events_filters, delete_prev_message_by_peer
points_rt = Router()
@bot.di
def points_menu_handler( # legacy
event: UpdateInteractiveMediaEvent,
context: FSMContext,
event_service: EventService,
):
delete_prev_message_by_peer(bot, event.peer)
clear_context_keep_events_filters(context)
send_points_page(
event.peer,
offset=0,
event_service=event_service,
context=context,
)
@bot.di
def points_page_handler( # legacy
event: UpdateInteractiveMediaEvent, context: FSMContext, event_service: EventService
):
delete_prev_message_by_peer(bot, event.peer)
context.set_state(None)
try:
offset = int(event.data.value)
if offset < 0:
offset = 0
except Exception:
offset = 0
send_points_page(
event.peer,
offset=offset,
event_service=event_service,
context=context,
)
@bot.di
def points_event_open_handler( # legacy
event: UpdateInteractiveMediaEvent,
context: FSMContext,
event_service: EventService,
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="points",
)
--- services/bot/core/handlers/__init__.py ---
from core.bot_kit.router import Router
from .admin import admin_menu_handler, admin_rt, del_moderator_handler, set_moderator_handler
from .ai import ai_assistant_handler, ai_rt, event_source_callback_handler
from .event import (
event_user_enter_code_start_handler,
event_user_sign_out_handler,
event_user_sign_up_handler,
events_menu_handler,
part_events_menu_handler,
part_events_page_handler,
user_events_open_handler,
user_events_page_handler,
user_events_rt,
)
from .event_participations import (
event_participants_open_handler,
event_participants_page_handler,
event_participation_hours_plus_handler,
)
from .events_filters import (
events_filters_apply_handler,
events_filters_back_handler,
events_filters_mode_open_handler,
events_filters_mode_select_handler,
events_filters_not_implemented_handler,
events_filters_open_handler,
events_filters_reset_handler,
events_filters_tags_open_handler,
events_filters_tags_toggle_handler,
)
from .main import main_menu_handler, start_handler
from .moderation_create import (
create_event_start_handler,
create_rt,
event_create_back_to_time_handler,
project_input_dash_handler,
)
from .moderation_edit import (
edit_rt,
event_menu_delete_handler,
event_menu_edit_field_handler,
event_menu_edit_handler,
event_menu_enter_code_start_handler,
event_tags_toggle_handler,
gosb_approved,
gosb_update_selected,
tb_approved,
tb_reselect_handler,
tb_update_selected,
)
from .moderation_menu import (
all_events_handler,
all_events_open_handler,
all_events_page_handler,
moderation_menu_handler,
moderation_menu_rt,
my_events_handler,
my_events_open_handler,
my_events_page_handler,
)
from .noop import noop_handler
from .points import points_event_open_handler, points_menu_handler, points_page_handler, points_rt
from .profile import my_profile_handler, profile_tags_toggle_handler, update_user_interests_start
from .profile import rt as update_user_rt
from .report import (
report_create_start_handler,
report_rt,
report_view_handler,
reports_export_handler,
)
from .request import (
all_requests_handler,
my_requests_handler,
my_requests_page_handler,
request_approve_handler,
request_cancel_handler,
request_open_handler,
request_reject_handler,
request_repeat_handler,
requests_page_handler,
requests_rt,
)
from .view_by_uuid import rt as view_by_uuid_rt
from .view_by_uuid import view_by_uuid_start_handler
from .volunteer_home import points_leaderboard_handler, volunteer_home_handler
events_rt = Router()
events_rt.register(moderation_menu_rt)
events_rt.register(create_rt)
events_rt.register(edit_rt)
__all__ = [
"admin_menu_handler",
"admin_rt",
"ai_assistant_handler",
"ai_rt",
"all_events_handler",
"all_events_open_handler",
"all_events_page_handler",
"all_requests_handler",
"create_event_start_handler",
"create_rt",
"del_moderator_handler",
"edit_rt",
"event_create_back_to_time_handler",
"event_menu_delete_handler",
"event_menu_edit_field_handler",
"event_menu_edit_handler",
"event_menu_enter_code_start_handler",
"event_participants_open_handler",
"event_participants_page_handler",
"event_participation_hours_plus_handler",
"event_source_callback_handler",
"event_tags_toggle_handler",
"event_user_enter_code_start_handler",
"event_user_sign_out_handler",
"event_user_sign_up_handler",
"events_filters_apply_handler",
"events_filters_back_handler",
"events_filters_mode_open_handler",
"events_filters_mode_select_handler",
"events_filters_not_implemented_handler",
"events_filters_open_handler",
"events_filters_reset_handler",
"events_filters_tags_open_handler",
"events_filters_tags_toggle_handler",
"events_menu_handler",
"events_rt",
"gosb_approved",
"gosb_update_selected",
"main_menu_handler",
"moderation_menu_handler",
"moderation_menu_rt",
"my_events_handler",
"my_events_open_handler",
"my_events_page_handler",
"my_profile_handler",
"my_requests_handler",
"my_requests_page_handler",
"noop_handler",
"part_events_menu_handler",
"part_events_page_handler",
"points_event_open_handler",
"points_leaderboard_handler",
"points_menu_handler",
"points_page_handler",
"points_rt",
"profile_tags_toggle_handler",
"project_input_dash_handler",
"report_create_start_handler",
"report_rt",
"report_view_handler",
"reports_export_handler",
"request_approve_handler",
"request_cancel_handler",
"request_open_handler",
"request_reject_handler",
"request_repeat_handler",
"requests_page_handler",
"requests_rt",
"set_moderator_handler",
"start_handler",
"tb_approved",
"tb_reselect_handler",
"tb_update_selected",
"update_user_interests_start",
"update_user_rt",
"user_events_open_handler",
"user_events_page_handler",
"user_events_rt",
"view_by_uuid_rt",
"view_by_uuid_start_handler",
"volunteer_home_handler",
]
--- services/bot/main.py ---
from core.bot_kit.router import Router
from core.config import bot
from core.handlers import (
admin_menu_handler,
admin_rt,
ai_assistant_handler,
ai_rt,
all_events_handler,
all_events_open_handler,
all_events_page_handler,
all_requests_handler,
create_event_start_handler,
create_rt,
del_moderator_handler,
edit_rt,
event_create_back_to_time_handler,
event_menu_delete_handler,
event_menu_edit_field_handler,
event_menu_edit_handler,
event_menu_enter_code_start_handler,
event_participants_open_handler,
event_participants_page_handler,
event_participation_hours_plus_handler,
event_source_callback_handler,
event_tags_toggle_handler,
event_user_enter_code_start_handler,
event_user_sign_out_handler,
event_user_sign_up_handler,
events_filters_apply_handler,
events_filters_back_handler,
events_filters_mode_open_handler,
events_filters_mode_select_handler,
events_filters_not_implemented_handler,
events_filters_open_handler,
events_filters_reset_handler,
events_filters_tags_open_handler,
events_filters_tags_toggle_handler,
events_menu_handler,
events_rt,
gosb_approved,
gosb_update_selected,
main_menu_handler,
moderation_menu_handler,
moderation_menu_rt,
my_events_handler,
my_events_open_handler,
my_events_page_handler,
my_profile_handler,
my_requests_handler,
my_requests_page_handler,
noop_handler,
part_events_menu_handler,
part_events_page_handler,
points_event_open_handler,
points_leaderboard_handler,
points_menu_handler,
points_page_handler,
points_rt,
profile_tags_toggle_handler,
project_input_dash_handler,
report_create_start_handler,
report_rt,
report_view_handler,
reports_export_handler,
request_approve_handler,
request_cancel_handler,
request_open_handler,
request_reject_handler,
request_repeat_handler,
requests_page_handler,
requests_rt,
set_moderator_handler,
start_handler,
tb_approved,
tb_reselect_handler,
tb_update_selected,
update_user_interests_start,
update_user_rt,
user_events_open_handler,
user_events_page_handler,
user_events_rt,
view_by_uuid_rt,
view_by_uuid_start_handler,
volunteer_home_handler,
)
from core.handlers.projects import (
projects_menu_handler,
user_projects_open_handler,
user_projects_page_handler,
)
from dialog_bot_sdk.entities.messaging import CommandHandler, EventHandler
def handlers_setting() -> None: # legacy
router = Router()
router.register(edit_rt)
router.register(moderation_menu_rt)
router.register(create_rt)
router.register(update_user_rt)
router.register(events_rt)
router.register(user_events_rt)
router.register(points_rt)
router.register(view_by_uuid_rt)
router.register(report_rt)
router.register(ai_rt)
router.register(requests_rt)
router.register(admin_rt)
router.subscribe(bot)
bot.messaging.command_handler(
[
CommandHandler(
function=start_handler,
command="start",
description="Расскажу о себе",
),
CommandHandler(
function=moderation_menu_handler,
command="workship",
description="Мастерская",
),
CommandHandler(
function=create_event_start_handler,
command="create_event",
description="Создать мероприятие",
),
CommandHandler(
function=all_events_handler,
command="all_events",
description="Показать все мероприятия",
),
CommandHandler(
function=reports_export_handler,
command="reports_export",
description="Выгрузка отчётов",
),
]
)
bot.messaging.event_handler(
[
EventHandler(function=volunteer_home_handler, _id="volunteer_home"),
EventHandler(function=main_menu_handler, _id="main_menu"),
EventHandler(function=ai_assistant_handler, _id="ai_assistant"),
EventHandler(function=my_profile_handler, _id="my_profile"),
EventHandler(function=update_user_interests_start, _id="update_user_interests"),
EventHandler(function=profile_tags_toggle_handler, _id="profile_tags_toggle"),
EventHandler(function=points_menu_handler, _id="points"),
EventHandler(function=points_leaderboard_handler, _id="points_leaderboard"),
EventHandler(function=moderation_menu_handler, _id="moderation"),
EventHandler(function=reports_export_handler, _id="download_reports_list"),
# PROJECTS
EventHandler(function=projects_menu_handler, _id="projects"),
EventHandler(function=user_projects_page_handler, _id="user_projects_next"),
EventHandler(function=user_projects_page_handler, _id="user_projects_prev"),
EventHandler(function=user_projects_open_handler, _id="user_projects_open"),
# EVENTS
EventHandler(function=all_events_handler, _id="all_events"),
EventHandler(function=events_menu_handler, _id="events"),
EventHandler(function=part_events_menu_handler, _id="part_events"),
EventHandler(function=user_events_page_handler, _id="user_events_prev"),
EventHandler(function=user_events_page_handler, _id="user_events_next"),
EventHandler(function=part_events_page_handler, _id="part_events_prev"),
EventHandler(function=part_events_page_handler, _id="part_events_next"),
EventHandler(function=event_user_sign_up_handler, _id="event_user_sign_up"),
EventHandler(function=event_user_sign_out_handler, _id="event_user_sign_out"),
EventHandler(function=event_user_enter_code_start_handler, _id="event_user_enter_code"),
EventHandler(function=event_tags_toggle_handler, _id="event_tags_toggle"),
EventHandler(function=event_participants_open_handler, _id="event_participants_open"),
EventHandler(function=event_participants_page_handler, _id="event_participants_prev"),
EventHandler(function=event_participants_page_handler, _id="event_participants_next"),
EventHandler(
function=event_participation_hours_plus_handler,
_id="event_participation_hours_plus",
),
EventHandler(function=report_create_start_handler, _id="event_report_create"),
EventHandler(function=report_view_handler, _id="event_report_view"),
EventHandler(function=all_events_page_handler, _id="all_events_prev"),
EventHandler(function=all_events_page_handler, _id="all_events_next"),
EventHandler(function=my_events_handler, _id="my_events"),
EventHandler(function=my_events_page_handler, _id="my_events_prev"),
EventHandler(function=my_events_page_handler, _id="my_events_next"),
EventHandler(function=my_events_open_handler, _id="my_events_open"),
EventHandler(function=create_event_start_handler, _id="create_event"),
EventHandler(function=admin_menu_handler, _id="administration"),
EventHandler(function=event_menu_enter_code_start_handler, _id="event_menu_enter_code"),
EventHandler(function=event_menu_delete_handler, _id="event_menu_delete"),
EventHandler(function=event_menu_edit_handler, _id="event_menu_edit"),
EventHandler(function=view_by_uuid_start_handler, _id="view_by_uuid"),
EventHandler(function=event_menu_edit_field_handler, _id="event_menu_edit_name"),
EventHandler(function=event_menu_edit_field_handler, _id="event_menu_edit_description"),
EventHandler(function=event_menu_edit_field_handler, _id="event_menu_edit_date"),
EventHandler(function=event_menu_edit_field_handler, _id="event_menu_edit_time"),
EventHandler(function=event_menu_edit_field_handler, _id="event_menu_edit_tags"),
EventHandler(function=event_menu_edit_field_handler, _id="event_menu_edit_organizers"),
EventHandler(function=event_menu_edit_field_handler, _id="event_menu_edit_hours"),
EventHandler(function=event_menu_edit_field_handler, _id="event_menu_edit_code"),
EventHandler(function=event_menu_edit_field_handler, _id="event_menu_edit_gosb"),
EventHandler(
function=event_menu_edit_field_handler, _id="event_menu_edit_participation_limit"
),
EventHandler(function=user_events_open_handler, _id="user_events_open"),
EventHandler(function=all_events_open_handler, _id="all_events_open"),
EventHandler(function=points_page_handler, _id="points_prev"),
EventHandler(function=points_page_handler, _id="points_next"),
EventHandler(function=points_event_open_handler, _id="points_event_open"),
EventHandler(function=points_event_open_handler, _id="points_events_open"),
EventHandler(function=events_filters_open_handler, _id="events_filters_open"),
EventHandler(function=events_filters_apply_handler, _id="events_filters_apply"),
EventHandler(function=events_filters_reset_handler, _id="events_filters_reset"),
EventHandler(function=events_filters_back_handler, _id="events_filters_back"),
EventHandler(function=events_filters_tags_open_handler, _id="events_filters_tags"),
EventHandler(
function=events_filters_tags_toggle_handler, _id="events_filters_tags_toggle"
),
EventHandler(
function=events_filters_not_implemented_handler, _id="events_filters_date"
),
EventHandler(function=events_filters_mode_open_handler, _id="events_filters_mode"),
EventHandler(
function=events_filters_mode_select_handler, _id="events_filters_mode_select"
),
EventHandler(function=all_requests_handler, _id="requests"),
EventHandler(function=my_requests_handler, _id="my_requests"),
EventHandler(function=requests_page_handler, _id="requests_prev"),
EventHandler(function=my_requests_page_handler, _id="my_requests_prev"),
EventHandler(function=requests_page_handler, _id="requests_next"),
EventHandler(function=my_requests_page_handler, _id="my_requests_next"),
EventHandler(function=request_open_handler, _id="request_open"),
EventHandler(function=request_open_handler, _id="my_request_open"),
EventHandler(function=request_approve_handler, _id="request_approve"),
EventHandler(function=request_reject_handler, _id="request_reject"),
EventHandler(function=request_repeat_handler, _id="request_repeat"),
EventHandler(function=request_cancel_handler, _id="request_cancel"),
# TODO почистить
EventHandler(function=project_input_dash_handler, _id="project_input_dash"),
EventHandler(function=noop_handler, _id="noop_prev"),
EventHandler(function=noop_handler, _id="noop_mid"),
EventHandler(function=noop_handler, _id="noop_next"),
EventHandler(function=noop_handler, _id="noop_uuid"),
EventHandler(function=noop_handler, _id="noop"),
EventHandler(function=tb_approved, _id="tb_approved"),
EventHandler(function=gosb_approved, _id="gosb_approved"),
EventHandler(function=tb_update_selected, _id="tb_select"),
EventHandler(
function=event_create_back_to_time_handler, _id="event_create_back_to_time"
),
EventHandler(function=tb_reselect_handler, _id="tb_reselect"),
EventHandler(function=gosb_update_selected, _id="gosb_select"),
EventHandler(function=event_source_callback_handler, _id="ai_event_card"),
EventHandler(function=set_moderator_handler, _id="add_moderator"),
EventHandler(function=del_moderator_handler, _id="delete_moderator"),
]
)
def main() -> None: # legacy
handlers_setting()
bot.profile.edit_about_sync("Волонтёрский бот, который помогает сотрудникам Сбера нести добро!")
bot.updates.on_updates(do_read_message=True, do_register_commands=True)
if __name__ == "__main__":
main()
список файлов проекта:
.env.example
.gitignore
Makefile
docker-compose.yml
docs/TODO.md
docs/buisiness-model.md
docs/devops-instruction.md
docs/domain-model.md
docs/event-access-policy.md
docs/integration-notes.md
docs/specification.pdf
policy.yml
pyproject.toml
pyrightconfig.json
rag_seed.yml
seed.yml
services/ai/Dockerfile
services/ai/main.py
services/ai/rag_seed.yml
services/ai/requirements.txt
services/ai/russian_trusted_root_ca_pem.crt
services/ai/src/api/__init__.py
services/ai/src/api/controllers/__init__.py
services/ai/src/api/controllers/assistant_controller.py
services/ai/src/api/controllers/health_controller.py
services/ai/src/api/controllers/index_controller.py
services/ai/src/api/middlewares/auth.py
services/ai/src/api/models/__init__.py
services/ai/src/api/models/request.py
services/ai/src/api/models/response.py
services/ai/src/api/routes.py
services/ai/src/app.py
services/ai/src/clients/embedding_model.py
services/ai/src/clients/event_vector_store.py
services/ai/src/clients/intent_vector_store.py
services/ai/src/clients/language_model.py
services/ai/src/clients/question_vector_store.py
services/ai/src/clients/seed_loader.py
services/ai/src/config/env.py
services/ai/src/db/database.py
services/ai/src/repositories/__init__.py
services/ai/src/repositories/conversation.py
services/ai/src/repositories/seed_version.py
services/ai/src/run.py
services/ai/src/services/__init__.py
services/ai/src/services/assistant.py
services/ai/src/services/indexing.py
services/ai/src/services/intents.py
services/ai/src/services/rag_init.py
services/ai/src/utils/__init__.py
services/ai/src/utils/asterisks.py
services/ai/src/utils/logger.py
services/backend/.dockerignore
services/backend/Dockerfile
services/backend/bootstrap/modules.go
services/backend/cmd/root.go
services/backend/docs/docs.go
services/backend/docs/swagger.json
services/backend/docs/swagger.yaml
services/backend/go.mod
services/backend/go.sum
services/backend/internal/adapters/assistant/assistant_client.go
services/backend/internal/adapters/assistant/module.go
services/backend/internal/adapters/assistant/requests.go
services/backend/internal/adapters/policy/module.go
services/backend/internal/adapters/policy/policy_matrix.go
services/backend/internal/adapters/reports/collection_mapper.go
services/backend/internal/adapters/reports/collection_schema.go
services/backend/internal/adapters/reports/module.go
services/backend/internal/adapters/reports/report_query.go
services/backend/internal/adapters/reports/report_repo.go
services/backend/internal/adapters/reports/report_schema.go
services/backend/internal/adapters/volunteering/event_card/event_card_mappers.go
services/backend/internal/adapters/volunteering/event_card/event_card_query.go
services/backend/internal/adapters/volunteering/event_card/event_card_schema.go
services/backend/internal/adapters/volunteering/event_card/module.go
services/backend/internal/adapters/volunteering/event_organizer_card/module.go
services/backend/internal/adapters/volunteering/event_organizer_card/organizer_card_mappers.go
services/backend/internal/adapters/volunteering/event_organizer_card/organizer_card_query.go
services/backend/internal/adapters/volunteering/event_organizer_card/organizer_card_schema.go
services/backend/internal/adapters/volunteering/event_organizers/event_organizer_mappers.go
services/backend/internal/adapters/volunteering/event_organizers/event_organizer_repo.go
services/backend/internal/adapters/volunteering/event_organizers/event_organizer_schema.go
services/backend/internal/adapters/volunteering/event_organizers/module.go
services/backend/internal/adapters/volunteering/event_tags/event_tag_mappers.go
services/backend/internal/adapters/volunteering/event_tags/event_tag_repo.go
services/backend/internal/adapters/volunteering/event_tags/event_tag_schema.go
services/backend/internal/adapters/volunteering/event_tags/module.go
services/backend/internal/adapters/volunteering/events/event_config_provider.go
services/backend/internal/adapters/volunteering/events/event_mappers.go
services/backend/internal/adapters/volunteering/events/event_repository.go
services/backend/internal/adapters/volunteering/events/event_schema.go
services/backend/internal/adapters/volunteering/events/module.go
services/backend/internal/adapters/volunteering/gosbs/gosb_mappers.go
services/backend/internal/adapters/volunteering/gosbs/gosb_repo.go
services/backend/internal/adapters/volunteering/gosbs/gosb_schema.go
services/backend/internal/adapters/volunteering/gosbs/module.go
services/backend/internal/adapters/volunteering/module.go
services/backend/internal/adapters/volunteering/participation/module.go
services/backend/internal/adapters/volunteering/participation/participation_repo.go
services/backend/internal/adapters/volunteering/participation/participation_schema.go
services/backend/internal/adapters/volunteering/participation_card/module.go
services/backend/internal/adapters/volunteering/participation_card/participation_card_mapper.go
services/backend/internal/adapters/volunteering/participation_card/participation_card_query.go
services/backend/internal/adapters/volunteering/participation_card/participation_card_schema.go
services/backend/internal/adapters/volunteering/project/module.go
services/backend/internal/adapters/volunteering/project/project_mappers.go
services/backend/internal/adapters/volunteering/project/project_repository.go
services/backend/internal/adapters/volunteering/project/project_schema.go
services/backend/internal/adapters/volunteering/project_card/module.go
services/backend/internal/adapters/volunteering/project_card/project_card_mapers.go
services/backend/internal/adapters/volunteering/project_card/project_card_query.go
services/backend/internal/adapters/volunteering/project_card/project_card_schema.go
services/backend/internal/adapters/volunteering/request_card/module.go
services/backend/internal/adapters/volunteering/request_card/request_card_mappers.go
services/backend/internal/adapters/volunteering/request_card/request_card_query.go
services/backend/internal/adapters/volunteering/request_card/request_card_schema.go
services/backend/internal/adapters/volunteering/requests/module.go
services/backend/internal/adapters/volunteering/requests/request_mappers.go
services/backend/internal/adapters/volunteering/requests/request_repo.go
services/backend/internal/adapters/volunteering/requests/request_schema.go
services/backend/internal/adapters/volunteering/tags/module.go
services/backend/internal/adapters/volunteering/tags/tag_mapper.go
services/backend/internal/adapters/volunteering/tags/tag_schema.go
services/backend/internal/adapters/volunteering/tags/tags_repository.go
services/backend/internal/adapters/volunteering/terbank_moderators/module.go
services/backend/internal/adapters/volunteering/terbank_moderators/terbank_moderator_schema.go
services/backend/internal/adapters/volunteering/terbank_moderators/terbank_moderators_mappers.go
services/backend/internal/adapters/volunteering/terbank_moderators/terbank_moderators_repo.go
services/backend/internal/adapters/volunteering/terbanks/module.go
services/backend/internal/adapters/volunteering/terbanks/terbank_repo.go
services/backend/internal/adapters/volunteering/terbanks/terbank_schema.go
services/backend/internal/adapters/volunteering/terbanks/terbanks_mappers.go
services/backend/internal/adapters/volunteering/volunteer/module.go
services/backend/internal/adapters/volunteering/volunteer/volunteer_mapper.go
services/backend/internal/adapters/volunteering/volunteer/volunteer_repository.go
services/backend/internal/adapters/volunteering/volunteer/volunteer_schema.go
services/backend/internal/adapters/volunteering/volunteer_card/module.go
services/backend/internal/adapters/volunteering/volunteer_card/volunteer_card_mapper.go
services/backend/internal/adapters/volunteering/volunteer_card/volunteer_card_query.go
services/backend/internal/adapters/volunteering/volunteer_card/volunteer_card_schema.go
services/backend/internal/adapters/volunteering/volunteer_tags/module.go
services/backend/internal/adapters/volunteering/volunteer_tags/volunteer_tag_mappers.go
services/backend/internal/adapters/volunteering/volunteer_tags/volunteer_tag_repo.go
services/backend/internal/adapters/volunteering/volunteer_tags/volunteer_tag_schema.go
services/backend/internal/config/env.go
services/backend/internal/config/module.go
services/backend/internal/config/seed_config.go
services/backend/internal/domain/assistant/ask_response.go
services/backend/internal/domain/assistant/assistant_answer.go
services/backend/internal/domain/assistant/assistent_client.go
services/backend/internal/domain/assistant/assistent_errors.go
services/backend/internal/domain/assistant/assistent_service.go
services/backend/internal/domain/assistant/event_rules.go
services/backend/internal/domain/assistant/module.go
services/backend/internal/domain/assistant/rules.go
services/backend/internal/domain/event_bus.go
services/backend/internal/domain/policy/auth.go
services/backend/internal/domain/policy/context.go
services/backend/internal/domain/policy/module.go
services/backend/internal/domain/policy/permission_resolver.go
services/backend/internal/domain/policy/policy_matrix.go
services/backend/internal/domain/policy/role_resolver.go
services/backend/internal/domain/reports/module.go
services/backend/internal/domain/reports/report.go
services/backend/internal/domain/reports/report_collection.go
services/backend/internal/domain/reports/report_errors.go
services/backend/internal/domain/reports/report_query.go
services/backend/internal/domain/reports/report_repository.go
services/backend/internal/domain/reports/report_service.go
services/backend/internal/domain/volunteering/event_card/allowed_actions.go
services/backend/internal/domain/volunteering/event_card/event_card.go
services/backend/internal/domain/volunteering/event_card/event_card_errors.go
services/backend/internal/domain/volunteering/event_card/event_card_page.go
services/backend/internal/domain/volunteering/event_card/event_card_page_view.go
services/backend/internal/domain/volunteering/event_card/event_card_query.go
services/backend/internal/domain/volunteering/event_card/event_card_service.go
services/backend/internal/domain/volunteering/event_card/event_card_view.go
services/backend/internal/domain/volunteering/event_card/event_view.go
services/backend/internal/domain/volunteering/event_card/filter.go
services/backend/internal/domain/volunteering/event_card/module.go
services/backend/internal/domain/volunteering/event_card/page_meta.go
services/backend/internal/domain/volunteering/event_organizer_card/card_service.go
services/backend/internal/domain/volunteering/event_organizer_card/event_organizer_card.go
services/backend/internal/domain/volunteering/event_organizer_card/module.go
services/backend/internal/domain/volunteering/event_organizer_card/organizer_query.go
services/backend/internal/domain/volunteering/event_organizers/event_organizer.go
services/backend/internal/domain/volunteering/event_organizers/event_organizer_repository.go
services/backend/internal/domain/volunteering/event_organizers/event_organizer_service.go
services/backend/internal/domain/volunteering/event_organizers/event_roganizer_errors.go
services/backend/internal/domain/volunteering/event_organizers/module.go
services/backend/internal/domain/volunteering/event_organizers/organizer_request.go
services/backend/internal/domain/volunteering/event_tags/event_tag.go
services/backend/internal/domain/volunteering/event_tags/event_tag_errors.go
services/backend/internal/domain/volunteering/event_tags/event_tag_repository.go
services/backend/internal/domain/volunteering/event_tags/event_tag_service.go
services/backend/internal/domain/volunteering/event_tags/module.go
services/backend/internal/domain/volunteering/events/create_event_request.go
services/backend/internal/domain/volunteering/events/event.go
services/backend/internal/domain/volunteering/events/event_config.go
services/backend/internal/domain/volunteering/events/event_errors.go
services/backend/internal/domain/volunteering/events/event_events.go
services/backend/internal/domain/volunteering/events/event_repository.go
services/backend/internal/domain/volunteering/events/event_service.go
services/backend/internal/domain/volunteering/events/event_state.go
services/backend/internal/domain/volunteering/events/event_update.go
services/backend/internal/domain/volunteering/events/module.go
services/backend/internal/domain/volunteering/events/verificaion_result.go
services/backend/internal/domain/volunteering/gosbs/actual_gosb.go
services/backend/internal/domain/volunteering/gosbs/gosb.go
services/backend/internal/domain/volunteering/gosbs/gosb_errors.go
services/backend/internal/domain/volunteering/gosbs/gosb_repository.go
services/backend/internal/domain/volunteering/gosbs/gosb_service.go
services/backend/internal/domain/volunteering/gosbs/module.go
services/backend/internal/domain/volunteering/module.go
services/backend/internal/domain/volunteering/participation_card/page_meta.go
services/backend/internal/domain/volunteering/participation_card/participation_card.go
services/backend/internal/domain/volunteering/participation_card/participation_card_page.go
services/backend/internal/domain/volunteering/participation_card/participation_card_query.go
services/backend/internal/domain/volunteering/participations/module.go
services/backend/internal/domain/volunteering/participations/participation.go
services/backend/internal/domain/volunteering/participations/participation_errors.go
services/backend/internal/domain/volunteering/participations/participation_repository.go
services/backend/internal/domain/volunteering/participations/participation_service.go
services/backend/internal/domain/volunteering/project_card/allowed_actions.go
services/backend/internal/domain/volunteering/project_card/filter.go
services/backend/internal/domain/volunteering/project_card/module.go
services/backend/internal/domain/volunteering/project_card/page_meta.go
services/backend/internal/domain/volunteering/project_card/project_card.go
services/backend/internal/domain/volunteering/project_card/project_card_page.go
services/backend/internal/domain/volunteering/project_card/project_card_page_view.go
services/backend/internal/domain/volunteering/project_card/project_card_service.go
services/backend/internal/domain/volunteering/project_card/project_card_view.go
services/backend/internal/domain/volunteering/project_card/project_query.go
services/backend/internal/domain/volunteering/project_card/project_view.go
services/backend/internal/domain/volunteering/projects/create_project_request.go
services/backend/internal/domain/volunteering/projects/module.go
services/backend/internal/domain/volunteering/projects/project.go
services/backend/internal/domain/volunteering/projects/project_errors.go
services/backend/internal/domain/volunteering/projects/project_repository.go
services/backend/internal/domain/volunteering/projects/project_service.go
services/backend/internal/domain/volunteering/projects/project_state.go
services/backend/internal/domain/volunteering/projects/project_update.go
services/backend/internal/domain/volunteering/registry/create_result.go
services/backend/internal/domain/volunteering/registry/create_type.go
services/backend/internal/domain/volunteering/registry/module.go
services/backend/internal/domain/volunteering/registry/registry_service.go
services/backend/internal/domain/volunteering/registry/resource_activator.go
services/backend/internal/domain/volunteering/request_card/allowed_actions.go
services/backend/internal/domain/volunteering/request_card/filter.go
services/backend/internal/domain/volunteering/request_card/module.go
services/backend/internal/domain/volunteering/request_card/page_meta.go
services/backend/internal/domain/volunteering/request_card/request_card.go
services/backend/internal/domain/volunteering/request_card/request_card_errors.go
services/backend/internal/domain/volunteering/request_card/request_card_page.go
services/backend/internal/domain/volunteering/request_card/request_card_query.go
services/backend/internal/domain/volunteering/request_card/request_card_service.go
services/backend/internal/domain/volunteering/request_card/request_card_view.go
services/backend/internal/domain/volunteering/requests/module.go
services/backend/internal/domain/volunteering/requests/request.go
services/backend/internal/domain/volunteering/requests/request_errors.go
services/backend/internal/domain/volunteering/requests/request_repository.go
services/backend/internal/domain/volunteering/requests/request_service.go
services/backend/internal/domain/volunteering/requests/request_state.go
services/backend/internal/domain/volunteering/tags/actual_tag.go
services/backend/internal/domain/volunteering/tags/module.go
services/backend/internal/domain/volunteering/tags/tag.go
services/backend/internal/domain/volunteering/tags/tag_errors.go
services/backend/internal/domain/volunteering/tags/tag_repository.go
services/backend/internal/domain/volunteering/tags/tag_service.go
services/backend/internal/domain/volunteering/terbank_moderators/module.go
services/backend/internal/domain/volunteering/terbank_moderators/terbank_moderator.go
services/backend/internal/domain/volunteering/terbank_moderators/terbank_moderator_errors.go
services/backend/internal/domain/volunteering/terbank_moderators/terbank_moderator_repository.go
services/backend/internal/domain/volunteering/terbank_moderators/terbank_moderator_service.go
services/backend/internal/domain/volunteering/terbanks/actual_terbank.go
services/backend/internal/domain/volunteering/terbanks/module.go
services/backend/internal/domain/volunteering/terbanks/terbank.go
services/backend/internal/domain/volunteering/terbanks/terbank_errors.go
services/backend/internal/domain/volunteering/terbanks/terbank_repository.go
services/backend/internal/domain/volunteering/terbanks/terbank_service.go
services/backend/internal/domain/volunteering/volunteer_card/module.go
services/backend/internal/domain/volunteering/volunteer_card/volunteer_card.go
services/backend/internal/domain/volunteering/volunteer_card/volunteer_card_query.go
services/backend/internal/domain/volunteering/volunteer_card/volunteer_card_service.go
services/backend/internal/domain/volunteering/volunteer_tags/module.go
services/backend/internal/domain/volunteering/volunteer_tags/volunteer_tag.go
services/backend/internal/domain/volunteering/volunteer_tags/volunteer_tag_errors.go
services/backend/internal/domain/volunteering/volunteer_tags/volunteer_tag_repository.go
services/backend/internal/domain/volunteering/volunteer_tags/volunteer_tag_service.go
services/backend/internal/domain/volunteering/volunteers/module.go
services/backend/internal/domain/volunteering/volunteers/volunteer.go
services/backend/internal/domain/volunteering/volunteers/volunteer_errors.go
services/backend/internal/domain/volunteering/volunteers/volunteer_repository.go
services/backend/internal/domain/volunteering/volunteers/volunteer_service.go
services/backend/internal/drivers/http/middlewares/auth_middleware.go
services/backend/internal/drivers/http/middlewares/context_middleware.go
services/backend/internal/drivers/http/middlewares/module.go
services/backend/internal/drivers/http/middlewares/policy_middleware.go
services/backend/internal/drivers/http/middlewares/role_middleware.go
services/backend/internal/drivers/http/middlewares/test_auth_middleware.go
services/backend/internal/drivers/http/v1/assistant/assistant_controller.go
services/backend/internal/drivers/http/v1/assistant/assistant_requests.go
services/backend/internal/drivers/http/v1/assistant/assistant_routes.go
services/backend/internal/drivers/http/v1/health/health_controller.go
services/backend/internal/drivers/http/v1/health/health_routes.go
services/backend/internal/drivers/http/v1/reports/event_report_builder.go
services/backend/internal/drivers/http/v1/reports/report_controller.go
services/backend/internal/drivers/http/v1/reports/report_request.go
services/backend/internal/drivers/http/v1/reports/report_routes.go
services/backend/internal/drivers/http/v1/routes.go
services/backend/internal/drivers/http/v1/swagger/swagger_routes.go
services/backend/internal/drivers/http/v1/volunteering/event_card/event_card_controller.go
services/backend/internal/drivers/http/v1/volunteering/event_card/event_card_request.go
services/backend/internal/drivers/http/v1/volunteering/event_organizers/event_organizer_controller.go
services/backend/internal/drivers/http/v1/volunteering/event_organizers/event_organizer_requests.go
services/backend/internal/drivers/http/v1/volunteering/event_tags/event_tag_controller.go
services/backend/internal/drivers/http/v1/volunteering/event_tags/event_tag_request.go
services/backend/internal/drivers/http/v1/volunteering/events/event_controller.go
services/backend/internal/drivers/http/v1/volunteering/gosbs/gosb_controller.go
services/backend/internal/drivers/http/v1/volunteering/participation/participation_controller.go
services/backend/internal/drivers/http/v1/volunteering/participation/participation_requests.go
services/backend/internal/drivers/http/v1/volunteering/participation_card/participation_card_controller.go
services/backend/internal/drivers/http/v1/volunteering/participation_card/participation_card_request.go
services/backend/internal/drivers/http/v1/volunteering/project_card/project_card_controller.go
services/backend/internal/drivers/http/v1/volunteering/project_card/project_card_request.go
services/backend/internal/drivers/http/v1/volunteering/projects/project_controller.go
services/backend/internal/drivers/http/v1/volunteering/registry/registry_controller.go
services/backend/internal/drivers/http/v1/volunteering/request_card/request_card_controller.go
services/backend/internal/drivers/http/v1/volunteering/request_card/request_card_request.go
services/backend/internal/drivers/http/v1/volunteering/requests/request_contoller.go
services/backend/internal/drivers/http/v1/volunteering/requests/request_requests.go
services/backend/internal/drivers/http/v1/volunteering/tags/tags_controller.go
services/backend/internal/drivers/http/v1/volunteering/terbank_moderators/terbank_moderator_controller.go
services/backend/internal/drivers/http/v1/volunteering/terbank_moderators/terbank_moderator_requests.go
services/backend/internal/drivers/http/v1/volunteering/terbanks/terbank_controller.go
services/backend/internal/drivers/http/v1/volunteering/volunteer/volunteer_controller.go
services/backend/internal/drivers/http/v1/volunteering/volunteer/volunteer_requests.go
services/backend/internal/drivers/http/v1/volunteering/volunteer_card/volunteer_card_controller.go
services/backend/internal/drivers/http/v1/volunteering/volunteer_tag/volunteer_tag_controller.go
services/backend/internal/drivers/http/v1/volunteering/volunteer_tag/volunteer_tag_requests.go
services/backend/internal/drivers/http/v1/volunteering/volunteering_routes.go
services/backend/internal/drivers/seeders/admin_seeder.go
services/backend/internal/drivers/seeders/gosb_seeder.go
services/backend/internal/drivers/seeders/seeders.go
services/backend/internal/drivers/seeders/tag_seeder.go
services/backend/internal/drivers/seeders/terbank_seeder.go
services/backend/main.go
services/backend/migrations/000001_volunteer_table.down.sql
services/backend/migrations/000001_volunteer_table.up.sql
services/backend/migrations/000002_tag_table.down.sql
services/backend/migrations/000002_tag_table.up.sql
services/backend/migrations/000003_volunteer_tag_table.down.sql
services/backend/migrations/000003_volunteer_tag_table.up.sql
services/backend/migrations/000004_volunteer_role.down.sql
services/backend/migrations/000004_volunteer_role.up.sql
services/backend/migrations/000005_event_table.down.sql
services/backend/migrations/000005_event_table.up.sql
services/backend/migrations/000006_project_table.down.sql
services/backend/migrations/000006_project_table.up.sql
services/backend/migrations/000007_request_table.down.sql
services/backend/migrations/000007_request_table.up.sql
services/backend/migrations/000008_event_tag_table.down.sql
services/backend/migrations/000008_event_tag_table.up.sql
services/backend/migrations/000009_event_creator.down.sql
services/backend/migrations/000009_event_creator.up.sql
services/backend/migrations/000010_terbank_table.down.sql
services/backend/migrations/000010_terbank_table.up.sql
services/backend/migrations/000011_terbank_moderator_table.down.sql
services/backend/migrations/000011_terbank_moderator_table.up.sql
services/backend/migrations/000012_gosb_table.down.sql
services/backend/migrations/000012_gosb_table.up.sql
services/backend/migrations/000013_change_role.down.sql
services/backend/migrations/000013_change_role.up.sql
services/backend/migrations/000014_rename_terbank_id.down.sql
services/backend/migrations/000014_rename_terbank_id.up.sql
services/backend/migrations/000015_organizer_table.down.sql
services/backend/migrations/000015_organizer_table.up.sql
services/backend/migrations/000016_organizer_role.down.sql
services/backend/migrations/000016_organizer_role.up.sql
services/backend/migrations/000017_participation_table.down.sql
services/backend/migrations/000017_participation_table.up.sql
services/backend/migrations/000018_event_hours.down.sql
services/backend/migrations/000018_event_hours.up.sql
services/backend/migrations/000019_report_table.down.sql
services/backend/migrations/000019_report_table.up.sql
services/backend/migrations/000020_many_reports.down.sql
services/backend/migrations/000020_many_reports.up.sql
services/backend/migrations/000021_gosp_to_gosb.down.sql
services/backend/migrations/000021_gosp_to_gosb.up.sql
services/backend/migrations/000022_participation_limit.down.sql
services/backend/migrations/000022_participation_limit.up.sql
services/backend/migrations/000023_participation_count.down.sql
services/backend/migrations/000023_participation_count.up.sql
services/backend/migrations/000024_participation_hours.down.sql
services/backend/migrations/000024_participation_hours.up.sql
services/backend/migrations/000025_project_active.down.sql
services/backend/migrations/000025_project_active.up.sql
services/backend/migrations/000026_project_archive_at.down.sql
services/backend/migrations/000026_project_archive_at.up.sql
services/backend/pkg/bearer_parser.go
services/backend/pkg/excel_service.go
services/backend/pkg/gin_binding.go
services/backend/pkg/gorm.go
services/backend/pkg/gorm_logger.go
services/backend/pkg/gorm_paginate_simple.go
services/backend/pkg/http.go
services/backend/pkg/logger.go
services/backend/pkg/migrate_postgres.go
services/backend/pkg/module.go
services/backend/pkg/request_handler.go
services/bot/Dockerfile
services/bot/chain.pem
services/bot/core/__init__.py
services/bot/core/bot_kit/__init__.py
services/bot/core/bot_kit/fsm/__init__.py
services/bot/core/bot_kit/fsm/context.py
services/bot/core/bot_kit/fsm/state.py
services/bot/core/bot_kit/fsm/storage/__init__.py
services/bot/core/bot_kit/fsm/storage/base.py
services/bot/core/bot_kit/fsm/storage/in_memory.py
services/bot/core/bot_kit/router.py
services/bot/core/clients/__init__.py
services/bot/core/clients/admin.py
services/bot/core/clients/ai_assistant.py
services/bot/core/clients/base.py
services/bot/core/clients/event.py
services/bot/core/clients/participation.py
services/bot/core/clients/project.py
services/bot/core/clients/report.py
services/bot/core/clients/request.py
services/bot/core/clients/tag.py
services/bot/core/clients/terbank.py
services/bot/core/clients/user.py
services/bot/core/config/__init__.py
services/bot/core/config/bootstrap.py
services/bot/core/config/settings.py
services/bot/core/config/validation_config.py
services/bot/core/dependencies/__init__.py
services/bot/core/dependencies/admin.py
services/bot/core/dependencies/base.py
services/bot/core/dependencies/context.py
services/bot/core/dependencies/di.py
services/bot/core/dependencies/event.py
services/bot/core/dependencies/participation.py
services/bot/core/dependencies/project.py
services/bot/core/dependencies/registry.py
services/bot/core/dependencies/report.py
services/bot/core/dependencies/request.py
services/bot/core/dependencies/tag.py
services/bot/core/dependencies/terbank.py
services/bot/core/dependencies/user.py
services/bot/core/handlers/__init__.py
services/bot/core/handlers/admin.py
services/bot/core/handlers/ai.py
services/bot/core/handlers/event.py
services/bot/core/handlers/event_participations.py
services/bot/core/handlers/events_filters.py
services/bot/core/handlers/events_pages.py
services/bot/core/handlers/events_ui.py
services/bot/core/handlers/main.py
services/bot/core/handlers/moderation_common.py
services/bot/core/handlers/moderation_create.py
services/bot/core/handlers/moderation_edit.py
services/bot/core/handlers/moderation_menu.py
services/bot/core/handlers/noop.py
services/bot/core/handlers/participation_ui.py
services/bot/core/handlers/points.py
services/bot/core/handlers/profile.py
services/bot/core/handlers/project_pages.py
services/bot/core/handlers/projects.py
services/bot/core/handlers/projects_ui.py
services/bot/core/handlers/report.py
services/bot/core/handlers/request.py
services/bot/core/handlers/validators.py
services/bot/core/handlers/view_by_uuid.py
services/bot/core/handlers/volunteer_home.py
services/bot/core/markups/__init__.py
services/bot/core/markups/admin.py
services/bot/core/markups/ai.py
services/bot/core/markups/event.py
services/bot/core/markups/events_filters.py
services/bot/core/markups/gosb.py
services/bot/core/markups/main.py
services/bot/core/markups/moderation.py
services/bot/core/markups/pagination.py
services/bot/core/markups/participation.py
services/bot/core/markups/points.py
services/bot/core/markups/profile.py
services/bot/core/markups/project.py
services/bot/core/markups/reports.py
services/bot/core/markups/request.py
services/bot/core/markups/tags.py
services/bot/core/markups/terbank.py
services/bot/core/markups/volunteer_home.py
services/bot/core/schemas/__init__.py
services/bot/core/schemas/event.py
services/bot/core/schemas/gosb.py
services/bot/core/schemas/participation.py
services/bot/core/schemas/project.py
services/bot/core/schemas/request.py
services/bot/core/schemas/tag.py
services/bot/core/schemas/terbank.py
services/bot/core/schemas/user.py
services/bot/core/services/__init__.py
services/bot/core/services/admin.py
services/bot/core/services/base.py
services/bot/core/services/event.py
services/bot/core/services/file.py
services/bot/core/services/participation.py
services/bot/core/services/project.py
services/bot/core/services/registry/__init__.py
services/bot/core/services/registry/base.py
services/bot/core/services/registry/registry.py
services/bot/core/services/report.py
services/bot/core/services/requests.py
services/bot/core/services/tag.py
services/bot/core/services/terbank.py
services/bot/core/services/user.py
services/bot/core/states/__init__.py
services/bot/core/utils/__init__.py
services/bot/core/utils/date_time.py
services/bot/core/utils/events_filter.py
services/bot/core/utils/fsm/__init__.py
services/bot/core/utils/fsm/base.py
services/bot/core/utils/fsm/state.py
services/bot/core/utils/fsm/storage/__init__.py
services/bot/core/utils/fsm/storage/base.py
services/bot/core/utils/fsm/storage/memory.py
services/bot/core/utils/logger.py
services/bot/core/utils/messages.py
services/bot/core/utils/tags_toggle_ui.py
services/bot/core/utils/text_wrap.py
services/bot/main.py
services/bot/requirements.txt
templates.yml
utils/delete_all_events.zsh
utils/delete_all_users.zsh
utils/format
utils/prettify_logs.py
utils/restore_admin.zsh
utils/seed_events.json
utils/seed_test_data.zsh
utils/seed_users.tsv
validation.yml
Editor is loading...
Leave a Comment