import json import os SETTINGS_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "settings.json") DEFAULT_SETTINGS = { # Расписание "MORNING_SLOT_START": "08:30", "MORNING_SLOT_END": "10:00", "EVENING_SLOT_START": "18:30", "EVENING_SLOT_END": "20:00", "DAILY_CAROUSELS_LIMIT": 1, "DAILY_SINGLE_POSTS_LIMIT": 1, "CAROUSEL_SLIDE_COUNT": 5, # Промпты "PROMPT_SINGLE": """Використовуючи наступний новинний контекст: \"\"\"{news_context}\"\"\" {exclude_str} Створи 1 одиночний пост для Threads українською мовою. Вибери одну найцікавішу новину з фокусом на одну з тем: ІІ-безпека/етика, DeFi/криптовалюти, або стартапи/тех-сектор. ⚡ ТВОЇ СУПЕРСИЛИ (SKILLS) & КРИТИЧНІ ПРАВИЛА: 1. РОЛЬ: SENIOR SOCIAL MEDIA COPYWRITER (Текст посту) - Психологія: Пиши максимально живим, людським стилем (як реальний експерт у Threads), уникаючи будь-якого ШІ-жаргону. Тон: {tone}. - Позиція та думка: КАТЕГОРИЧНО ЗАБОРОНЕНО використовувати шаблон "[Факт]. [Риторичний вопрос]. Деталі...". Замість цього додай власну чітку експертну думку/позицію перед будь-яким запитанням (наприклад: "Я вважаю X, тому що Y. А як до цього ставитесь ви?"). Позиція має бути до запитання, а не замість неї. Уникай пустих риторичних питань. - Самодостатність: Пост має бути повністю самодостатнім і нести цінність одразу в тексті. Посилання або картинка — лише доповнення, а не привід клікати. - Заголовки: Пиши заголовки-крючки (hook), що чіпляють увагу, а не нудні заголовки-новини. - Посилання: КАТЕГОРИЧНО ЗАБОРОНЕНО вставляти будь-які URL або посилання в полі description. Посилання на джерело має повертатися виключно окремо в ключі source_url. - Хештеги: КАТЕГОРИЧНО ЗАБОРОНЕНО використовувати масові теги (#AI, #Crypto, #ШІ, #Технології). Використовуй лише 1-2 вузьких тега максимум (наприклад, #DeFiУкраїна, #LLMбезпека, #ХарківTech). Розташовуй теги строго в самому кінці тексту. 2. РОЛЬ: ART DIRECTOR & UI/UX DESIGNER (Візуал) - Дизайн фону: Ти створюєш преміальні промпти для генерації зображень (Flux.1). Ти ідеально відчуваєш композицію, тому завжди вимагаєш "clean negative space" для читабельності тексту. - Копірайт для карток: Заголовок картки — до 4 слів (punchy). Тези — до 8 слів, тільки саме "м'ясо". Поверни результат строго у форматі JSON із наступними ключами: { "category": "одна з трьох категорій: 'AI', 'Business' або 'Crypto'", "bg_prompt": "англійською мовою детальний промпт для генерації фону (Flux.1). Premium dark aesthetics, volumetric lighting, glowing elements, background only, smooth clean negative space, absolutely no text", "card_title": "короткий заголовок (до 4 слів, без води)", "card_bullets": [ "теза 1 (чіткий факт до 8 слів)", "теза 2 (висновок або інсайт до 8 слів)" ], "description": "Супровідний текст посту (до 400 символів). Жива мова з власною позицією, без шаблонів. Жодних посилань/URL у цьому тексті! Строго 1-2 вузьких тега в самому кінці.", "source_url": "коротке посилання на першоджерело новини (обов'язково)" } Повертай ТІЛЬКИ чистий JSON-об'єкт.""", "PROMPT_CAROUSEL": """Використовуючи наступний новинний контекст: \"\"\"{news_context}\"\"\" {exclude_str} Створи глибокий розбір однієї з новин у вигляді каруселі з {slide_count} слайдів для Threads українською мовою з фокусом на теми: безпека/етика ІІ, DeFi/криптовалюти, або стартапи/тех-сфера. ⚡ ТВОЇ СУПЕРСИЛИ (SKILLS) & КРИТИЧНІ ПРАВИЛА: 1. РОЛЬ: SENIOR SOCIAL MEDIA COPYWRITER - Сюжет: Будуй карусель як історію (Curiosity Gap). Кожен слайд змушує свайпнути далі. Тон: {tone}. - Текст посту: Напиши коментар автора до каруселі. Він має бути повністю самодостатнім, нести самостійну цінність та містити твою чітку експертну думку/позицію перед питанням до аудиторії. - Заборонені шаблони: КАТЕГОРИЧНО ЗАБОРОНЕНО використовувати шаблони на кшталт "[Факт]. [Риторичне питання]. Деталі в каруселі. Джерело". Натомість використовуй заголовок-крючок (hook). - Заборонені фрази: Уникай типових ШІ-фразочок ("безумовно", "у сучасному світі", "у світі технологій"). Пиши як реальний живий експерт. - Посилання: КАТЕГОРИЧНО ЗАБОРОНЕНО вставляти будь-які URL або посилання в полі description. Посилання на джерело має повертатися виключно окремо в ключі source_url. - Хештеги: КАТЕГОРИЧНО ЗАБОРОНЕНО використовувати масові теги (#AI, #Crypto, #ШІ, #Технології). Використовуй лише 1-2 вузьких тега максимум. Розташовуй теги строго в самому кінці тексту. 2. РОЛЬ: ART DIRECTOR & UI/UX DESIGNER - Фонова динаміка: Ти пишеш промпти для фонів так, щоб вони еволюціонували від слайду до слайду, зберігаючи загальну кінематографічную стилістику (premium dark aesthetics, neon, volumetric lighting). - Читабельність: Завжди гарантуй вільний простір для тексту ('smooth clean negative space, absolutely no text'). Поверни результат строго у форматі JSON із наступними ключами: { "category": "одна з трьох категорій: 'AI', 'Business' або 'Crypto'", "description": "Супровідний текст посту (до 350 символів). Жива людська мова, що виражає твою позицію та інтригує переглянути карусель. Жодних посилань/URL у цьому полі! Строго 1-2 вузьких тега в самому кінці.", "source_url": "коротке посилання на першоджерело новини (обов'язково)", "slides": [ масив з {slide_count} об'єктів слайдів: { "title": "...", "bullets": ["...", "..."], "bg_prompt": "..." } ] } Повертай ТІЛЬКИ чистий JSON-об'єкт.""", "PROMPT_PREDICTOR": """Ты — AI-редактор и эксперт по виральному контенту в соцсетях. Твоя задача — оценить черновик поста для Threads перед его публикацией. {context} Черновик поста: --- {post_text} --- Оцени потенциал этого поста по 10-балльной шкале (где 10 — абсолютный хит, а 1 — скучная новостная сводка, которую никто не лайкнет). Пост должен иметь мощный 'крючок' (hook) в первом предложении, вызывать эмоции (шок, интерес, несогласие) и стимулировать дискуссию в комментариях. Верни СТРОГО JSON: { "score": 8, "criticism": "оставь пустым, если балл >= 7, иначе напиши 1-2 предложения, что именно изменить" } Возвращай только чистый JSON без markdown.""" } def load_settings(): if not os.path.exists(SETTINGS_PATH): return DEFAULT_SETTINGS.copy() try: with open(SETTINGS_PATH, "r", encoding="utf-8") as f: data = json.load(f) # Merge with defaults to ensure all keys exist merged = DEFAULT_SETTINGS.copy() merged.update(data) return merged except Exception as e: print(f"[SettingsManager] Ошибка чтения settings.json: {e}") return DEFAULT_SETTINGS.copy() def save_settings(new_settings): try: current = load_settings() current.update(new_settings) with open(SETTINGS_PATH, "w", encoding="utf-8") as f: json.dump(current, f, ensure_ascii=False, indent=2) return True except Exception as e: print(f"[SettingsManager] Ошибка записи settings.json: {e}") return False