update personas to add Tone

my-fix-branch
Shane 7 months ago
parent 1f1c5012c7
commit a1d2ce4215
  1. 30
      foodie_automator_reddit.py
  2. 82
      foodie_config.py
  3. 13
      foodie_utils.py
  4. 59
      foodie_x_config.py
  5. 17
      foodie_x_poster.py

@ -47,18 +47,36 @@ def setup_logging():
if os.path.exists(LOG_FILE): if os.path.exists(LOG_FILE):
with open(LOG_FILE, 'r') as f: with open(LOG_FILE, 'r') as f:
lines = f.readlines() 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: 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: 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: if timestamp > cutoff:
pruned_lines.append(line) pruned_entries.append(entry)
except ValueError: except ValueError:
logging.warning(f"Skipping malformed log line: {line.strip()[:50]}...") logging.warning(f"Skipping malformed log entry (no timestamp): {entry[:50]}...")
continue continue
with open(LOG_FILE, 'w') as f: with open(LOG_FILE, 'w') as f:
f.writelines(pruned_lines) f.writelines(pruned_entries)
logging.basicConfig( logging.basicConfig(
filename=LOG_FILE, filename=LOG_FILE,

@ -104,43 +104,67 @@ CTAS = [
] ]
SUMMARY_PERSONA_PROMPTS = { SUMMARY_PERSONA_PROMPTS = {
"Visionary Editor": ( "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!'. " "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. " "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. " "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 clickbait-y question like Neil Patel would do to boost engagement! Do not include emojis in the summary." "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": ( },
"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!'. " "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. " "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. " "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 clickbait-y question like Neil Patel would do to boost engagement! Do not include emojis in the summary." "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": ( },
"You’re a forward-thinking editor obsessed with trends. Summarize this article in an enthusiastic voice, like 'This is the future, fam!'. " "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. " "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. " "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 clickbait-y 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."
), )
"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?'. " "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. " "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. " "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 clickbait-y question like Neil Patel would do to boost engagement! Do not include emojis in the summary." "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": ( },
"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!'. " "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. " "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. " "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 clickbait-y question like Neil Patel would do to boost engagement! Do not include emojis in the summary." "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": ( },
"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!'. " "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. " "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. " "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 clickbait-y question like Neil Patel would do to boost engagement! Do not include emojis in the summary." "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") REDDIT_CLIENT_ID = os.getenv("REDDIT_CLIENT_ID")
REDDIT_CLIENT_SECRET = os.getenv("REDDIT_CLIENT_SECRET") REDDIT_CLIENT_SECRET = os.getenv("REDDIT_CLIENT_SECRET")

@ -339,7 +339,18 @@ def generate_title_from_summary(summary):
def summarize_with_gpt4o(content, source_name, link, interest_score=0, extra_prompt=""): def summarize_with_gpt4o(content, source_name, link, interest_score=0, extra_prompt=""):
try: try:
persona = select_best_persona(interest_score, content) 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") logging.info(f"Using {persona} with interest_score and content")
full_prompt = ( full_prompt = (

@ -62,37 +62,62 @@ X_API_CREDENTIALS = [
] ]
X_PERSONA_PROMPTS = { X_PERSONA_PROMPTS = {
"Visionary Editor": ( "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!'. " "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. " "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." "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!'. " "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. " "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." "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!'. " "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. " "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." "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?'. " "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. " "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." "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!'. " "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. " "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." "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!'. " "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. " "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." "Avoid emojis and clichés like 'game-changer'. Return only the tweet text."
) )
} }
}
AUTHOR_BACKGROUNDS_FILE = '/home/shane/foodie_automator/author_backgrounds.json' AUTHOR_BACKGROUNDS_FILE = '/home/shane/foodie_automator/author_backgrounds.json'
X_POST_COUNTS_FILE = '/home/shane/foodie_automator/x_post_counts.json' X_POST_COUNTS_FILE = '/home/shane/foodie_automator/x_post_counts.json'

@ -1,3 +1,4 @@
# foodie_x_poster.py
import json import json
import logging import logging
import random import random
@ -93,7 +94,13 @@ def delete_used_post(post_title):
logging.info(f"Deleted post '{post_title}' from recent_posts.json") logging.info(f"Deleted post '{post_title}' from recent_posts.json")
def generate_article_tweet(author, post, persona): 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.", "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']}'." 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']}." 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.", "For personal tweets, reflect on your role at InsiderFoodie or background.",
content content
) )

Loading…
Cancel
Save