Make project defaults reusable

This commit is contained in:
Your Name
2026-08-03 15:50:45 +05:00
parent 2358e9d660
commit 6ae61b0302
11 changed files with 116 additions and 29 deletions
+10
View File
@@ -71,6 +71,11 @@ PROVIDER_OPTIONS = [
{"value": "openai_compatible", "label": "OpenAI-compatible API"},
]
SITE_POSTER_PROVIDER_OPTIONS = [
{"value": "", "label": "Не выбран"},
{"value": "ghost", "label": "Ghost"},
]
PROMPT_HINTS = {
"ai_qualifier_prompt": {
"title": "Свободная инструкция квалификатора",
@@ -284,6 +289,10 @@ SETTING_ORDER = {
"max_poster_dry_run",
],
"Site Poster": [
"site_poster_provider",
"site_poster_base_url",
"site_poster_secret_file",
"site_poster_fallback_title",
"site_poster_interval_sec",
"site_poster_photo_batch_size",
"site_poster_video_batch_size",
@@ -450,6 +459,7 @@ def base_context(request: Request, user: dict | None, **extra: Any) -> dict[str,
"app_env": settings.app_env,
"app_title": settings.display_admin_title,
"site_title": settings.display_site_title,
"site_poster_provider_options": SITE_POSTER_PROVIDER_OPTIONS,
}
ctx.update(extra)
return ctx
+7
View File
@@ -325,6 +325,13 @@
<option value="{{ p.value }}" {% if s.value_json == p.value %}selected{% endif %}>{{ p.label }}</option>
{% endfor %}
</select>
{% elif s.key == "site_poster_provider" %}
<select name="value" class="select max-w-sm w-full">
{% for p in site_poster_provider_options %}
<option value="{{ p.value }}" {% if s.value_json == p.value %}selected{% endif %}>{{ p.label }}</option>
{% endfor %}
</select>
{% elif s.key == "ai_qualifier_model" %}
<select name="value" class="select max-w-sm w-full">
+1 -1
View File
@@ -324,7 +324,7 @@ class DailyReportWorker:
report_date = data["date"].strftime("%d.%m.%Y")
return "\n".join(
[
f"Ежедневный отчет N8 Parser за {report_date}",
f"Ежедневный отчет {settings.display_site_title} за {report_date}",
"",
"Сбор:",
f"raw собрано: {fmt_int(data['raw_collected'])}",
+37 -12
View File
@@ -22,8 +22,10 @@ class SitePoster:
self.interval_sec = 300
self.photo_batch_size = 10
self.video_batch_size = 1
self.ghost_url = "https://f-n8.ru"
self.key_file = "/opt/vk-parser/.site-poster-key"
self.provider = ""
self.base_url = ""
self.secret_file = ""
self.fallback_title = "Новость"
async def init(self) -> None:
self.pool = await get_pool()
@@ -32,25 +34,48 @@ class SitePoster:
self.interval_sec = max(30, await fetch_int_setting("site_poster_interval_sec", 300))
self.photo_batch_size = max(0, await fetch_int_setting("site_poster_photo_batch_size", 10))
self.video_batch_size = max(0, await fetch_int_setting("site_poster_video_batch_size", 1))
self.ghost_url = str(await fetch_setting("site_poster_ghost_url", "https://f-n8.ru") or "https://f-n8.ru").rstrip("/")
self.key_file = str(await fetch_setting("site_poster_key_file", "/opt/vk-parser/.site-poster-key") or "").strip()
if not self.key_file:
raise RuntimeError("site_poster_key_file is empty")
legacy_ghost_url = str(await fetch_setting("site_poster_ghost_url", "") or "").strip()
legacy_key_file = str(await fetch_setting("site_poster_key_file", "") or "").strip()
self.provider = str(await fetch_setting("site_poster_provider", "") or "").strip().lower()
if not self.provider and legacy_ghost_url:
self.provider = "ghost"
self.base_url = str(
await fetch_setting("site_poster_base_url", "")
or legacy_ghost_url
or ""
).rstrip("/")
self.secret_file = str(
await fetch_setting("site_poster_secret_file", "")
or legacy_key_file
or ""
).strip()
self.fallback_title = str(await fetch_setting("site_poster_fallback_title", "Новость") or "Новость").strip()
async def publish_mode(self, flag: str, limit: int) -> tuple[int, int]:
if limit < 1:
return 0, 0
command = [
def build_publish_command(self, flag: str, limit: int) -> list[str]:
if self.provider != "ghost":
raise RuntimeError(f"unsupported site_poster_provider: {self.provider or '<empty>'}")
if not self.base_url:
raise RuntimeError("site_poster_base_url is empty")
if not self.secret_file and not os.environ.get("SITE_POSTER_GHOST_ADMIN_KEY", "").strip():
raise RuntimeError("site_poster_secret_file and SITE_POSTER_GHOST_ADMIN_KEY are empty")
return [
sys.executable,
str(self.project_root / "scripts" / "publish_site_batch.py"),
flag,
"--limit",
str(limit),
"--ghost-url",
self.ghost_url,
self.base_url,
"--key-file",
self.key_file,
self.secret_file,
"--fallback-title",
self.fallback_title or "Новость",
]
async def publish_mode(self, flag: str, limit: int) -> tuple[int, int]:
if limit < 1:
return 0, 0
command = self.build_publish_command(flag, limit)
env = os.environ.copy()
env["PYTHONPATH"] = str(self.project_root / "src")
env["PYTHONUNBUFFERED"] = "1"
+1 -1
View File
@@ -125,7 +125,7 @@ class TelegramPoster:
await self.bot.session.close()
self.bot = Bot(token=token)
self.bot_token = token
self.chat_id, self.thread_id = parse_topic(str(await fetch_setting("tg_poster_chat_id", "-1003712724327") or ""))
self.chat_id, self.thread_id = parse_topic(str(await fetch_setting("tg_poster_chat_id", "") or ""))
self.schedule = self.normalize_schedule(await fetch_setting("tg_poster_schedule_json", []))
self.media_group_max_items = max(1, min(MAX_MEDIA_GROUP, await fetch_int_setting("tg_poster_media_group_max_items", 10)))
self.caption_limit = max(128, min(1024, await fetch_int_setting("tg_poster_caption_limit", 1024)))