userType: external

gitBranch: main

type: user

role: user

content: Review linkedin-generator.py, photo-analyzer.py, blog-writer.py. Check code quality, error handling, security, production-readiness. Give Issues (Critical/Medium/Minor), Recommendations, Quality Score 1-10 per script. Be concise. Then exit.

permissionMode: default


type: message

role: assistant

type: text

text: I'll review all three scripts in parallel.

service_tier: standard

type: assistant


slug: wild-snacking-clover

content:
     1→#!/usr/bin/env python3
     2→"""
     3→RateRight LinkedIn Post Generator
     4→
     5→Generate professional LinkedIn posts for the construction industry with multiple length variations.
     6→
     7→Usage:
     8→    python linkedin-generator.py "topic/theme"
     9→
    10→Example:
    11→    python linkedin-generator.py "steel fixing crew shortage in Sydney"
    12→    python linkedin-generator.py "why formwork quality matters for project success"
    13→
    14→Output:
    15→    - Short post (50-100 words)
    16→    - Medium post (150-200 words)
    17→    - Long post (250-300 words)
    18→    - Each includes hashtags and call-to-action
    19→"""
    20→
    21→import sys
    22→import json
    23→import logging
    24→from datetime import datetime
    25→from pathlib import Path
    26→
    27→try:
    28→    import openai
    29→except ImportError:
    30→    print("OpenAI library not found. Installing...")
    31→    import subprocess
    32→    subprocess.check_call([sys.executable, "-m", "pip", "install", "openai"])
    33→    import openai
    34→
    35→# Setup logging
    36→log_dir = Path("/home/ccuser/rateright-growth/rivet/memory")
    37→log_dir.mkdir(exist_ok=True)
    38→logging.basicConfig(
    39→    level=logging.INFO,
    40→    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    41→    handlers=[
    42→        logging.FileHandler(log_dir / "content-engine.log"),
    43→        logging.StreamHandler()
    44→    ]
    45→)
    46→logger = logging.getLogger(__name__)
    47→
    48→def load_openai_key():
    49→    """Load OpenAI API key from secrets file."""
    50→    try:
    51→        secrets_path = "/root/.clawdbot/secrets.json"
    52→        with open(secrets_path, 'r') as f:
    53→            secrets = json.load(f)
    54→            return secrets.get("openai_api_key") or secrets.get("OPENAI_API_KEY")
    55→    except Exception as e:
    56→        logger.error(f"Failed to load OpenAI key: {e}")
    57→        return None
    58→
    59→def generate_linkedin_posts(topic):
    60→    """Generate LinkedIn posts using OpenAI API."""
    61→
    62→    api_key = load_openai_key()
    63→    if not api_key:
    64→        logger.error("OpenAI API key not found")
    65→        return None
    66→
    67→    client = openai.OpenAI(api_key=api_key)
    68→
    69→    system_prompt = """You are a content marketing expert for RateRight, a construction hiring marketplace in Australia.
    70→    Create professional LinkedIn posts that speak to contractors and construction professionals.
    71→
    72→    Tone: Professional but relatable, contractor-to-contractor, data-driven
    73→    Focus: Construction industry insights, hiring challenges, workforce trends
    74→    Audience: Sydney-based contractors, project managers, construction business owners
    75→
    76→    Include:
    77→    - Industry-specific insights and data
    78→    - Practical takeaways for contractors
    79→    - Call-to-action related to hiring or workforce management
    80→    - Relevant hashtags (max 8 per post)
    81→    - Australian spelling and terminology"""
    82→
    83→    user_prompt = f"""Create 3 LinkedIn posts about: {topic}
    84→
    85→Requirements:
    86→1. Short version (50-100 words) - Quick insight with 1-2 key points
    87→2. Medium version (150-200 words) - Deeper analysis with 2-3 key points
    88→3. Long version (250-300 words) - Comprehensive insight with data/examples
    89→
    90→Each post must:
    91→- Start with a strong hook
    92→- Include construction industry context
    93→- Provide actionable insights
    94→- End with a clear call-to-action
    95→- Use 6-8 relevant hashtags
    96→- Reference Sydney/Australia construction market when relevant
    97→
    98→Topic: {topic}"""
    99→
   100→    try:
   101→        response = client.chat.completions.create(
   102→            model="gpt-4",
   103→            messages=[
   104→                {"role": "system", "content": system_prompt},
   105→                {"role": "user", "content": user_prompt}
   106→            ],
   107→            temperature=0.7,
   108→            max_tokens=1500
   109→        )
   110→
   111→        content = response.choices[0].message.content
   112→        logger.info(f"Successfully generated LinkedIn posts for topic: {topic}")
   113→        return content
   114→
   115→    except Exception as e:
   116→        logger.error(f"OpenAI API error: {e}")
   117→        return None
   118→
   119→def format_output(content, topic):
   120→    """Format the generated content for display."""
   121→    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
   122→
   123→    output = f"""# RateRight LinkedIn Post Generator
   124→Generated on: {timestamp}
   125→Topic: {topic}
   126→
   127→{'='*60}
   128→{content}
   129→{'='*60}
   130→
   131→"""
   132→    return output
   133→
   134→def main():
   135→    """Main function to handle command line execution."""
   136→
   137→    if len(sys.argv) < 2:
   138→        print("Usage: python linkedin-generator.py 'topic/theme'")
   139→        print("Example: python linkedin-generator.py 'steel fixing crew shortage in Sydney'")
   140→        sys.exit(1)
   141→
   142→    topic = " ".join(sys.argv[1:])
   143→    logger.info(f"Starting LinkedIn post generation for topic: {topic}")
   144→
   145→    # Generate posts
   146→    content = generate_linkedin_posts(topic)
   147→
   148→    if content:
   149→        formatted_output = format_output(content, topic)
   150→        print(formatted_output)
   151→        logger.info("LinkedIn posts generated successfully")
   152→    else:
   153→        print("Failed to generate LinkedIn posts. Check logs for details.")
   154→        logger.error("Failed to generate LinkedIn posts")
   155→        sys.exit(1)
   156→
   157→if __name__ == "__main__":
   158→    main()

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>

content:
#!/usr/bin/env python3
"""
RateRight LinkedIn Post Generator

Generate professional LinkedIn posts for the construction industry with multiple length variations.

Usage:
    python linkedin-generator.py "topic/theme"

Example:
    python linkedin-generator.py "steel fixing crew shortage in Sydney"
    python linkedin-generator.py "why formwork quality matters for project success"

Output:
    - Short post (50-100 words)
    - Medium post (150-200 words)
    - Long post (250-300 words)
    - Each includes hashtags and call-to-action
"""

import sys
import json
import logging
from datetime import datetime
from pathlib import Path

try:
    import openai
except ImportError:
    print("OpenAI library not found. Installing...")
    import subprocess
    subprocess.check_call([sys.executable, "-m", "pip", "install", "openai"])
    import openai

# Setup logging
log_dir = Path("/home/ccuser/rateright-growth/rivet/memory")
log_dir.mkdir(exist_ok=True)
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler(log_dir / "content-engine.log"),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

def load_openai_key():
    """Load OpenAI API key from secrets file."""
    try:
        secrets_path = "/root/.clawdbot/secrets.json"
        with open(secrets_path, 'r') as f:
            secrets = json.load(f)
            return secrets.get("openai_api_key") or secrets.get("OPENAI_API_KEY")
    except Exception as e:
        logger.error(f"Failed to load OpenAI key: {e}")
        return None

def generate_linkedin_posts(topic):
    """Generate LinkedIn posts using OpenAI API."""

    api_key = load_openai_key()
    if not api_key:
        logger.error("OpenAI API key not found")
        return None

    client = openai.OpenAI(api_key=api_key)

    system_prompt = """You are a content marketing expert for RateRight, a construction hiring marketplace in Australia.
    Create professional LinkedIn posts that speak to contractors and construction professionals.

    Tone: Professional but relatable, contractor-to-contractor, data-driven
    Focus: Construction industry insights, hiring challenges, workforce trends
    Audience: Sydney-based contractors, project managers, construction business owners

    Include:
    - Industry-specific insights and data
    - Practical takeaways for contractors
    - Call-to-action related to hiring or workforce management
    - Relevant hashtags (max 8 per post)
    - Australian spelling and terminology"""

    user_prompt = f"""Create 3 LinkedIn posts about: {topic}

Requirements:
1. Short version (50-100 words) - Quick insight with 1-2 key points
2. Medium version (150-200 words) - Deeper analysis with 2-3 key points
3. Long version (250-300 words) - Comprehensive insight with data/examples

Each post must:
- Start with a strong hook
- Include construction industry context
- Provide actionable insights
- End with a clear call-to-action
- Use 6-8 relevant hashtags
- Reference Sydney/Australia construction market when relevant

Topic: {topic}"""

    try:
        response = client.chat.completions.create(
            model="gpt-4",
            messages=[
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": user_prompt}
            ],
            temperature=0.7,
            max_tokens=1500
        )

        content = response.choices[0].message.content
        logger.info(f"Successfully generated LinkedIn posts for topic: {topic}")
        return content

    except Exception as e:
        logger.error(f"OpenAI API error: {e}")
        return None

def format_output(content, topic):
    """Format the generated content for display."""
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    output = f"""# RateRight LinkedIn Post Generator
Generated on: {timestamp}
Topic: {topic}

{'='*60}
{content}
{'='*60}

"""
    return output

def main():
    """Main function to handle command line execution."""

    if len(sys.argv) < 2:
        print("Usage: python linkedin-generator.py 'topic/theme'")
        print("Example: python linkedin-generator.py 'steel fixing crew shortage in Sydney'")
        sys.exit(1)

    topic = " ".join(sys.argv[1:])
    logger.info(f"Starting LinkedIn post generation for topic: {topic}")

    # Generate posts
    content = generate_linkedin_posts(topic)

    if content:
        formatted_output = format_output(content, topic)
        print(formatted_output)
        logger.info("LinkedIn posts generated successfully")
    else:
        print("Failed to generate LinkedIn posts. Check logs for details.")
        logger.error("Failed to generate LinkedIn posts")
        sys.exit(1)

if __name__ == "__main__":
    main()


content:
     1→#!/usr/bin/env python3
     2→"""
     3→RateRight Construction Photo Analyzer
     4→
     5→Analyze construction site photos to identify trade types, safety gear, equipment, and conditions.
     6→Generates marketing insights and post caption suggestions.
     7→
     8→Usage:
     9→    python photo-analyzer.py /path/to/photo.jpg
    10→
    11→Example:
    12→    python photo-analyzer.py ./site-photos/formwork-crew.jpg
    13→    python photo-analyzer.py https://example.com/construction-photo.jpg
    14→
    15→Output:
    16→    - Trade type identification
    17→    - Safety equipment assessment
    18→    - Equipment and tools identified
    19→    - Site conditions evaluation
    20→    - Marketing insights (e.g., "steel fixing crew needed")
    21→    - Social media caption suggestions
    22→"""
    23→
    24→import sys
    25→import json
    26→import logging
    27→from pathlib import Path
    28→import base64
    29→import requests
    30→
    31→try:
    32→    import openai
    33→except ImportError:
    34→    print("OpenAI library not found. Installing...")
    35→    import subprocess
    36→    subprocess.check_call([sys.executable, "-m", "pip", "install", "openai"])
    37→    import openai
    38→
    39→# Setup logging
    40→log_dir = Path("/home/ccuser/rateright-growth/rivet/memory")
    41→log_dir.mkdir(exist_ok=True)
    42→logging.basicConfig(
    43→    level=logging.INFO,
    44→    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    45→    handlers=[
    46→        logging.FileHandler(log_dir / "content-engine.log"),
    47→        logging.StreamHandler()
    48→    ]
    49→)
    50→logger = logging.getLogger(__name__)
    51→
    52→def load_openai_key():
    53→    """Load OpenAI API key from secrets file."""
    54→    try:
    55→        secrets_path = "/root/.clawdbot/secrets.json"
    56→        with open(secrets_path, 'r') as f:
    57→            secrets = json.load(f)
    58→            return secrets.get("openai_api_key") or secrets.get("OPENAI_API_KEY")
    59→    except Exception as e:
    60→        logger.error(f"Failed to load OpenAI key: {e}")
    61→        return None
    62→
    63→def encode_image(image_path):
    64→    """Encode image to base64 for API processing."""
    65→    try:
    66→        if image_path.startswith(('http://', 'https://')):
    67→            # Download image from URL
    68→            response = requests.get(image_path, timeout=30)
    69→            response.raise_for_status()
    70→            return base64.b64encode(response.content).decode('utf-8')
    71→        else:
    72→            # Read local image file
    73→            with open(image_path, "rb") as image_file:
    74→                return base64.b64encode(image_file.read()).decode('utf-8')
    75→    except Exception as e:
    76→        logger.error(f"Failed to encode image: {e}")
    77→        return None
    78→
    79→def analyze_construction_photo(image_path):
    80→    """Analyze construction photo using OpenAI Vision API."""
    81→
    82→    api_key = load_openai_key()
    83→    if not api_key:
    84→        logger.error("OpenAI API key not found")
    85→        return None
    86→
    87→    client = openai.OpenAI(api_key=api_key)
    88→
    89→    # Encode image
    90→    base64_image = encode_image(image_path)
    91→    if not base64_image:
    92→        return None
    93→
    94→    system_prompt = """You are a construction industry expert analyzing site photos for RateRight,
    95→    an Australian construction hiring marketplace. Provide detailed analysis for marketing purposes.
    96→
    97→    Focus on identifying:
    98→    1. Trade types and specializations visible
    99→    2. Safety equipment and compliance
   100→    3. Equipment, tools, and machinery
   101→    4. Site conditions and project phase
   102→    5. Workforce size and composition
   103→    6. Quality standards and workmanship level
   104→
   105→    Generate marketable insights about hiring needs and workforce requirements."""
   106→
   107→    user_prompt = """Analyze this construction site photo and provide:
   108→
   109→1. TRADE IDENTIFICATION
   110→   - Primary trade(s) visible
   111→   - Specialization level (apprentice, journeyman, expert)
   112→   - Number of workers visible
   113→
   114→2. SAFETY ASSESSMENT
   115→   - PPE compliance (hard hats, hi-vis, steel caps, etc.)
   116→   - Safety equipment present
   117→   - Any safety concerns visible
   118→
   119→3. EQUIPMENT & TOOLS
   120→   - Major equipment visible
   121→   - Hand tools and specialized gear
   122→   - Equipment quality/condition
   123→
   124→4. SITE CONDITIONS
   125→   - Project type (residential, commercial, infrastructure)
   126→   - Construction phase
   127→   - Site organization and cleanliness
   128→   - Weather conditions if apparent
   129→
   130→5. MARKETABLE INSIGHTS
   131→   - Hiring needs this photo suggests
   132→   - Skill gaps or shortages evident
   133→   - Equipment/supply needs
   134→   - Project timeline implications
   135→
   136→6. SOCIAL MEDIA CAPTIONS
   137→   - 3 LinkedIn caption suggestions
   138→   - 2 Instagram caption suggestions
   139→   - 1 TikTok caption idea
   140→   - Include relevant hashtags and emojis where appropriate
   141→
   142→Format response with clear sections and bullet points. Be specific and actionable for marketing purposes."""
   143→
   144→    try:
   145→        response = client.chat.completions.create(
   146→            model="gpt-4-vision-preview",
   147→            messages=[
   148→                {"role": "system", "content": system_prompt},
   149→                {
   150→                    "role": "user",
   151→                    "content": [
   152→                        {"type": "text", "text": user_prompt},
   153→                        {
   154→                            "type": "image_url",
   155→                            "image_url": {
   156→                                "url": f"data:image/jpeg;base64,{base64_image}"
   157→                            }
   158→                        }
   159→                    ]
   160→                }
   161→            ],
   162→            max_tokens=1500
   163→        )
   164→
   165→        content = response.choices[0].message.content
   166→        logger.info(f"Successfully analyzed construction photo: {image_path}")
   167→        return content
   168→
   169→    except Exception as e:
   170→        logger.error(f"OpenAI Vision API error: {e}")
   171→        return None
   172→
   173→def format_output(content, image_path):
   174→    """Format the analysis for display."""
   175→    from datetime import datetime
   176→
   177→    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
   178→
   179→    output = f"""# RateRight Construction Photo Analyzer
   180→Analyzed on: {timestamp}
   181→Image: {image_path}
   182→
   183→{'='*60}
   184→{content}
   185→{'='*60}
   186→
   187→"""
   188→    return output
   189→
   190→def main():
   191→    """Main function to handle command line execution."""
   192→
   193→    if len(sys.argv) < 2:
   194→        print("Usage: python photo-analyzer.py /path/to/photo.jpg")
   195→        print("Example: python photo-analyzer.py ./site-photos/formwork-crew.jpg")
   196→        print("         python photo-analyzer.py https://example.com/photo.jpg")
   197→        sys.exit(1)
   198→
   199→    image_path = sys.argv[1]
   200→    logger.info(f"Starting photo analysis for: {image_path}")
   201→
   202→    # Validate image path
   203→    if not image_path.startswith(('http://', 'https://')):
   204→        if not Path(image_path).exists():
   205→            print(f"Error: Image file not found: {image_path}")
   206→            logger.error(f"Image file not found: {image_path}")
   207→            sys.exit(1)
   208→
   209→    # Analyze photo
   210→    content = analyze_construction_photo(image_path)
   211→
   212→    if content:
   213→        formatted_output = format_output(content, image_path)
   214→        print(formatted_output)
   215→        logger.info("Photo analysis completed successfully")
   216→    else:
   217→        print("Failed to analyze photo. Check logs for details.")
   218→        logger.error("Photo analysis failed")
   219→        sys.exit(1)
   220→
   221→if __name__ == "__main__":
   222→    main()

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>

content:
#!/usr/bin/env python3
"""
RateRight Construction Photo Analyzer

Analyze construction site photos to identify trade types, safety gear, equipment, and conditions.
Generates marketing insights and post caption suggestions.

Usage:
    python photo-analyzer.py /path/to/photo.jpg

Example:
    python photo-analyzer.py ./site-photos/formwork-crew.jpg
    python photo-analyzer.py https://example.com/construction-photo.jpg

Output:
    - Trade type identification
    - Safety equipment assessment
    - Equipment and tools identified
    - Site conditions evaluation
    - Marketing insights (e.g., "steel fixing crew needed")
    - Social media caption suggestions
"""

import sys
import json
import logging
from pathlib import Path
import base64
import requests

try:
    import openai
except ImportError:
    print("OpenAI library not found. Installing...")
    import subprocess
    subprocess.check_call([sys.executable, "-m", "pip", "install", "openai"])
    import openai

# Setup logging
log_dir = Path("/home/ccuser/rateright-growth/rivet/memory")
log_dir.mkdir(exist_ok=True)
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler(log_dir / "content-engine.log"),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

def load_openai_key():
    """Load OpenAI API key from secrets file."""
    try:
        secrets_path = "/root/.clawdbot/secrets.json"
        with open(secrets_path, 'r') as f:
            secrets = json.load(f)
            return secrets.get("openai_api_key") or secrets.get("OPENAI_API_KEY")
    except Exception as e:
        logger.error(f"Failed to load OpenAI key: {e}")
        return None

def encode_image(image_path):
    """Encode image to base64 for API processing."""
    try:
        if image_path.startswith(('http://', 'https://')):
            # Download image from URL
            response = requests.get(image_path, timeout=30)
            response.raise_for_status()
            return base64.b64encode(response.content).decode('utf-8')
        else:
            # Read local image file
            with open(image_path, "rb") as image_file:
                return base64.b64encode(image_file.read()).decode('utf-8')
    except Exception as e:
        logger.error(f"Failed to encode image: {e}")
        return None

def analyze_construction_photo(image_path):
    """Analyze construction photo using OpenAI Vision API."""

    api_key = load_openai_key()
    if not api_key:
        logger.error("OpenAI API key not found")
        return None

    client = openai.OpenAI(api_key=api_key)

    # Encode image
    base64_image = encode_image(image_path)
    if not base64_image:
        return None

    system_prompt = """You are a construction industry expert analyzing site photos for RateRight,
    an Australian construction hiring marketplace. Provide detailed analysis for marketing purposes.

    Focus on identifying:
    1. Trade types and specializations visible
    2. Safety equipment and compliance
    3. Equipment, tools, and machinery
    4. Site conditions and project phase
    5. Workforce size and composition
    6. Quality standards and workmanship level

    Generate marketable insights about hiring needs and workforce requirements."""

    user_prompt = """Analyze this construction site photo and provide:

1. TRADE IDENTIFICATION
   - Primary trade(s) visible
   - Specialization level (apprentice, journeyman, expert)
   - Number of workers visible

2. SAFETY ASSESSMENT
   - PPE compliance (hard hats, hi-vis, steel caps, etc.)
   - Safety equipment present
   - Any safety concerns visible

3. EQUIPMENT & TOOLS
   - Major equipment visible
   - Hand tools and specialized gear
   - Equipment quality/condition

4. SITE CONDITIONS
   - Project type (residential, commercial, infrastructure)
   - Construction phase
   - Site organization and cleanliness
   - Weather conditions if apparent

5. MARKETABLE INSIGHTS
   - Hiring needs this photo suggests
   - Skill gaps or shortages evident
   - Equipment/supply needs
   - Project timeline implications

6. SOCIAL MEDIA CAPTIONS
   - 3 LinkedIn caption suggestions
   - 2 Instagram caption suggestions
   - 1 TikTok caption idea
   - Include relevant hashtags and emojis where appropriate

Format response with clear sections and bullet points. Be specific and actionable for marketing purposes."""

    try:
        response = client.chat.completions.create(
            model="gpt-4-vision-preview",
            messages=[
                {"role": "system", "content": system_prompt},
                {
                    "role": "user",
                    "content": [
                        {"type": "text", "text": user_prompt},
                        {
                            "type": "image_url",
                            "image_url": {
                                "url": f"data:image/jpeg;base64,{base64_image}"
                            }
                        }
                    ]
                }
            ],
            max_tokens=1500
        )

        content = response.choices[0].message.content
        logger.info(f"Successfully analyzed construction photo: {image_path}")
        return content

    except Exception as e:
        logger.error(f"OpenAI Vision API error: {e}")
        return None

def format_output(content, image_path):
    """Format the analysis for display."""
    from datetime import datetime

    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    output = f"""# RateRight Construction Photo Analyzer
Analyzed on: {timestamp}
Image: {image_path}

{'='*60}
{content}
{'='*60}

"""
    return output

def main():
    """Main function to handle command line execution."""

    if len(sys.argv) < 2:
        print("Usage: python photo-analyzer.py /path/to/photo.jpg")
        print("Example: python photo-analyzer.py ./site-photos/formwork-crew.jpg")
        print("         python photo-analyzer.py https://example.com/photo.jpg")
        sys.exit(1)

    image_path = sys.argv[1]
    logger.info(f"Starting photo analysis for: {image_path}")

    # Validate image path
    if not image_path.startswith(('http://', 'https://')):
        if not Path(image_path).exists():
            print(f"Error: Image file not found: {image_path}")
            logger.error(f"Image file not found: {image_path}")
            sys.exit(1)

    # Analyze photo
    content = analyze_construction_photo(image_path)

    if content:
        formatted_output = format_output(content, image_path)
        print(formatted_output)
        logger.info("Photo analysis completed successfully")
    else:
        print("Failed to analyze photo. Check logs for details.")
        logger.error("Photo analysis failed")
        sys.exit(1)

if __name__ == "__main__":
    main()


content:
     1→#!/usr/bin/env python3
     2→"""
     3→RateRight Blog Writer
     4→
     5→Generate SEO-optimized blog posts for RateRight website about construction hiring topics.
     6→
     7→Usage:
     8→    python blog-writer.py "topic/keyword"
     9→
    10→Example:
    11→    python blog-writer.py "how to hire steel fixers in Sydney"
    12→    python blog-writer.py "construction labour hire best practices"
    13→
    14→Output:
    15→    - 800-1200 word blog post in markdown format
    16→    - SEO-optimized with construction hiring keywords
    17→    - Professional but accessible tone
    18→    - Includes research from web sources
    19→    - Ready for website publication
    20→"""
    21→
    22→import sys
    23→import json
    24→import logging
    25→from datetime import datetime
    26→from pathlib import Path
    27→
    28→try:
    29→    import openai
    30→except ImportError:
    31→    print("OpenAI library not found. Installing...")
    32→    import subprocess
    33→    subprocess.check_call([sys.executable, "-m", "pip", "install", "openai"])
    34→    import openai
    35→
    36→# Setup logging
    37→log_dir = Path("/home/ccuser/rateright-growth/rivet/memory")
    38→log_dir.mkdir(exist_ok=True)
    39→logging.basicConfig(
    40→    level=logging.INFO,
    41→    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    42→    handlers=[
    43→        logging.FileHandler(log_dir / "content-engine.log"),
    44→        logging.StreamHandler()
    45→    ]
    46→)
    47→logger = logging.getLogger(__name__)
    48→
    49→def load_openai_key():
    50→    """Load OpenAI API key from secrets file."""
    51→    try:
    52→        secrets_path = "/root/.clawdbot/secrets.json"
    53→        with open(secrets_path, 'r') as f:
    54→            secrets = json.load(f)
    55→            return secrets.get("openai_api_key") or secrets.get("OPENAI_API_KEY")
    56→    except Exception as e:
    57→        logger.error(f"Failed to load OpenAI key: {e}")
    58→        return None
    59→
    60→def research_topic(topic):
    61→    """Research topic using web search for current information."""
    62→    try:
    63→        # Use Brave search to gather current information
    64→        search_query = f"construction hiring {topic} Australia Sydney 2024"
    65→
    66→        # Note: This would use the web_search tool, but since we're in a script,
    67→        # we'll simulate the research with the topic itself
    68→        logger.info(f"Researching topic: {topic}")
    69→
    70→        # Key areas to research based on the topic
    71→        research_areas = {
    72→            "hiring_challenges": "Current construction labour shortages in Sydney",
    73→            "market_trends": "Construction industry workforce trends Australia 2024",
    74→            "best_practices": "Construction hiring best practices and compliance",
    75→            "local_insights": "Sydney construction market conditions and labour demand"
    76→        }
    77→
    78→        return f"""Research findings for: {topic}
    79→
    80→Current Market Context (2024):
    81→- Sydney construction industry facing significant labour shortages
    82→- High demand for skilled trades: steel fixers, formworkers, concreters
    83→- Increased focus on safety compliance and certification requirements
    84→- Growing trend toward specialized labour hire services
    85→- Project delays due to workforce availability issues
    86→
    87→Key Industry Insights:
    88→- Construction accounts for ~9% of Australian GDP
    89→- Sydney major infrastructure projects driving demand
    90→- Skills shortage particularly acute in specialized trades
    91→- Contractors seeking reliable, compliant labour hire solutions
    92→- Emphasis on safety record and certification verification"""
    93→
    94→    except Exception as e:
    95→        logger.error(f"Research error: {e}")
    96→        return f"Topic: {topic} - Research data unavailable"
    97→
    98→def generate_blog_post(topic, research_data):
    99→    """Generate blog post using OpenAI API."""
   100→
   101→    api_key = load_openai_key()
   102→    if not api_key:
   103→        logger.error("OpenAI API key not found")
   104→        return None
   105→
   106→    client = openai.OpenAI(api_key=api_key)
   107→
   108→    system_prompt = """You are a content marketing expert for RateRight, a construction hiring marketplace in Australia.
   109→    Write SEO-optimized blog posts that rank well in search engines and provide value to contractors and construction professionals.
   110→
   111→    Target Audience: Sydney-based contractors, project managers, construction business owners
   112→    Tone: Professional but accessible, knowledgeable but not overly technical
   113→    Style: Educational, actionable, data-driven where possible
   114→
   115→    SEO Requirements:
   116→    - Target primary keyword in title, first paragraph, and naturally throughout
   117→    - Include related keywords and long-tail variations
   118→    - Use header tags (H1, H2, H3) appropriately
   119→    - Write compelling meta description
   120→    - Include internal linking opportunities
   121→    - Optimize for featured snippets when possible
   122→
   123→    Content Structure:
   124→    - Compelling headline with primary keyword
   125→    - Engaging introduction that hooks the reader
   126→    - Clear sections with descriptive headers
   127→    - Actionable takeaways and tips
   128→    - Strong conclusion with call-to-action
   129→    - Include statistics and industry insights
   130→
   131→    Construction Keywords to Include:
   132→    - construction hiring, labour hire Sydney, steel fixers, formworkers
   133→    - concreters, scaffolders, carpenters, construction jobs
   134→    - Sydney construction, Australian construction industry"""
   135→
   136→    user_prompt = f"""Write a comprehensive blog post about: {topic}
   137→
   138→Research Data:
   139→{research_data}
   140→
   141→Requirements:
   142→- 800-1200 words in markdown format
   143→- Target primary keyword: {topic}
   144→- Include relevant construction industry statistics
   145→- Provide actionable advice for contractors
   146→- Address common pain points in construction hiring
   147→- Include RateRight as solution where relevant (but not overly promotional)
   148→- Use Australian spelling and terminology
   149→- Include compelling meta description
   150→- Structure with clear H2 and H3 headers
   151→- End with strong call-to-action
   152→
   153→Format the entire post in markdown with:
   154→- SEO-optimized title
   155→- Meta description
   156→- Table of contents (if appropriate)
   157→- Clear header hierarchy
   158→- Bullet points and numbered lists where helpful
   159→- Bold text for emphasis
   160→- Internal linking suggestions [in brackets]"""
   161→
   162→    try:
   163→        response = client.chat.completions.create(
   164→            model="gpt-4",
   165→            messages=[
   166→                {"role": "system", "content": system_prompt},
   167→                {"role": "user", "content": user_prompt}
   168→            ],
   169→            temperature=0.7,
   170→            max_tokens=3000
   171→        )
   172→
   173→        content = response.choices[0].message.content
   174→        logger.info(f"Successfully generated blog post for topic: {topic}")
   175→        return content
   176→
   177→    except Exception as e:
   178→        logger.error(f"OpenAI API error: {e}")
   179→        return None
   180→
   181→def format_blog_output(content, topic):
   182→    """Format the blog post for display."""
   183→    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
   184→
   185→    output = f"""# RateRight Blog Writer
   186→Generated on: {timestamp}
   187→Topic: {topic}
   188→Word Count: ~{len(content.split())} words
   189→
   190→{'='*60}
   191→{content}
   192→{'='*60}
   193→
   194→"""
   195→    return output
   196→
   197→def main():
   198→    """Main function to handle command line execution."""
   199→
   200→    if len(sys.argv) < 2:
   201→        print("Usage: python blog-writer.py 'topic/keyword'")
   202→        print("Example: python blog-writer.py 'how to hire steel fixers in Sydney'")
   203→        print("         python blog-writer.py 'construction labour hire best practices'")
   204→        sys.exit(1)
   205→
   206→    topic = " ".join(sys.argv[1:])
   207→    logger.info(f"Starting blog post generation for topic: {topic}")
   208→
   209→    # Research topic
   210→    research_data = research_topic(topic)
   211→    logger.info("Research phase completed")
   212→
   213→    # Generate blog post
   214→    content = generate_blog_post(topic, research_data)
   215→
   216→    if content:
   217→        formatted_output = format_blog_output(content, topic)
   218→        print(formatted_output)
   219→        logger.info("Blog post generated successfully")
   220→
   221→        # Save to file option
   222→        save_option = input("Save to file? (y/n): ").lower().strip()
   223→        if save_option == 'y':
   224→            filename = f"blog-{topic.replace(' ', '-').lower()[:50]}.md"
   225→            output_path = Path("/home/ccuser/rateright-growth/rivet/scripts") / filename
   226→
   227→            with open(output_path, 'w', encoding='utf-8') as f:
   228→                f.write(content)
   229→
   230→            print(f"Blog post saved to: {output_path}")
   231→            logger.info(f"Blog post saved to: {output_path}")
   232→
   233→    else:
   234→        print("Failed to generate blog post. Check logs for details.")
   235→        logger.error("Blog post generation failed")
   236→        sys.exit(1)
   237→
   238→if __name__ == "__main__":
   239→    main()

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>

content:
#!/usr/bin/env python3
"""
RateRight Blog Writer

Generate SEO-optimized blog posts for RateRight website about construction hiring topics.

Usage:
    python blog-writer.py "topic/keyword"

Example:
    python blog-writer.py "how to hire steel fixers in Sydney"
    python blog-writer.py "construction labour hire best practices"

Output:
    - 800-1200 word blog post in markdown format
    - SEO-optimized with construction hiring keywords
    - Professional but accessible tone
    - Includes research from web sources
    - Ready for website publication
"""

import sys
import json
import logging
from datetime import datetime
from pathlib import Path

try:
    import openai
except ImportError:
    print("OpenAI library not found. Installing...")
    import subprocess
    subprocess.check_call([sys.executable, "-m", "pip", "install", "openai"])
    import openai

# Setup logging
log_dir = Path("/home/ccuser/rateright-growth/rivet/memory")
log_dir.mkdir(exist_ok=True)
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler(log_dir / "content-engine.log"),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

def load_openai_key():
    """Load OpenAI API key from secrets file."""
    try:
        secrets_path = "/root/.clawdbot/secrets.json"
        with open(secrets_path, 'r') as f:
            secrets = json.load(f)
            return secrets.get("openai_api_key") or secrets.get("OPENAI_API_KEY")
    except Exception as e:
        logger.error(f"Failed to load OpenAI key: {e}")
        return None

def research_topic(topic):
    """Research topic using web search for current information."""
    try:
        # Use Brave search to gather current information
        search_query = f"construction hiring {topic} Australia Sydney 2024"

        # Note: This would use the web_search tool, but since we're in a script,
        # we'll simulate the research with the topic itself
        logger.info(f"Researching topic: {topic}")

        # Key areas to research based on the topic
        research_areas = {
            "hiring_challenges": "Current construction labour shortages in Sydney",
            "market_trends": "Construction industry workforce trends Australia 2024",
            "best_practices": "Construction hiring best practices and compliance",
            "local_insights": "Sydney construction market conditions and labour demand"
        }

        return f"""Research findings for: {topic}

Current Market Context (2024):
- Sydney construction industry facing significant labour shortages
- High demand for skilled trades: steel fixers, formworkers, concreters
- Increased focus on safety compliance and certification requirements
- Growing trend toward specialized labour hire services
- Project delays due to workforce availability issues

Key Industry Insights:
- Construction accounts for ~9% of Australian GDP
- Sydney major infrastructure projects driving demand
- Skills shortage particularly acute in specialized trades
- Contractors seeking reliable, compliant labour hire solutions
- Emphasis on safety record and certification verification"""

    except Exception as e:
        logger.error(f"Research error: {e}")
        return f"Topic: {topic} - Research data unavailable"

def generate_blog_post(topic, research_data):
    """Generate blog post using OpenAI API."""

    api_key = load_openai_key()
    if not api_key:
        logger.error("OpenAI API key not found")
        return None

    client = openai.OpenAI(api_key=api_key)

    system_prompt = """You are a content marketing expert for RateRight, a construction hiring marketplace in Australia.
    Write SEO-optimized blog posts that rank well in search engines and provide value to contractors and construction professionals.

    Target Audience: Sydney-based contractors, project managers, construction business owners
    Tone: Professional but accessible, knowledgeable but not overly technical
    Style: Educational, actionable, data-driven where possible

    SEO Requirements:
    - Target primary keyword in title, first paragraph, and naturally throughout
    - Include related keywords and long-tail variations
    - Use header tags (H1, H2, H3) appropriately
    - Write compelling meta description
    - Include internal linking opportunities
    - Optimize for featured snippets when possible

    Content Structure:
    - Compelling headline with primary keyword
    - Engaging introduction that hooks the reader
    - Clear sections with descriptive headers
    - Actionable takeaways and tips
    - Strong conclusion with call-to-action
    - Include statistics and industry insights

    Construction Keywords to Include:
    - construction hiring, labour hire Sydney, steel fixers, formworkers
    - concreters, scaffolders, carpenters, construction jobs
    - Sydney construction, Australian construction industry"""

    user_prompt = f"""Write a comprehensive blog post about: {topic}

Research Data:
{research_data}

Requirements:
- 800-1200 words in markdown format
- Target primary keyword: {topic}
- Include relevant construction industry statistics
- Provide actionable advice for contractors
- Address common pain points in construction hiring
- Include RateRight as solution where relevant (but not overly promotional)
- Use Australian spelling and terminology
- Include compelling meta description
- Structure with clear H2 and H3 headers
- End with strong call-to-action

Format the entire post in markdown with:
- SEO-optimized title
- Meta description
- Table of contents (if appropriate)
- Clear header hierarchy
- Bullet points and numbered lists where helpful
- Bold text for emphasis
- Internal linking suggestions [in brackets]"""

    try:
        response = client.chat.completions.create(
            model="gpt-4",
            messages=[
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": user_prompt}
            ],
            temperature=0.7,
            max_tokens=3000
        )

        content = response.choices[0].message.content
        logger.info(f"Successfully generated blog post for topic: {topic}")
        return content

    except Exception as e:
        logger.error(f"OpenAI API error: {e}")
        return None

def format_blog_output(content, topic):
    """Format the blog post for display."""
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    output = f"""# RateRight Blog Writer
Generated on: {timestamp}
Topic: {topic}
Word Count: ~{len(content.split())} words

{'='*60}
{content}
{'='*60}

"""
    return output

def main():
    """Main function to handle command line execution."""

    if len(sys.argv) < 2:
        print("Usage: python blog-writer.py 'topic/keyword'")
        print("Example: python blog-writer.py 'how to hire steel fixers in Sydney'")
        print("         python blog-writer.py 'construction labour hire best practices'")
        sys.exit(1)

    topic = " ".join(sys.argv[1:])
    logger.info(f"Starting blog post generation for topic: {topic}")

    # Research topic
    research_data = research_topic(topic)
    logger.info("Research phase completed")

    # Generate blog post
    content = generate_blog_post(topic, research_data)

    if content:
        formatted_output = format_blog_output(content, topic)
        print(formatted_output)
        logger.info("Blog post generated successfully")

        # Save to file option
        save_option = input("Save to file? (y/n): ").lower().strip()
        if save_option == 'y':
            filename = f"blog-{topic.replace(' ', '-').lower()[:50]}.md"
            output_path = Path("/home/ccuser/rateright-growth/rivet/scripts") / filename

            with open(output_path, 'w', encoding='utf-8') as f:
                f.write(content)

            print(f"Blog post saved to: {output_path}")
            logger.info(f"Blog post saved to: {output_path}")

    else:
        print("Failed to generate blog post. Check logs for details.")
        logger.error("Blog post generation failed")
        sys.exit(1)

if __name__ == "__main__":
    main()


text:
---

## Code Review: Content Engine Scripts

---

### 1. `linkedin-generator.py`

**Issues**

| Severity | Issue |
|----------|-------|
| **Critical** | **Auto-installs packages at import time** (lines 27-33). Runs `pip install` as a side effect of importing. In production this is a supply chain risk — a compromised PyPI package could be installed silently. Dependencies should be declared in `requirements.txt`. |
| **Critical** | **Hardcoded secrets path** (line 51). `/root/.clawdbot/secrets.json` ties the script to a single machine and user. Fails silently elsewhere. Should use env vars or a configurable path. |
| **Medium** | **Bare `except Exception`** throughout. Catches everything including `KeyboardInterrupt` in Python <3.11 edge cases. More targeted exceptions (`FileNotFoundError`, `openai.APIError`) would improve debuggability. |
| **Medium** | **No input sanitization** on `topic` (line 83). User input is injected directly into the prompt. While not a traditional injection, prompt injection via CLI args is possible. |
| **Minor** | **No output persistence** — generated content is only printed, never saved. Easy to lose work. |
| **Minor** | **No retry logic** for API calls. A single transient failure kills the run. |

**Quality Score: 5/10** — Functional prototype, not production-ready.

---

### 2. `photo-analyzer.py`

**Issues**

| Severity | Issue |
|----------|-------|
| **Critical** | **SSRF via URL input** (line 68). `requests.get(image_path)` fetches any user-supplied URL with no allowlist/validation. An attacker could target internal services (`http://169.254.169.254/` for cloud metadata, `http://localhost:*`). |
| **Critical** | **Auto-install at import** (lines 31-36). Same supply chain risk as above. |
| **Critical** | **Hardcoded secrets path** (line 55). Same issue. |
| **Critical** | **Deprecated model** (line 147). `gpt-4-vision-preview` is deprecated. Should use `gpt-4o` or `gpt-4-turbo`. |
| **Medium** | **No file size limit** on image reads (line 73). `open(image_path, "rb").read()` on a multi-GB file will OOM the process. |
| **Medium** | **No file type validation**. Accepts any file extension — PDFs, executables, etc. will be base64-encoded and sent to the API, wasting tokens. |
| **Medium** | **`requests` imported but not declared** as a dependency. Will fail if not installed. |
| **Minor** | **`datetime` imported inside function** (line 175) instead of at module level — inconsistent with the rest of the script. |

**Quality Score: 3/10** — Has real security vulnerabilities (SSRF). Not safe for production.

---

### 3. `blog-writer.py`

**Issues**

| Severity | Issue |
|----------|-------|
| **Critical** | **Auto-install at import** (lines 29-33). Same issue across all three scripts. |
| **Critical** | **Hardcoded secrets path** (line 53). Same issue. |
| **Critical** | **`research_topic()` is a fake** (lines 60-96). The function claims to "research topic using web search" but returns hardcoded static text every time. The "research" is a lie — the blog posts are generated from canned data, not real research. The hardcoded year "2024" will also be stale. |
| **Medium** | **Interactive `input()` call** (line 222) in what could be a scripted/automated pipeline. Blocks indefinitely if run non-interactively (cron, CI, piped input). Should use a CLI flag (`--save`) instead. |
| **Medium** | **Unsanitized filename construction** (line 224). `topic.replace(' ', '-').lower()[:50]` doesn't strip path traversal characters. A topic like `../../etc/cron.d/evil` would write outside the intended directory. |
| **Medium** | **No input sanitization** on topic passed to prompt. |
| **Minor** | **Word count is approximate** (line 188). `len(content.split())` is displayed as exact but prefixed with `~`, which is fine, but misleading in a "production" output. |

**Quality Score: 3/10** — Fake research function and path traversal make this unreliable and unsafe.

---

### Cross-Cutting Recommendations

1. **Eliminate auto-install pattern** in all three scripts. Use a `requirements.txt` and install deps at deploy time.
2. **Use environment variables** (`OPENAI_API_KEY`) instead of hardcoded secrets paths. The OpenAI client reads `OPENAI_API_KEY` from env by default — no custom loading needed.
3. **Add input validation** — sanitize CLI args, validate file types/sizes, restrict URLs to public hosts.
4. **Extract shared code** — `load_openai_key()`, logging setup, and the auto-install block are copy-pasted identically across all three files. Should be a shared module.
5. **Add retry logic** with exponential backoff for API calls (use `tenacity` or `backoff` library).
6. **Fix the fake research function** in `blog-writer.py` — either implement real web search or remove the misleading wrapper.
7. **Fix the SSRF** in `photo-analyzer.py` — validate URLs against an allowlist, block private/internal IPs.
8. **Update deprecated model** `gpt-4-vision-preview` → `gpt-4o`.

---

### Summary

| Script | Score | Status |
|--------|-------|--------|
| `linkedin-generator.py` | **5/10** | Functional prototype, needs hardening |
| `photo-analyzer.py` | **3/10** | SSRF vulnerability, deprecated model, not production-safe |
| `blog-writer.py` | **3/10** | Fake research, path traversal risk, blocking `input()` |

All three are in "quick prototype" state — fine for local testing, not ready for production or automation pipelines.