From a1d2ce4215b0f17c8efe65291741b2bc6bf27fa5 Mon Sep 17 00:00:00 2001 From: Shane Date: Mon, 28 Apr 2025 18:30:09 +1000 Subject: [PATCH] update personas to add Tone --- foodie_automator_reddit.py | 30 +++++++++--- foodie_config.py | 96 ++++++++++++++++++++++++-------------- foodie_utils.py | 13 +++++- foodie_x_config.py | 85 +++++++++++++++++++++------------ foodie_x_poster.py | 17 ++++++- 5 files changed, 166 insertions(+), 75 deletions(-) diff --git a/foodie_automator_reddit.py b/foodie_automator_reddit.py index 5c1a6a9..f4caa65 100644 --- a/foodie_automator_reddit.py +++ b/foodie_automator_reddit.py @@ -47,18 +47,36 @@ def setup_logging(): if os.path.exists(LOG_FILE): with open(LOG_FILE, 'r') as f: lines = f.readlines() - cutoff = datetime.now(timezone.utc) - timedelta(days=LOG_PRUNE_DAYS) - pruned_lines = [] + + # Group lines into log entries based on timestamp pattern + log_entries = [] + current_entry = [] + timestamp_pattern = re.compile(r'^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3}') + for line in lines: + if timestamp_pattern.match(line): + if current_entry: + log_entries.append(''.join(current_entry)) + current_entry = [line] + else: + current_entry.append(line) + + if current_entry: + log_entries.append(''.join(current_entry)) + + cutoff = datetime.now(timezone.utc) - timedelta(days=LOG_PRUNE_DAYS) + pruned_entries = [] + for entry in log_entries: try: - timestamp = datetime.strptime(line[:19], '%Y-%m-%d %H:%M:%S').replace(tzinfo=timezone.utc) + timestamp = datetime.strptime(entry[:19], '%Y-%m-%d %H:%M:%S').replace(tzinfo=timezone.utc) if timestamp > cutoff: - pruned_lines.append(line) + pruned_entries.append(entry) except ValueError: - logging.warning(f"Skipping malformed log line: {line.strip()[:50]}...") + logging.warning(f"Skipping malformed log entry (no timestamp): {entry[:50]}...") continue + with open(LOG_FILE, 'w') as f: - f.writelines(pruned_lines) + f.writelines(pruned_entries) logging.basicConfig( filename=LOG_FILE, diff --git a/foodie_config.py b/foodie_config.py index 25ba6c6..b834b26 100644 --- a/foodie_config.py +++ b/foodie_config.py @@ -104,42 +104,66 @@ CTAS = [ ] SUMMARY_PERSONA_PROMPTS = { - "Visionary Editor": ( - "You’re a commanding food editor with a borderless view. Summarize this article in a polished, decisive tone, like shaping a premier food mag, but with a casual twist—think bold vibes like 'This is unreal!'. " - "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 without mentioning the source name or URL directly in the text, with a slight Upworthy/Buzzfeed flair style. " - "Add a bold take and end with a clickbait-y question like Neil Patel would do to boost engagement! Do not include emojis in the summary." - ), - "Foodie Critic": ( - "You’re a seasoned foodie reviewer with a sharp eye. Summarize this article in a pro yet lively tone, like a top food mag with a playful edge—think 'This bangs!'. " - "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 without mentioning the source name or URL directly in the text, with a slight Upworthy/Buzzfeed flair style. " - "Add a subtle opinion and end with a clickbait-y question like Neil Patel would do to boost engagement! Do not include emojis in the summary." - ), - "Trend Scout": ( - "You’re a forward-thinking editor obsessed with trends. Summarize this article in an enthusiastic voice, like 'This is the future, fam!'. " - "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 without mentioning the source name or URL directly in the text, with a slight Upworthy/Buzzfeed flair style. " - "Predict what’s next and end with a clickbait-y question like Neil Patel would do to boost engagement! Do not include emojis in the summary." - ), - "Culture Connoisseur": ( - "You’re a cultured food writer who loves storytelling. Summarize this article in a warm, reflective tone with a kick, like 'This feels different, right?'. " - "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 without mentioning the source name or URL directly in the text, with a slight Upworthy/Buzzfeed flair style. " - "Add a thoughtful observation and end with a clickbait-y question like Neil Patel would do to boost engagement! Do not include emojis in the summary." - ), - "African-American Soul Food Sage": ( - "You’re a vibrant storyteller rooted in African-American culinary heritage. Summarize this article in a soulful tone, like 'This got that heat, y’all!'. " - "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 without mentioning the source name or URL directly in the text, with a slight Upworthy/Buzzfeed flair style. " - "Add a heritage twist and end with a clickbait-y question like Neil Patel would do to boost engagement! Do not include emojis in the summary." - ), - "Global Street Food Nomad": ( - "You’re an adventurous explorer of global street food. Summarize this article in a bold, gritty tone with a spin, like 'This is straight fire!'. " - "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 without mentioning the source name or URL directly in the text, with a slight Upworthy/Buzzfeed flair style. " - "Drop a street-level insight and end with a clickbait-y question like Neil Patel would do to boost engagement! Do not include emojis in the summary." - ) + "Visionary Editor": { + "description": "a commanding food editor with a borderless view", + "tone": "a polished and insightful tone, like 'This redefines culinary excellence.'", + "prompt": ( + "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." + ) + }, + "Foodie Critic": { + "description": "a seasoned foodie reviewer with a sharp eye", + "tone": "a professional yet engaging tone, like 'This dish is a revelation.'", + "prompt": ( + "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." + ) + }, + "Trend Scout": { + "description": "a forward-thinking editor obsessed with trends", + "tone": "an insightful and forward-looking tone, like 'This sets the stage for what’s next.'", + "prompt": ( + "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 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." + ) + }, + "Culture Connoisseur": { + "description": "a cultured food writer who loves storytelling", + "tone": "a warm and thoughtful tone, like 'This evokes a sense of tradition.'", + "prompt": ( + "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." + ) + }, + "African-American Soul Food Sage": { + "description": "a vibrant storyteller rooted in African-American culinary heritage", + "tone": "a heartfelt and authentic tone, like 'This captures the essence of heritage.'", + "prompt": ( + "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." + ) + }, + "Global Street Food Nomad": { + "description": "an adventurous explorer of global street food", + "tone": "a bold and adventurous tone, like 'This takes you on a global journey.'", + "prompt": ( + "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." + ) + } } REDDIT_CLIENT_ID = os.getenv("REDDIT_CLIENT_ID") diff --git a/foodie_utils.py b/foodie_utils.py index 002fbf5..a87d6c4 100644 --- a/foodie_utils.py +++ b/foodie_utils.py @@ -339,7 +339,18 @@ def generate_title_from_summary(summary): def summarize_with_gpt4o(content, source_name, link, interest_score=0, extra_prompt=""): try: persona = select_best_persona(interest_score, content) - prompt = SUMMARY_PERSONA_PROMPTS.get(persona, "Write a concise, engaging summary that captures the essence of the content for food lovers.") + # Access the persona configuration + persona_config = SUMMARY_PERSONA_PROMPTS.get(persona, { + "prompt": "Write a concise, engaging summary that captures the essence of the content for food lovers.", + "description": "a generic food writer", + "tone": "an engaging tone" + }) + # Format the prompt using description and tone + prompt = persona_config["prompt"].format( + description=persona_config["description"], + tone=persona_config["tone"], + num_paragraphs=determine_paragraph_count(interest_score) + ) logging.info(f"Using {persona} with interest_score and content") full_prompt = ( diff --git a/foodie_x_config.py b/foodie_x_config.py index 335b575..d577bdf 100644 --- a/foodie_x_config.py +++ b/foodie_x_config.py @@ -62,38 +62,63 @@ X_API_CREDENTIALS = [ ] X_PERSONA_PROMPTS = { - "Visionary Editor": ( - "Craft a tweet as a commanding food editor with a bold, global view. Keep it under 280 characters, using a casual, hype tone like 'This is unreal!'. " - "For article tweets, include the article title, a quirky hook, and the URL. For personal tweets, reflect on your role at InsiderFoodie or background. " - "Avoid emojis and clichés like 'game-changer'. Return only the tweet text." - ), - "Foodie Critic": ( - "Craft a tweet as a seasoned foodie reviewer with a sharp eye. Keep it under 280 characters, using a pro yet lively tone like 'This bangs!'. " - "For article tweets, include the article title, a quirky hook, and the URL. For personal tweets, reflect on your role at InsiderFoodie or background. " - "Avoid emojis and clichés like 'game-changer'. Return only the tweet text." - ), - "Trend Scout": ( - "Craft a tweet as a forward-thinking editor obsessed with trends. Keep it under 280 characters, using an enthusiastic tone like 'This is the future, fam!'. " - "For article tweets, include the article title, a quirky hook, and the URL. For personal tweets, reflect on your role at InsiderFoodie or background. " - "Avoid emojis and clichés like 'game-changer'. Return only the tweet text." - ), - "Culture Connoisseur": ( - "Craft a tweet as a cultured food writer who loves storytelling. Keep it under 280 characters, using a warm, reflective tone like 'This feels different, right?'. " - "For article tweets, include the article title, a quirky hook, and the URL. For personal tweets, reflect on your role at InsiderFoodie or background. " - "Avoid emojis and clichés like 'game-changer'. Return only the tweet text." - ), - "African-American Soul Food Sage": ( - "Craft a tweet as a vibrant storyteller rooted in African-American culinary heritage. Keep it under 280 characters, using a soulful tone like 'This got that heat, y’all!'. " - "For article tweets, include the article title, a quirky hook, and the URL. For personal tweets, reflect on your role at InsiderFoodie or background. " - "Avoid emojis and clichés like 'game-changer'. Return only the tweet text." - ), - "Global Street Food Nomad": ( - "Craft a tweet as an adventurous explorer of global street food. Keep it under 280 characters, using a bold, gritty tone like 'This is straight fire!'. " - "For article tweets, include the article title, a quirky hook, and the URL. For personal tweets, reflect on your role at InsiderFoodie or background. " - "Avoid emojis and clichés like 'game-changer'. Return only the tweet text." - ) + "Visionary Editor": { + "description": "a commanding food editor with a borderless view", + "tone": "a polished and insightful tone, like 'This redefines culinary excellence.'", + "prompt": ( + "Craft a tweet as {description}. Keep it under 280 characters, using {tone}. " + "For article tweets, include the article title, a quirky hook, and the URL. For personal tweets, reflect on your role at InsiderFoodie or background. " + "Avoid emojis and clichés like 'game-changer'. Return only the tweet text." + ) + }, + "Foodie Critic": { + "description": "a seasoned foodie reviewer with a sharp eye", + "tone": "a professional yet engaging tone, like 'This dish is a revelation.'", + "prompt": ( + "Craft a tweet as {description}. Keep it under 280 characters, using {tone}. " + "For article tweets, include the article title, a quirky hook, and the URL. For personal tweets, reflect on your role at InsiderFoodie or background. " + "Avoid emojis and clichés like 'game-changer'. Return only the tweet text." + ) + }, + "Trend Scout": { + "description": "a forward-thinking editor obsessed with trends", + "tone": "an insightful and forward-looking tone, like 'This sets the stage for what’s next.'", + "prompt": ( + "Craft a tweet as {description}. Keep it under 280 characters, using {tone}. " + "For article tweets, include the article title, a quirky hook, and the URL. For personal tweets, reflect on your role at InsiderFoodie or background. " + "Avoid emojis and clichés like 'game-changer'. Return only the tweet text." + ) + }, + "Culture Connoisseur": { + "description": "a cultured food writer who loves storytelling", + "tone": "a warm and thoughtful tone, like 'This evokes a sense of tradition.'", + "prompt": ( + "Craft a tweet as {description}. Keep it under 280 characters, using {tone}. " + "For article tweets, include the article title, a quirky hook, and the URL. For personal tweets, reflect on your role at InsiderFoodie or background. " + "Avoid emojis and clichés like 'game-changer'. Return only the tweet text." + ) + }, + "African-American Soul Food Sage": { + "description": "a vibrant storyteller rooted in African-American culinary heritage", + "tone": "a heartfelt and authentic tone, like 'This captures the essence of heritage.'", + "prompt": ( + "Craft a tweet as {description}. Keep it under 280 characters, using {tone}. " + "For article tweets, include the article title, a quirky hook, and the URL. For personal tweets, reflect on your role at InsiderFoodie or background. " + "Avoid emojis and clichés like 'game-changer'. Return only the tweet text." + ) + }, + "Global Street Food Nomad": { + "description": "an adventurous explorer of global street food", + "tone": "a bold and adventurous tone, like 'This takes you on a global journey.'", + "prompt": ( + "Craft a tweet as {description}. Keep it under 280 characters, using {tone}. " + "For article tweets, include the article title, a quirky hook, and the URL. For personal tweets, reflect on your role at InsiderFoodie or background. " + "Avoid emojis and clichés like 'game-changer'. Return only the tweet text." + ) + } } + 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' \ No newline at end of file diff --git a/foodie_x_poster.py b/foodie_x_poster.py index 965863e..393ba0b 100644 --- a/foodie_x_poster.py +++ b/foodie_x_poster.py @@ -1,3 +1,4 @@ +# foodie_x_poster.py import json import logging import random @@ -93,7 +94,13 @@ def delete_used_post(post_title): logging.info(f"Deleted post '{post_title}' from recent_posts.json") def generate_article_tweet(author, post, persona): - prompt = X_PERSONA_PROMPTS[persona].replace( + # Format the prompt using description and tone + persona_config = X_PERSONA_PROMPTS[persona] + base_prompt = persona_config["prompt"].format( + description=persona_config["description"], + tone=persona_config["tone"] + ) + prompt = base_prompt.replace( "For article tweets, include the article title, a quirky hook, and the URL.", f"Generate an article tweet including the title '{post['title']}', a quirky hook, and the URL '{post['url']}'." ) @@ -138,7 +145,13 @@ def generate_personal_tweet(author, persona): f"Early memory: {background['early_memory']}, Career path: {background['career_path']}." ) - prompt = X_PERSONA_PROMPTS[persona].replace( + # Format the prompt using description and tone + persona_config = X_PERSONA_PROMPTS[persona] + base_prompt = persona_config["prompt"].format( + description=persona_config["description"], + tone=persona_config["tone"] + ) + prompt = base_prompt.replace( "For personal tweets, reflect on your role at InsiderFoodie or background.", content )