use cursor to optomize files

This commit is contained in:
2025-05-03 16:23:06 +10:00
parent 427a5cb919
commit 2ca39915e0
5 changed files with 1411 additions and 1634 deletions
+116 -31
View File
@@ -2,14 +2,71 @@
# Constants shared across all automator scripts
from dotenv import load_dotenv
import os
from typing import Dict, List, Optional, TypedDict, Union
from pathlib import Path
import logging
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('foodie_automator.log'),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
# Load environment variables
load_dotenv()
# API Keys
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
PIXABAY_API_KEY = os.getenv("PIXABAY_API_KEY")
FLICKR_API_KEY = os.getenv("FLICKR_API_KEY")
FLICKR_API_SECRET = os.getenv("FLICKR_API_SECRET")
AUTHORS = [
# Validate required API keys
def validate_api_keys() -> None:
"""Validate that all required API keys are present."""
required_keys = {
"OPENAI_API_KEY": OPENAI_API_KEY,
"PIXABAY_API_KEY": PIXABAY_API_KEY,
"FLICKR_API_KEY": FLICKR_API_KEY,
"FLICKR_API_SECRET": FLICKR_API_SECRET
}
missing_keys = [key for key, value in required_keys.items() if not value]
if missing_keys:
logger.error(f"Missing required API keys: {', '.join(missing_keys)}")
raise ValueError(f"Missing required API keys: {', '.join(missing_keys)}")
# Type definitions
class AuthorConfig(TypedDict):
url: str
username: str
password: str
persona: str
bio: str
dob: str
class XCredentials(TypedDict):
username: str
x_username: str
api_key: str
api_secret: str
access_token: str
access_token_secret: str
client_secret: str
class PersonaConfig(TypedDict):
description: str
tone: str
article_prompt: str
x_prompt: str
# Author configurations
AUTHORS: List[AuthorConfig] = [
{
"url": "https://insiderfoodie.com",
"username": "owenjohnson",
@@ -31,7 +88,7 @@ AUTHORS = [
"username": "aishapatel",
"password": os.getenv("AISHAPATEL_PASSWORD"),
"persona": "Trend Scout",
"bio": "I scout global food trends, obsessed with whats emerging. My sharp predictions map the industrys path—always one step ahead.",
"bio": "I scout global food trends, obsessed with what's emerging. My sharp predictions map the industry's path—always one step ahead.",
"dob": "1999-03-15"
},
{
@@ -47,7 +104,7 @@ AUTHORS = [
"username": "keishareid",
"password": os.getenv("KEISHAREID_PASSWORD"),
"persona": "African-American Soul Food Sage",
"bio": "I bring soul foods legacy to life, blending history with modern vibes. My stories celebrate flavor and resilience—dishing out culture with every bite.",
"bio": "I bring soul food's legacy to life, blending history with modern vibes. My stories celebrate flavor and resilience—dishing out culture with every bite.",
"dob": "1994-06-10"
},
{
@@ -60,7 +117,8 @@ AUTHORS = [
}
]
X_API_CREDENTIALS = [
# X (Twitter) API credentials
X_API_CREDENTIALS: List[XCredentials] = [
{
"username": "owenjohnson",
"x_username": "@insiderfoodieowen",
@@ -117,12 +175,13 @@ X_API_CREDENTIALS = [
}
]
PERSONA_CONFIGS = {
# Persona configurations
PERSONA_CONFIGS: Dict[str, PersonaConfig] = {
"Visionary Editor": {
"description": "a commanding food editor with a borderless view",
"tone": "a polished and insightful tone, like 'This redefines culinary excellence.'",
"article_prompt": (
"Youre {description}. Summarize this article in {tone}. "
"You're {description}. Summarize this article in {tone}. "
"Explore a wide range of food-related topics, skip recipes. Generate exactly {num_paragraphs} paragraphs, 60-80 words each, full thoughts, with a single \n break. "
"Write naturally in a refined yet engaging style, with a slight Upworthy/Buzzfeed flair, without mentioning the source name or URL directly in the text. "
"Add a bold take and end with a thought-provoking question like Neil Patel would do to boost engagement! Do not include emojis in the summary."
@@ -139,7 +198,7 @@ PERSONA_CONFIGS = {
"description": "a seasoned foodie reviewer with a sharp eye",
"tone": "a professional yet engaging tone, like 'This dish is a revelation.'",
"article_prompt": (
"Youre {description}. Summarize this article in {tone}. "
"You're {description}. Summarize this article in {tone}. "
"Explore a wide range of food-related topics, skip recipes. Generate exactly {num_paragraphs} paragraphs, 60-80 words each, full thoughts, with a single \n break. "
"Write naturally in a refined yet engaging style, with a slight Upworthy/Buzzfeed flair, without mentioning the source name or URL directly in the text. "
"Add a subtle opinion and end with a thought-provoking question like Neil Patel would do to boost engagement! Do not include emojis in the summary."
@@ -154,12 +213,12 @@ PERSONA_CONFIGS = {
},
"Trend Scout": {
"description": "a forward-thinking editor obsessed with trends",
"tone": "an insightful and forward-looking tone, like 'This sets the stage for whats next.'",
"tone": "an insightful and forward-looking tone, like 'This sets the stage for what's next.'",
"article_prompt": (
"Youre {description}. Summarize this article in {tone}. "
"You're {description}. Summarize this article in {tone}. "
"Explore a wide range of food-related topics, skip recipes. Generate exactly {num_paragraphs} paragraphs, 60-80 words each, full thoughts, with a single \n break. "
"Write naturally in a refined yet engaging style, with a slight Upworthy/Buzzfeed flair, without mentioning the source name or URL directly in the text. "
"Predict whats next and end with a thought-provoking question like Neil Patel would do to boost engagement! Do not include emojis in the summary."
"Predict what's next and end with a thought-provoking question like Neil Patel would do to boost engagement! Do not include emojis in the summary."
),
"x_prompt": (
"Craft a tweet as {description}. Keep it under 280 characters, using {tone}. "
@@ -173,7 +232,7 @@ PERSONA_CONFIGS = {
"description": "a cultured food writer who loves storytelling",
"tone": "a warm and thoughtful tone, like 'This evokes a sense of tradition.'",
"article_prompt": (
"Youre {description}. Summarize this article in {tone}. "
"You're {description}. Summarize this article in {tone}. "
"Explore a wide range of food-related topics, skip recipes. Generate exactly {num_paragraphs} paragraphs, 60-80 words each, full thoughts, with a single \n break. "
"Write naturally in a refined yet engaging style, with a slight Upworthy/Buzzfeed flair, without mentioning the source name or URL directly in the text. "
"Add a thoughtful observation and end with a thought-provoking question like Neil Patel would do to boost engagement! Do not include emojis in the summary."
@@ -190,7 +249,7 @@ PERSONA_CONFIGS = {
"description": "a vibrant storyteller rooted in African-American culinary heritage",
"tone": "a heartfelt and authentic tone, like 'This captures the essence of heritage.'",
"article_prompt": (
"Youre {description}. Summarize this article in {tone}. "
"You're {description}. Summarize this article in {tone}. "
"Explore a wide range of food-related topics, skip recipes. Generate exactly {num_paragraphs} paragraphs, 60-80 words each, full thoughts, with a single \n break. "
"Write naturally in a refined yet engaging style, with a slight Upworthy/Buzzfeed flair, without mentioning the source name or URL directly in the text. "
"Add a heritage twist and end with a thought-provoking question like Neil Patel would do to boost engagement! Do not include emojis in the summary."
@@ -207,7 +266,7 @@ PERSONA_CONFIGS = {
"description": "an adventurous explorer of global street food",
"tone": "a bold and adventurous tone, like 'This takes you on a global journey.'",
"article_prompt": (
"Youre {description}. Summarize this article in {tone}. "
"You're {description}. Summarize this article in {tone}. "
"Explore a wide range of food-related topics, skip recipes. Generate exactly {num_paragraphs} paragraphs, 60-80 words each, full thoughts, with a single \n break. "
"Write naturally in a refined yet engaging style, with a slight Upworthy/Buzzfeed flair, without mentioning the source name or URL directly in the text. "
"Drop a street-level insight and end with a thought-provoking question like Neil Patel would do to boost engagement! Do not include emojis in the summary."
@@ -223,25 +282,30 @@ PERSONA_CONFIGS = {
}
# File paths
POSTED_RSS_TITLES_FILE = '/home/shane/foodie_automator/posted_rss_titles.json'
POSTED_GOOGLE_TITLES_FILE = '/home/shane/foodie_automator/posted_google_titles.json'
POSTED_REDDIT_TITLES_FILE = '/home/shane/foodie_automator/posted_reddit_titles.json'
USED_IMAGES_FILE = '/home/shane/foodie_automator/used_images.json'
AUTHOR_BACKGROUNDS_FILE = '/home/shane/foodie_automator/author_backgrounds.json'
X_POST_COUNTS_FILE = '/home/shane/foodie_automator/x_post_counts.json'
RECENT_POSTS_FILE = '/home/shane/foodie_automator/recent_posts.json'
BASE_DIR = Path("/home/shane/foodie_automator")
FILE_PATHS = {
"posted_rss_titles": BASE_DIR / "posted_rss_titles.json",
"posted_google_titles": BASE_DIR / "posted_google_titles.json",
"posted_reddit_titles": BASE_DIR / "posted_reddit_titles.json",
"used_images": BASE_DIR / "used_images.json",
"author_backgrounds": BASE_DIR / "author_backgrounds.json",
"x_post_counts": BASE_DIR / "x_post_counts.json",
"recent_posts": BASE_DIR / "recent_posts.json"
}
# Expiration periods
EXPIRATION_DAYS = 3
IMAGE_EXPIRATION_DAYS = 7
RSS_FEEDS = [
# RSS feed configurations
RSS_FEEDS: List[str] = [
"https://www.eater.com/rss/full.xml",
"https://www.nrn.com/rss.xml",
"https://rss.nytimes.com/services/xml/rss/nyt/DiningandWine.xml",
"https://www.theguardian.com/food/rss"
]
RSS_FEED_NAMES = {
RSS_FEED_NAMES: Dict[str, tuple[str, str]] = {
"https://www.eater.com/rss/full.xml": ("Eater", "https://www.eater.com/"),
"https://www.nrn.com/rss.xml": ("Nation's Restaurant News", "https://www.nrn.com/"),
"https://rss.nytimes.com/services/xml/rss/nyt/DiningandWine.xml": ("The New York Times", "https://www.nytimes.com/section/food"),
@@ -276,12 +340,33 @@ FAST_FOOD_KEYWORDS = [
SUMMARY_MODEL = "gpt-4o" # or "gpt-4.1-mini" for testing
LIGHT_TASK_MODEL = "gpt-4o-mini"
def get_clean_source_name(source_name):
"""
Retrieve a clean source name from RSS_FEED_NAMES if source_name matches a feed URL,
otherwise return the original source_name as a fallback.
"""
for feed_url, (clean_name, _) in RSS_FEED_NAMES.items():
if feed_url == source_name:
return clean_name
return source_name
def get_clean_source_name(source_name: str) -> str:
"""Clean and standardize source names."""
try:
# Remove common prefixes and suffixes
clean_name = source_name.strip()
clean_name = clean_name.replace("The ", "").replace("the ", "")
clean_name = clean_name.replace("Food", "").replace("food", "")
clean_name = clean_name.replace("Dining", "").replace("dining", "")
clean_name = clean_name.replace("Restaurant", "").replace("restaurant", "")
# Remove any remaining whitespace
clean_name = " ".join(clean_name.split())
return clean_name if clean_name else source_name
except Exception as e:
logger.error(f"Error cleaning source name '{source_name}': {e}")
return source_name
# Validate configurations on import
validate_api_keys()
# Ensure all file paths exist
for path in FILE_PATHS.values():
path.parent.mkdir(parents=True, exist_ok=True)
if not path.exists():
path.touch()
logger.info(f"Created missing file: {path}")
# Log successful configuration
logger.info("Configuration loaded successfully")