feat(content): implement Content Engine MVP (v1.0) with GTM integration

This commit is contained in:
2026-01-20 12:45:59 +00:00
parent 401ad7e6e8
commit 41e60c72bc
17 changed files with 1169 additions and 30 deletions

View File

@@ -0,0 +1,181 @@
import argparse
import base64
import json
import logging
import sys
import os
from datetime import datetime
import content_db_manager as db_manager
# Ensure helper path is correct
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
from helpers import call_gemini_flash, scrape_website_details
from config import Config
LOG_DIR = "Log_from_docker"
if not os.path.exists(LOG_DIR):
os.makedirs(LOG_DIR)
run_timestamp = datetime.now().strftime("%y-%m-%d_%H-%M-%S")
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
Config.load_api_keys()
def get_copywriter_instruction(lang='de'):
return r"""
Du bist ein Senior Copywriter und SEO-Experte. Deine Spezialität ist der 'Challenger Sale'.
Du schreibst Texte, die fachlich tief fundiert, professionell und leicht aggressiv/fordernd sind.
DEIN STIL:
- Keine Buzzwords ohne Substanz.
- Fokus auf den 'Cost of Inaction' (Was kostet es den Kunden, wenn er NICHT handelt?).
- Übersetzung von Technik in geschäftlichen Nutzen.
- SEO-Integration: Baue Keywords natürlich aber präsent ein.
"""
# --- MODES ---
def list_gtm_projects(payload):
projects = db_manager.get_all_gtm_projects()
return {"projects": projects}
def import_project(payload):
gtm_id = payload.get('gtmProjectId')
if not gtm_id:
return {"error": "Missing gtmProjectId"}
result = db_manager.import_gtm_project(gtm_id)
if not result:
return {"error": "GTM Project not found or import failed"}
return result
def list_content_projects(payload):
projects = db_manager.get_all_content_projects()
return {"projects": projects}
def load_project_details(payload):
project_id = payload.get('projectId')
project = db_manager.get_content_project(project_id)
if not project:
return {"error": "Project not found"}
assets = db_manager.get_project_assets(project_id)
project['assets'] = assets
return project
def seo_brainstorming(payload):
project_id = payload.get('projectId')
lang = payload.get('lang', 'de')
project = db_manager.get_content_project(project_id)
if not project:
return {"error": "Project context not found"}
gtm_data = project.get('gtm_data_snapshot', {})
# GOLDEN RULE: Use Raw Quotes and .format()
prompt = r"""
Basierend auf folgendem GTM-Kontext (Strategie für ein technisches Produkt):
{gtm_context}
AUFGABE:
Generiere eine strategische Liste von 15 SEO-Keywords.
1. 5 Short-Tail Fokus-Keywords (z.B. Produktkategorie + 'kaufen/mieten').
2. 10 Long-Tail Keywords, die spezifische Pain Points oder Usecases adressieren (z.B. 'Kostenreduktion bei Sicherheitsrundgängen').
Die Keywords müssen für Entscheider relevant sein (CFO, Head of Security, Operations Manager).
Output NUR als JSON Liste von Strings.
""".format(gtm_context=json.dumps(gtm_data))
response = call_gemini_flash(prompt, system_instruction=get_copywriter_instruction(lang), json_mode=True)
keywords = json.loads(response)
db_manager.save_seo_strategy(project_id, {"seed_keywords": keywords})
return {"keywords": keywords}
def generate_section(payload):
project_id = payload.get('projectId')
section_key = payload.get('sectionKey') # e.g., 'hero', 'problem', 'features'
manual_content = payload.get('manualContent')
lang = payload.get('lang', 'de')
keywords = payload.get('keywords', [])
if manual_content:
# User is saving their manual edits
db_manager.save_content_asset(project_id, 'website_section', section_key, f"Section: {section_key}", manual_content, keywords)
return {"status": "saved", "sectionKey": section_key}
project = db_manager.get_content_project(project_id)
if not project:
return {"error": "Project context not found"}
gtm_data = project.get('gtm_data_snapshot', {})
# Context extraction
category = project.get('category')
prompt = r"""
Erstelle den Website-Inhalt für die Sektion '{section}' eines Produkts in der Kategorie '{cat}'.
STRATEGIE-KONTEXT:
{gtm_context}
SEO-KEYWORDS ZU NUTZEN:
{kws}
ANFORDERUNG:
- Schreibe im Stil eines Senior Copywriters (fachlich fundiert, Challenger Sale).
- Format: Markdown.
- Die Sektion muss den Nutzer zur nächsten Aktion (CTA) führen.
""".format(
section=section_key,
cat=category,
gtm_context=json.dumps(gtm_data),
kws=json.dumps(keywords)
)
content = call_gemini_flash(prompt, system_instruction=get_copywriter_instruction(lang), json_mode=False)
# Save as asset
db_manager.save_content_asset(project_id, 'website_section', section_key, f"Section: {section_key}", content, keywords)
return {"content": content, "sectionKey": section_key}
def main():
parser = argparse.ArgumentParser(description="Content Engine Orchestrator")
parser.add_argument("--mode", required=True)
parser.add_argument("--payload_file", help="Path to JSON payload")
args = parser.parse_args()
payload = {}
if args.payload_file:
with open(args.payload_file, 'r') as f:
payload = json.load(f)
modes = {
"list_gtm_projects": list_gtm_projects,
"import_project": import_project,
"list_content_projects": list_content_projects,
"load_project": load_project_details,
"seo_brainstorming": seo_brainstorming,
"generate_section": generate_section,
}
if args.mode in modes:
try:
result = modes[args.mode](payload)
print(json.dumps(result, ensure_ascii=False))
except Exception as e:
logging.error(f"Error in mode {args.mode}: {str(e)}")
print(json.dumps({"error": str(e)}))
else:
print(json.dumps({"error": f"Unknown mode: {args.mode}"}))
if __name__ == "__main__":
main()