Untitled
4ae4d
plain_text
a month ago
64 kB
3
Indexable
--- services/bot/core/handlers/events_pages.py ---
from core.schemas import EventCardsPageSchema, EventSchema
from core.services import EventService
from core.utils import EventsFilter
EVENTS_PAGE_SIZE = 10
def fetch_events_page(
*,
event_service: EventService,
offset: int,
limit: int,
requester_messenger_id: int,
filters: EventsFilter | None = None,
mine: bool = False,
) -> EventCardsPageSchema:
flt = filters or EventsFilter()
page = offset // limit + 1
tag_ids = list(flt.tag_ids or [])
# Получаем карточки
event_cards, current_page, total_pages = event_service.get_event_cards_page(
requester_messenger_id=requester_messenger_id,
page=page,
limit=limit,
tag_ids=tag_ids or None,
mine=mine,
)
# Возвращаем только карточки
return EventCardsPageSchema(
event_cards=event_cards,
current_page=current_page,
total_pages=total_pages,
)
def render_events_list_text( # legacy
*,
events: list[EventSchema],
offset: int,
total: int | None,
footer: str | None = None,
) -> str:
if not events:
return "⚠️ Мероприятий не найдено."
header_total = str(total) if total is not None else "?"
lines = [f"Мероприятия (показаны {offset + 1} - {offset + len(events)} из {header_total}):\n"]
for event in events:
date_str = event.date_time.strftime("%d.%m.%Y %H:%M")
lines.append(f" - {event.title} --- {date_str} --- `{event.id}`")
if footer:
lines.append("")
lines.append(footer)
return "\n".join(lines)
--- services/bot/core/services/event.py ---
from datetime import datetime, timezone
from typing import Any
from requests import HTTPError
from core.clients import EventClient
from core.schemas import EventCardSchema, ParticipationSchema
from core.services.base import BaseService
from core.services.registry import BaseServiceRegistry
from core.utils import logger
class EventService(BaseService): # legacy
"""
сервис мероприятий на стороне бота.
на данный момент:
- ходит в backend через EventClient
- умеет получать список мероприятий по городу
- умеет получать одно мероприятие по id
- создает мероприятие через клиент используя мастера создания
"""
def __init__(self, registry: BaseServiceRegistry, client: EventClient): # legacy
# DI может вызвать клиент без аргументов.
super().__init__(registry)
self.client = client
def get_event_cards_page(
self,
*,
requester_messenger_id: int,
page: int = 1,
limit: int = 10,
title_str: str | None = None,
tag_ids: list[str] | None = None,
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,
)
)
match resp.status_code:
case 200:
pass
case 400:
raise HTTPError("Error while getting event cards list: invalid parameters")
case 401:
raise HTTPError("Error while getting event cards list: unauthorized")
case 500:
raise HTTPError("Error while getting event cards list: internal server error")
case _:
raise HTTPError(
f"Error while getting event cards list: unexpected status {resp.status_code}"
)
body = resp.json() or {}
items = body.get("event_cards") or []
page_meta = body.get("page_meta") or {}
result: list[EventCardSchema] = []
for item in items:
if not isinstance(item, dict):
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: int) -> 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 == 200:
return None
if resp.status_code == 409:
return resp.json()["error"]
raise HTTPError(resp.json()["error"])
def get_event_by_id(
self,
event_id: str,
requester_messenger_id: int,
) -> EventCardSchema | None:
logger.debug(
"[EventService] get_event_by_id: event_id=%s requester=%s",
event_id,
requester_messenger_id,
)
resp = self.client.get_event_info(
event_id=event_id,
requester_messenger_id=requester_messenger_id,
)
match resp.status_code:
case 200:
pass
case 400:
raise HTTPError("Error while getting event card: invalid parameters")
case 401:
raise HTTPError("Error while getting event card: unauthorized")
case 404:
return None
case 500:
raise HTTPError("Error while getting event card: internal server error")
case _:
raise HTTPError(
f"Error while getting event card: unexpected status {resp.status_code}"
)
logger.debug(
"[EventService] get_event_by_id: body=%s",
resp.json(),
)
return EventCardSchema.from_dict(resp.json() or {})
def sign_out_by_event(self, event_id: str, messenger_id: int) -> 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: int,
code: str,
) -> ParticipationSchema | None:
resp = self.client.confirm_participation(
event_id=event_id,
messenger_id=messenger_id,
code=code,
)
match resp.status_code:
case 200:
pass
case 400:
return None
case 401:
raise HTTPError("Error while confirming participation: unauthorized")
case 404:
return None
case 500:
raise HTTPError("Error while confirming participation: internal server error")
case _:
raise HTTPError(
f"Error while confirming participation: unexpected status {resp.status_code}"
)
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: int):
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: int) -> 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: int) -> 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,
)
match resp.status_code:
case 200:
pass
case 401:
raise HTTPError("Error while creating event: unauthorized")
case 500:
raise HTTPError("Error while creating event: internal server error")
case _:
raise HTTPError(f"Error while creating event: unexpected status {resp.status_code}")
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: int, payload: dict[str, int | str]):
resp = self.client.update_event(
event_id=event_id,
messenger_id=messenger_id,
payload=payload,
)
match resp.status_code:
case 200:
return True
case 401:
raise HTTPError("Error while patching event: unauthorized")
case 403:
raise HTTPError("Error while patching event: forbidden")
case 404:
raise HTTPError("Error while patching event: event not found")
case 500:
raise HTTPError("Error while patching event: internal server error")
case _:
raise HTTPError(f"Error while patching event: unexpected status {resp.status_code}")
--- services/bot/core/clients/event.py ---
from core.clients.base import BaseApiClient
class EventClient(BaseApiClient):
def get_event_cards_page(
self,
*,
requester_messenger_id: int,
page: int = 1,
limit: int = 10,
title_str: str | None = None,
tag_ids: list[str] | None = None,
):
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": str(requester_messenger_id)},
params=params,
)
def get_my_event_cards_page(
self,
*,
requester_messenger_id: int,
page: int = 1,
limit: int = 10,
title_str: str | None = None,
tag_ids: list[str] | None = None,
):
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": str(requester_messenger_id)},
params=params,
)
def get_event_info(self, event_id: str, requester_messenger_id: int):
"""GET /events/{event_id}/card"""
return self.get(
f"/events/{event_id}/card",
headers={"Authorization": str(requester_messenger_id)},
)
def sign_up(self, *, event_id: str, messenger_id: int):
"""POST /events/{event_id}/participations/signup"""
return self.post(
f"/events/{event_id}/participations/signup",
headers={"Authorization": str(messenger_id)},
)
def sign_out(self, *, event_id: str, messenger_id: int):
"""DELETE /events/{event_id}/participations/cancel"""
return self.delete(
f"/events/{event_id}/participations/cancel",
headers={"Authorization": str(messenger_id)},
)
def confirm_participation(self, *, event_id: str, messenger_id: int, code: str):
"""POST /events/{event_id}/participations/confirm"""
return self.post(
f"/events/{event_id}/participations/confirm",
headers={"Authorization": str(messenger_id)},
json={"code": code},
)
def add_tag(self, *, event_id: str, tag_id: str, messenger_id: int):
"""POST /events/{event_id}/tags"""
return self.post(
f"/events/{event_id}/tags",
headers={"Authorization": str(messenger_id)},
json={"tag_id": tag_id},
)
def remove_tag(self, *, event_id: str, tag_id: str, messenger_id: int):
"""DELETE /events/{event_id}/tags"""
return self.delete(
f"/events/{event_id}/tags",
headers={"Authorization": str(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: int,
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": str(requester_messenger_id)},
json=payload,
)
def update_event(self, event_id: str, messenger_id: int, payload: dict[str, int | str]):
"""PATCH /events/{event_id}"""
return self.patch(
f"/events/{event_id}",
headers={"Authorization": str(messenger_id)},
json=payload,
)
--- services/bot/core/handlers/request.py ---
from __future__ import annotations
from dataclasses import dataclass
from dialog_bot_sdk.entities.messaging import (
InteractiveMediaGroup,
UpdateInteractiveMediaEvent,
UpdateMessage,
)
from dialog_bot_sdk.interactive_media import Button, MediaGroupBuilder
from core.bot_kit.fsm import FSMContext, State, StatesGroup
from core.bot_kit.router import Router
from core.config import bot
from core.markups import (
format_request_details,
requests_pagination_keyboard,
requests_select_keyboard,
)
from core.schemas import RequestCardsPageSchema
from core.services import RequestService
from core.utils import delete_prev_message, delete_prev_message_by_peer
requests_rt = Router()
class RequestRejectState(StatesGroup):
wait_reason = State()
@dataclass(frozen=True)
class RequestUIContext:
name: str
back_value: str
back_label: str
_UI: dict[str, RequestUIContext] = {
# модерация -> заявки
"requests": RequestUIContext(
name="requests",
back_value="requests",
back_label="⬅️ Назад",
),
"my_requests": RequestUIContext(
name="my_requests",
back_value="my_requests",
back_label="⬅️ Назад",
),
}
def get_ui(ctx_name: str) -> RequestUIContext:
return _UI.get(ctx_name, _UI["requests"])
def build_context_back(*, label: str = "❌ Отмена", value: str = "", ctx: str = ""):
ui = get_ui(ctx) # noqa: F841
def build_back_keyboard(
*, value: str = "", label: str = "❌ Отмена", media_id: str = "request_open"
) -> list[InteractiveMediaGroup]:
return MediaGroupBuilder(
[Button(media_id=media_id, value=value, label=label)],
).build() # type: ignore
def _send_requests_list_screen(
peer,
*,
ctx_name: str,
offset: int,
limit: int,
page_data: RequestCardsPageSchema,
header: str | None = None,
empty_text: str | None = None,
filter_summary: str | None = None,
note: str | None = None,
):
"""Отправляет список заявок"""
request_cards = page_data.request_cards
has_prev = page_data.has_prev
has_next = page_data.has_next
header_part = header if header is not None else "📋 Заявки\n\n"
header_part = f"{note}\n\n{header_part}" if note else header_part
filter_part = f"\n\n{filter_summary}" if filter_summary else ""
if not request_cards:
text = (
header_part
+ (
empty_text
or "⚠️ Заявок не найдено.\n\nПопробуй изменить критерии или вернись назад."
)
+ filter_part
)
im: list[InteractiveMediaGroup] = []
im += requests_pagination_keyboard(
offset=offset,
limit=limit,
has_prev=has_prev,
has_next=has_next,
leave_value=ctx_name,
leave_label="⬅️ Назад",
)
bot.messaging.send_message(peer=peer, text=text, interactive_media_groups=im)
return
text = (
header_part
+ f"Страница: {page_data.current_page} / {page_data.total_pages}\n\n"
+ "Выбери заявку для просмотра:"
+ filter_part
)
im: list[InteractiveMediaGroup] = []
im += requests_select_keyboard(
request_cards,
open_media_id="request_open",
start_index=offset + 1,
per_row=1,
)
im += requests_pagination_keyboard(
offset=offset,
limit=limit,
has_prev=has_prev,
has_next=has_next,
leave_value=ctx_name,
leave_label="🛡️ В меню модерации",
)
bot.messaging.send_message(peer=peer, text=text, interactive_media_groups=im)
def send_request_cards_page(
peer,
*,
offset: int,
request_service: RequestService,
ctx_name: str,
note: str | None = None,
context: FSMContext | None = None,
):
"""Отправляет страницу с заявками"""
limit = 10
page = offset // limit + 1
page_data = request_service.get_request_cards_page(
requester_messenger_id=peer.id,
page=page,
limit=limit,
)
_send_requests_list_screen(
peer,
ctx_name=ctx_name,
offset=offset,
limit=limit,
page_data=page_data,
note=note,
)
@bot.di
def all_requests_handler(
event: UpdateInteractiveMediaEvent,
context: FSMContext,
request_service: RequestService,
):
"""Обработчик для просмотра всех заявок"""
delete_prev_message_by_peer(bot, event.peer)
send_request_cards_page(
event.peer,
offset=0,
request_service=request_service,
ctx_name="requests",
context=context,
)
@bot.di
def requests_page_handler(
event: UpdateInteractiveMediaEvent,
context: FSMContext,
request_service: RequestService,
):
"""Обработчик пагинации заявок"""
delete_prev_message_by_peer(bot, event.peer)
try:
offset = int(event.data.value)
if offset < 0:
offset = 0
except Exception:
offset = 0
send_request_cards_page(
event.peer,
offset=offset,
request_service=request_service,
ctx_name="requests",
context=context,
)
@bot.di
def request_open_handler(
event: UpdateInteractiveMediaEvent,
context: FSMContext,
request_service: RequestService,
):
"""Обработчик открытия конкретной заявки"""
delete_prev_message_by_peer(bot, event.peer)
context.set_state(None)
request_id = str(event.data.value or "").strip()
# Получаем детали заявки
request_card = request_service.get_request_by_id(request_id, event.peer.id)
if request_card is None:
bot.messaging.send_message(
peer=event.peer,
text="⚠️ Заявка не найдена.",
interactive_media_groups=build_back_keyboard(media_id="requests"),
)
return
# Формируем детальное сообщение
text = format_request_details(request_card)
# Клавиатура для действий с заявкой
keyboard = _build_request_actions_keyboard(request_card, ctx_name="requests")
bot.messaging.send_message(
peer=event.peer,
text=text,
interactive_media_groups=keyboard,
)
def _build_request_actions_keyboard(request_card, ctx_name: str):
"""Строит клавиатуру для действий с заявкой"""
actions = []
req = request_card.request
actions.append(
Button(
media_id="leave",
value=ctx_name,
label="⬅️ Назад",
)
)
if req.status.lower() == "pending":
actions.append(
Button(
media_id="request_approve",
value=req.id,
label="✅ Одобрить",
)
)
actions.append(
Button(
media_id="request_reject",
value=req.id,
label="❌ Отклонить",
)
)
return MediaGroupBuilder(actions).build()
@bot.di
def requests_prev_page_handler(
event: UpdateInteractiveMediaEvent,
context: FSMContext,
request_service: RequestService,
):
"""Обработчик предыдущей страницы заявок"""
delete_prev_message_by_peer(bot, event.peer)
try:
offset = int(event.data.value)
if offset < 0:
offset = 0
except Exception:
offset = 0
send_request_cards_page(
event.peer,
offset=offset,
request_service=request_service,
ctx_name="moderation",
context=context,
)
@bot.di
def requests_next_page_handler(
event: UpdateInteractiveMediaEvent,
context: FSMContext,
request_service: RequestService,
):
"""Обработчик следующей страницы заявок"""
delete_prev_message_by_peer(bot, event.peer)
try:
offset = int(event.data.value)
if offset < 0:
offset = 0
except Exception:
offset = 0
send_request_cards_page(
event.peer,
offset=offset,
request_service=request_service,
ctx_name="moderation",
context=context,
)
@bot.di
def request_approve_handler(
event: UpdateInteractiveMediaEvent,
context: FSMContext,
request_service: RequestService,
):
"""Обработчик одобрения заявки"""
delete_prev_message_by_peer(bot, event.peer)
request_id = str(event.data.value or "").strip()
success = request_service.approve_request(request_id, event.peer.id)
if success:
note = "✅ Заявка успешно одобрена!"
else:
note = "⚠️ Не удалось одобрить заявку. Попробуй позже."
send_request_cards_page(
event.peer,
offset=0,
request_service=request_service,
ctx_name="moderation",
context=context,
note=note,
)
@bot.di
def request_reject_handler(
event: UpdateInteractiveMediaEvent,
context: FSMContext,
request_service: RequestService,
):
"""Обработчик отклонения заявки - запрашивает причину"""
delete_prev_message_by_peer(bot, event.peer)
request_id = str(event.data.value or "").strip()
context.update_data({"reject_request_id": request_id})
context.set_state(RequestRejectState.wait_reason)
bot.messaging.send_message(
peer=event.peer,
text="💬 Укажите причину отклонения заявки:",
interactive_media_groups=build_back_keyboard(value=request_id, media_id="request_open"),
)
@requests_rt.message(state=RequestRejectState.wait_reason)
@bot.di
def request_reject_reason_handler(
message: UpdateMessage,
context: FSMContext,
request_service: RequestService,
):
"""Обработчик получения причины отклонения заявки"""
delete_prev_message(bot, message)
data = context.get_data()
request_id = data.get("reject_request_id")
reason = message.message.text_message.text.strip()
note = ""
if not reason:
note = "⚠️ Причина не может быть пустой."
else:
success = request_service.reject_request(request_id, message.peer.id, reason)
note = "ℹ️ Заявка успешно отклонена." if success else "⚠️ Не удалось отклонить заявку."
context.set_state(None)
context.update_data({"reject_request_id": None})
send_request_cards_page(
message.peer,
offset=0,
request_service=request_service,
ctx_name="moderation",
note=note,
)
--- services/bot/core/markups/request.py ---
from dialog_bot_sdk.interactive_media import Button, InteractiveMediaGroup, MediaGroupBuilder
from core.markups.event import format_event_details
from core.markups.pagination import pagination_keyboard
from core.schemas import RequestCardSchema
from core.utils import format_datetime, wrap_text
def _get_status_emoji(status: str) -> str:
status_emojis = {
"pending": "⏳", # на рассмотрении
"approved": "✅", # одобрено
"rejected": "❌", # отклонено
}
return status_emojis.get(status.lower(), "⚪")
def _get_status_label(status: str) -> str:
status_label = {
"pending": "на рассмотрении",
"approved": "одобрено",
"rejected": "отлконено",
}
return status_label.get(status.lower(), "-")
def _get_request_type_label(request_type: str) -> str:
types = {
"event": "Мероприятие вне проекта",
"project_event": "Мероприятие в рамках",
"project": "Проект",
}
return types.get(request_type.lower(), request_type.capitalize())
def _request_button_label(request_card: RequestCardSchema) -> str:
"""Формирует подпись для кнопки заявки (короткий вариант)"""
req = request_card.request
resource = request_card.resource
request_type = _get_request_type_label(req.request_type)
resource_name = "—"
if resource.event:
resource_name = resource.event.title or "—"
status_emoji = _get_status_emoji(req.status)
status_label = _get_status_label(req.status)
return f"{request_type}\nНазвание: {resource_name}\nСтатус: {status_label} {status_emoji}"
def requests_select_keyboard(
requests_cards: list[RequestCardSchema],
*,
open_media_id: str,
start_index: int = 1,
per_row: int = 1,
) -> list[InteractiveMediaGroup]:
"""
Клавиатура для выбора заявки из списка (аналог events_select_keyboard)
"""
groups: list[InteractiveMediaGroup] = []
row: list[Button] = []
idx = start_index
for request_card in requests_cards:
label = _request_button_label(request_card)
row.append(Button(media_id=open_media_id, value=str(request_card.request.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 requests_pagination_keyboard(
*,
offset: int,
limit: int,
has_prev: bool,
has_next: bool,
prev_media_id: str = "requests_prev",
next_media_id: str = "requests_next",
leave_value: str = "moderation",
leave_label: str = "🛡️ В меню модерации",
keep_layout: bool = True,
) -> list[InteractiveMediaGroup]:
"""
Клавиатура пагинации для списка заявок
"""
return pagination_keyboard(
offset=offset,
limit=limit,
has_prev=has_prev,
has_next=has_next,
prev_media_id=prev_media_id,
next_media_id=next_media_id,
leave_value=leave_value,
leave_label=leave_label,
keep_layout=keep_layout,
)
def requests_filters_keyboard(
ctx_name: str = "moderation",
offset: int = 0,
) -> list[InteractiveMediaGroup]:
"""
Клавиатура фильтров для заявок
"""
return MediaGroupBuilder(
[Button(media_id="requests_filters_open", value=f"{ctx_name}|{offset}", label="🔍 Фильтры")]
).build()
def format_request_details(request_card: RequestCardSchema) -> str:
"""Форматирует детальную информацию о заявке"""
def _format_reject_reason(reject_reason: str) -> str:
reject_reason = wrap_text(reject_reason, 40, "")
return "❌ Причина отказа:\n```plain\n" + reject_reason + "\n```"
req = request_card.request
resource = request_card.resource
status_emoji = _get_status_emoji(req.status)
status_label = _get_status_label(req.status)
lines = [
f"📋 *Заявка* {status_emoji}",
"",
f"🆔 ID: `{req.id}`",
f"📝 Тип: {_get_request_type_label(req.request_type)}",
f"👤 Отправитель: `{req.messenger_id}`",
f"📅 Создана: {format_datetime(req.created_at)}",
f"🔄 Обновлена: {format_datetime(req.updated_at)}",
f"📊 Статус: {status_label} {status_emoji}",
"",
]
if req.status.lower() == "rejected" and req.reject_reason:
lines.append(_format_reject_reason(req.reject_reason))
if req.request_type == "event" or req.request_type == "project_event":
event_formated = format_event_details(
resource,
show_code=True,
show_organizer=True,
make_frame=True,
wrap=True,
)
lines.append(event_formated)
return "\n".join(lines)
def request_actions_keyboard(
request_id: str,
*,
status: str,
back_value: str,
back_label: str,
approve_media_id: str = "request_approve",
reject_media_id: str = "request_reject",
) -> list[InteractiveMediaGroup]:
"""
Клавиатура для действий с заявкой (аналог event_actions_keyboard)
"""
actions = []
if status.lower() == "pending":
actions.append(Button(media_id=approve_media_id, value=request_id, label="✅ Одобрить"))
actions.append(Button(media_id=reject_media_id, value=request_id, label="❌ Отклонить"))
actions.append(Button(media_id="leave", value=back_value, label=back_label))
return MediaGroupBuilder(actions).build()
--- services/bot/core/markups/pagination.py ---
from __future__ import annotations
from typing import cast
from dialog_bot_sdk.entities.messaging import InteractiveMediaStyle
from dialog_bot_sdk.interactive_media import Button, InteractiveMediaGroup, MediaGroupBuilder
def pagination_keyboard( # legacy
*,
offset: int,
limit: int,
has_prev: bool,
has_next: bool,
prev_media_id: str,
next_media_id: str,
prev_label: str = "⬅️ Назад",
next_label: str = "➡️ Вперёд",
include_leave: bool = True,
leave_media_id: str = "leave",
leave_value: str | None = None,
leave_label: str | None = None,
style: InteractiveMediaStyle | None = None,
keep_layout: bool = False,
placeholder_media_id: str = "noop",
placeholder_value: str = "noop",
placeholder_label: str = " ",
hide_when_all_placeholders: bool = False,
add_view_by_uuid_button: bool = False,
view_by_uuid_media_id: str = "view_by_uuid",
view_by_uuid_value: str = "events",
view_by_uuid_label: str = "🔎 По UUID",
) -> list[InteractiveMediaGroup]:
"""
унифицированная пагинация.
keep_layout=True:
если add_view_by_uuid_button=True:
- если include_leave=True: 4 слота [prev|ph] [leave|ph] [uuid] [next|ph]
- если include_leave=False: 3 слота [prev|ph] [uuid] [next|ph]
если add_view_by_uuid_button=False:
- если include_leave=True: 3 слота [prev|ph] [leave|ph] [next|ph]
- если include_leave=False: 2 слота [prev|ph] [next|ph]
hide_when_all_placeholders=True: если из кнопок только ph - то возвращаем пустой лист.
"""
def _btn(media_id: str, value: str, label: str) -> Button: # legacy
if style is None:
return Button(media_id=media_id, value=value, label=label)
return Button(media_id, value, label, style)
def _ph(slot: str) -> Button: # legacy
return _btn(
placeholder_media_id + "_" + slot,
placeholder_value,
placeholder_label,
)
def _prev() -> Button: # legacy
return _btn(prev_media_id, str(max(offset - limit, 0)), prev_label)
def _next() -> Button: # legacy
return _btn(next_media_id, str(offset + limit), next_label)
def _view_by_uuid() -> Button: # legacy
return _btn(
view_by_uuid_media_id,
str(view_by_uuid_value),
str(view_by_uuid_label),
)
def _leave() -> Button | None: # legacy
if not (include_leave and leave_value and leave_label):
return None
return _btn(leave_media_id, str(leave_value), str(leave_label))
# --- 3-slot layout for include_leave
if include_leave and keep_layout:
buttons: list[Button] = []
leave_btn = _leave()
buttons.append(_prev() if has_prev else _ph("prev"))
buttons.append(leave_btn if leave_btn is not None else _ph("mid"))
# --- 4-slot: [prev] [leave] [uuid] [next]
if add_view_by_uuid_button:
buttons.append(_view_by_uuid())
buttons.append(_next() if has_next else _ph("next"))
if hide_when_all_placeholders and all(
str(getattr(b, "media_id", "")).startswith(placeholder_media_id + "_") for b in buttons
):
return []
return cast(list[InteractiveMediaGroup], MediaGroupBuilder(buttons).build())
# --- default linear layout (как было)
buttons = []
if has_prev:
buttons.append(_prev())
elif keep_layout:
buttons.append(_ph("prev"))
leave_btn = _leave()
if leave_btn is not None:
buttons.append(leave_btn)
if add_view_by_uuid_button:
buttons.append(_view_by_uuid())
if has_next:
buttons.append(_next())
elif keep_layout:
buttons.append(_ph("next"))
if hide_when_all_placeholders and all(
str(getattr(b, "media_id", "")).startswith(placeholder_media_id + "_") for b in buttons
):
return []
return cast(list[InteractiveMediaGroup], MediaGroupBuilder(buttons).build() if buttons else [])
.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
pyproject.toml
pyrightconfig.json
python
seed.yml
services/ai/Dockerfile
services/ai/main.py
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/language_model.py
services/ai/src/clients/vector_store.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/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/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/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_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/participation_card_mapper.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/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_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_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/participation_card.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/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/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/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/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/roles/module.go
services/backend/internal/domain/volunteering/roles/role_service.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/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/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/ai_assistant.py
services/bot/core/clients/base.py
services/bot/core/clients/event.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/dependencies/__init__.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/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/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.py
services/bot/core/handlers/noop.py
services/bot/core/handlers/points.py
services/bot/core/handlers/profile.py
services/bot/core/handlers/report.py
services/bot/core/handlers/request.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/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/inline.py
services/bot/core/markups/pagination.py
services/bot/core/markups/points.py
services/bot/core/markups/profile.py
services/bot/core/markups/request.py
services/bot/core/markups/tag.py
services/bot/core/markups/tags.py
services/bot/core/markups/terbank.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/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/base.py
services/bot/core/services/event.py
services/bot/core/services/file.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/format
utils/prettify_logs.py
Editor is loading...
Leave a Comment