mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-01-20 11:50:27 +00:00
274 lines
7.1 KiB
Python
274 lines
7.1 KiB
Python
import logging
|
|
from typing import List, Optional
|
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.config import settings
|
|
from app.database.crud.faq import (
|
|
bulk_update_order,
|
|
create_faq_page,
|
|
delete_faq_page,
|
|
get_faq_page_by_id,
|
|
get_faq_pages,
|
|
get_faq_setting,
|
|
set_faq_enabled,
|
|
update_faq_page,
|
|
)
|
|
from app.database.models import FaqPage, FaqSetting
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class FaqService:
|
|
MAX_PAGE_LENGTH = 3500
|
|
|
|
@staticmethod
|
|
def _normalize_language(language: str) -> str:
|
|
base_language = language or settings.DEFAULT_LANGUAGE or "ru"
|
|
return base_language.split("-")[0].lower()
|
|
|
|
@staticmethod
|
|
def normalize_language(language: str) -> str:
|
|
return FaqService._normalize_language(language)
|
|
|
|
@classmethod
|
|
async def get_setting(
|
|
cls,
|
|
db: AsyncSession,
|
|
language: str,
|
|
*,
|
|
fallback: bool = True,
|
|
) -> Optional[FaqSetting]:
|
|
lang = cls._normalize_language(language)
|
|
setting = await get_faq_setting(db, lang)
|
|
|
|
if setting or not fallback:
|
|
return setting
|
|
|
|
default_lang = cls._normalize_language(settings.DEFAULT_LANGUAGE)
|
|
if lang != default_lang:
|
|
return await get_faq_setting(db, default_lang)
|
|
|
|
return setting
|
|
|
|
@classmethod
|
|
async def is_enabled(cls, db: AsyncSession, language: str) -> bool:
|
|
pages = await cls.get_pages(db, language)
|
|
return bool(pages)
|
|
|
|
@classmethod
|
|
async def set_enabled(
|
|
cls,
|
|
db: AsyncSession,
|
|
language: str,
|
|
enabled: bool,
|
|
) -> FaqSetting:
|
|
lang = cls._normalize_language(language)
|
|
return await set_faq_enabled(db, lang, enabled)
|
|
|
|
@classmethod
|
|
async def toggle_enabled(
|
|
cls,
|
|
db: AsyncSession,
|
|
language: str,
|
|
) -> FaqSetting:
|
|
lang = cls._normalize_language(language)
|
|
setting = await get_faq_setting(db, lang)
|
|
new_status = True
|
|
if setting:
|
|
new_status = not setting.is_enabled
|
|
return await set_faq_enabled(db, lang, new_status)
|
|
|
|
@classmethod
|
|
async def get_pages(
|
|
cls,
|
|
db: AsyncSession,
|
|
language: str,
|
|
*,
|
|
include_inactive: bool = False,
|
|
fallback: bool = True,
|
|
) -> List[FaqPage]:
|
|
lang = cls._normalize_language(language)
|
|
pages = await get_faq_pages(db, lang, include_inactive=include_inactive)
|
|
|
|
if pages:
|
|
setting = await get_faq_setting(db, lang)
|
|
if setting and not setting.is_enabled and not include_inactive:
|
|
return []
|
|
return pages
|
|
|
|
if not fallback:
|
|
return []
|
|
|
|
default_lang = cls._normalize_language(settings.DEFAULT_LANGUAGE)
|
|
if lang == default_lang:
|
|
return []
|
|
|
|
fallback_pages = await get_faq_pages(
|
|
db,
|
|
default_lang,
|
|
include_inactive=include_inactive,
|
|
)
|
|
|
|
if not fallback_pages:
|
|
return []
|
|
|
|
setting = await get_faq_setting(db, default_lang)
|
|
if setting and not setting.is_enabled and not include_inactive:
|
|
return []
|
|
|
|
return fallback_pages
|
|
|
|
@classmethod
|
|
async def get_page(
|
|
cls,
|
|
db: AsyncSession,
|
|
page_id: int,
|
|
language: str,
|
|
*,
|
|
fallback: bool = True,
|
|
include_inactive: bool = False,
|
|
) -> Optional[FaqPage]:
|
|
page = await get_faq_page_by_id(db, page_id)
|
|
if not page:
|
|
return None
|
|
|
|
lang = cls._normalize_language(language)
|
|
default_lang = cls._normalize_language(settings.DEFAULT_LANGUAGE)
|
|
|
|
if not include_inactive and not page.is_active:
|
|
return None
|
|
|
|
if page.language == lang:
|
|
return page
|
|
|
|
if fallback and page.language == default_lang:
|
|
return page
|
|
|
|
return None
|
|
|
|
@classmethod
|
|
async def create_page(
|
|
cls,
|
|
db: AsyncSession,
|
|
*,
|
|
language: str,
|
|
title: str,
|
|
content: str,
|
|
display_order: Optional[int] = None,
|
|
is_active: Optional[bool] = None,
|
|
) -> FaqPage:
|
|
lang = cls._normalize_language(language)
|
|
is_active_value = True if is_active is None else bool(is_active)
|
|
page = await create_faq_page(
|
|
db,
|
|
language=lang,
|
|
title=title,
|
|
content=content,
|
|
display_order=display_order,
|
|
is_active=is_active_value,
|
|
)
|
|
|
|
setting = await get_faq_setting(db, lang)
|
|
if not setting:
|
|
await set_faq_enabled(db, lang, True)
|
|
|
|
return page
|
|
|
|
@classmethod
|
|
async def update_page(
|
|
cls,
|
|
db: AsyncSession,
|
|
page: FaqPage,
|
|
*,
|
|
title: Optional[str] = None,
|
|
content: Optional[str] = None,
|
|
display_order: Optional[int] = None,
|
|
is_active: Optional[bool] = None,
|
|
) -> FaqPage:
|
|
return await update_faq_page(
|
|
db,
|
|
page,
|
|
title=title,
|
|
content=content,
|
|
display_order=display_order,
|
|
is_active=is_active,
|
|
)
|
|
|
|
@classmethod
|
|
async def delete_page(cls, db: AsyncSession, page_id: int) -> None:
|
|
await delete_faq_page(db, page_id)
|
|
|
|
@classmethod
|
|
async def reorder_pages(
|
|
cls,
|
|
db: AsyncSession,
|
|
language: str,
|
|
pages: List[FaqPage],
|
|
) -> None:
|
|
lang = cls._normalize_language(language)
|
|
ordered = [page for page in pages if page.language == lang]
|
|
payload = [(page.id, index + 1) for index, page in enumerate(ordered)]
|
|
await bulk_update_order(db, payload)
|
|
|
|
@staticmethod
|
|
def split_content_into_pages(
|
|
content: str,
|
|
*,
|
|
max_length: Optional[int] = None,
|
|
) -> List[str]:
|
|
if not content:
|
|
return []
|
|
|
|
normalized = content.replace("\r\n", "\n").strip()
|
|
if not normalized:
|
|
return []
|
|
|
|
limit = max_length or FaqService.MAX_PAGE_LENGTH
|
|
if len(normalized) <= limit:
|
|
return [normalized]
|
|
|
|
paragraphs = [
|
|
paragraph.strip()
|
|
for paragraph in normalized.split("\n\n")
|
|
if paragraph.strip()
|
|
]
|
|
|
|
pages: List[str] = []
|
|
current = ""
|
|
|
|
def flush_current() -> None:
|
|
nonlocal current
|
|
if current:
|
|
pages.append(current.strip())
|
|
current = ""
|
|
|
|
for paragraph in paragraphs:
|
|
candidate = f"{current}\n\n{paragraph}".strip() if current else paragraph
|
|
if len(candidate) <= limit:
|
|
current = candidate
|
|
continue
|
|
|
|
flush_current()
|
|
|
|
if len(paragraph) <= limit:
|
|
current = paragraph
|
|
continue
|
|
|
|
start = 0
|
|
while start < len(paragraph):
|
|
chunk = paragraph[start : start + limit]
|
|
pages.append(chunk.strip())
|
|
start += limit
|
|
|
|
current = ""
|
|
|
|
flush_current()
|
|
|
|
if not pages:
|
|
return [normalized[:limit]]
|
|
|
|
return pages
|
|
|