Make project defaults reusable
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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'])}",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)))
|
||||
|
||||
Reference in New Issue
Block a user