diff --git a/foodie_automator_google.py b/foodie_automator_google.py index 1a29d48..9af1754 100644 --- a/foodie_automator_google.py +++ b/foodie_automator_google.py @@ -20,21 +20,20 @@ from selenium.common.exceptions import TimeoutException from duckduckgo_search import DDGS from foodie_config import ( AUTHORS, RECIPE_KEYWORDS, PROMO_KEYWORDS, HOME_KEYWORDS, PRODUCT_KEYWORDS, - PERSONA_CONFIGS, CATEGORIES, CTAS, get_clean_source_name, X_API_CREDENTIALS + PERSONA_CONFIGS, CATEGORIES, get_clean_source_name, X_API_CREDENTIALS ) from foodie_utils import ( load_json_file, save_json_file, get_image, generate_image_query, upload_image_to_wp, select_best_persona, determine_paragraph_count, is_interesting, generate_title_from_summary, summarize_with_gpt4o, generate_category_from_summary, post_to_wp, prepare_post_data, - smart_image_and_filter, insert_link_naturally, get_flickr_image # Updated function name + smart_image_and_filter, insert_link_naturally, get_flickr_image ) -from foodie_hooks import get_dynamic_hook, select_best_cta +from foodie_hooks import get_dynamic_hook, select_best_cta, get_viral_share_prompt # Added import from dotenv import load_dotenv load_dotenv() -# Flag to indicate if we're in the middle of posting is_posting = False def signal_handler(sig, frame): @@ -179,7 +178,6 @@ def fetch_duckduckgo_news_context(trend_title, hours=24): return trend_title def curate_from_google_trends(geo_list=['US']): - # Fetch Google Trends data for each geo all_trends = [] for geo in geo_list: trends = scrape_google_trends(geo=geo) @@ -189,7 +187,7 @@ def curate_from_google_trends(geo_list=['US']): if not all_trends: print("No Google Trends data available") logging.info("No Google Trends data available") - return None, None, random.randint(600, 1800) # Return a default sleep time + return None, None, random.randint(600, 1800) attempts = 0 max_attempts = 10 @@ -210,7 +208,6 @@ def curate_from_google_trends(geo_list=['US']): print(f"Trying Google Trend: {title} from {source_name}") logging.info(f"Trying Google Trend: {title} from {source_name}") - # Check if the trend should be filtered out image_query, relevance_keywords, skip = smart_image_and_filter(title, summary) if skip: print(f"Skipping filtered Google Trend: {title}") @@ -218,7 +215,6 @@ def curate_from_google_trends(geo_list=['US']): attempts += 1 continue - # Calculate interest score scoring_content = f"{title}\n\n{summary}" interest_score = is_interesting(scoring_content) logging.info(f"Interest score for '{title}': {interest_score}") @@ -228,7 +224,6 @@ def curate_from_google_trends(geo_list=['US']): attempts += 1 continue - # Summarize the trend num_paragraphs = determine_paragraph_count(interest_score) extra_prompt = ( f"Generate exactly {num_paragraphs} paragraphs.\n" @@ -252,24 +247,27 @@ def curate_from_google_trends(geo_list=['US']): final_summary = insert_link_naturally(final_summary, source_name, link) - # Prepare post data post_data, author, category, image_url, image_source, uploader, pixabay_url = prepare_post_data(final_summary, title) if not post_data: attempts += 1 continue - # Fetch image image_url, image_source, uploader, page_url = get_flickr_image(image_query, relevance_keywords) if not image_url: image_url, image_source, uploader, page_url = get_image(image_query) - # Generate hooks and initial CTA hook = get_dynamic_hook(post_data["title"]).strip() cta = select_best_cta(post_data["title"], final_summary, post_url=None) - post_data["content"] = f"{final_summary}\n\n{cta}" + # Generate viral share prompt + share_prompt = get_viral_share_prompt(post_data["title"], final_summary) + share_links_template = ( + f'
' + ) + post_data["content"] = f"{final_summary}\n\n{cta}\n\n{share_links_template}" - # Post to WordPress and tweet global is_posting is_posting = True try: @@ -284,14 +282,18 @@ def curate_from_google_trends(geo_list=['US']): uploader=uploader, pixabay_url=pixabay_url, interest_score=interest_score, - should_post_tweet=True # Post the X tweet on the first call + should_post_tweet=True ) finally: is_posting = False if post_id: + share_text = f"Check out this foodie gem! {post_data['title']}" + share_text_encoded = quote(share_text) + post_url_encoded = quote(post_url) + share_links = share_links_template.format(post_url=post_url_encoded, share_text=share_text_encoded) cta = select_best_cta(post_data["title"], final_summary, post_url=post_url) - post_data["content"] = f"{final_summary}\n\n{cta}" + post_data["content"] = f"{final_summary}\n\n{cta}\n\n{share_links}" is_posting = True try: post_to_wp( @@ -306,7 +308,7 @@ def curate_from_google_trends(geo_list=['US']): pixabay_url=pixabay_url, interest_score=interest_score, post_id=post_id, - should_post_tweet=False # Skip X tweet on the update call + should_post_tweet=False ) finally: is_posting = False @@ -337,7 +339,7 @@ def run_google_trends_automator(): geo_list = ['US', 'GB', 'AU'] post_data, category, sleep_time = curate_from_google_trends(geo_list=geo_list) if sleep_time is None: - sleep_time = random.randint(600, 1800) # Fallback sleep time + sleep_time = random.randint(600, 1800) print(f"Sleeping for {sleep_time}s") logging.info(f"Completed run with sleep time: {sleep_time} seconds") time.sleep(sleep_time) diff --git a/foodie_automator_reddit.py b/foodie_automator_reddit.py index 3845ab2..17e20c8 100644 --- a/foodie_automator_reddit.py +++ b/foodie_automator_reddit.py @@ -17,7 +17,7 @@ import praw from dotenv import load_dotenv from foodie_config import ( AUTHORS, RECIPE_KEYWORDS, PROMO_KEYWORDS, HOME_KEYWORDS, PRODUCT_KEYWORDS, - PERSONA_CONFIGS, CATEGORIES, CTAS, get_clean_source_name, + PERSONA_CONFIGS, CATEGORIES, get_clean_source_name, REDDIT_CLIENT_ID, REDDIT_CLIENT_SECRET, REDDIT_USER_AGENT, LIGHT_TASK_MODEL, X_API_CREDENTIALS ) @@ -26,13 +26,12 @@ from foodie_utils import ( upload_image_to_wp, determine_paragraph_count, insert_link_naturally, summarize_with_gpt4o, generate_category_from_summary, post_to_wp, prepare_post_data, select_best_author, smart_image_and_filter, - get_flickr_image # Updated function name + get_flickr_image ) -from foodie_hooks import get_dynamic_hook, select_best_cta +from foodie_hooks import get_dynamic_hook, select_best_cta, get_viral_share_prompt # Added import load_dotenv() -# Flag to indicate if we're in the middle of posting is_posting = False def signal_handler(sig, frame): @@ -243,7 +242,6 @@ def curate_from_reddit(): logging.info(f"Trying Reddit Post: {title} from {source_name}") image_query, relevance_keywords, skip = smart_image_and_filter(title, summary) - # Check both raw_title and title for keywords if skip or any(keyword in title.lower() or keyword in raw_title.lower() for keyword in RECIPE_KEYWORDS + ["homemade"]): print(f"Skipping filtered Reddit post: {title}") logging.info(f"Skipping filtered Reddit post: {title}") @@ -297,7 +295,6 @@ def curate_from_reddit(): attempts += 1 continue - # Fetch image image_url, image_source, uploader, page_url = get_flickr_image(image_query, relevance_keywords) if not image_url: image_url, image_source, uploader, page_url = get_image(image_query) @@ -305,7 +302,14 @@ def curate_from_reddit(): hook = get_dynamic_hook(post_data["title"]).strip() cta = select_best_cta(post_data["title"], final_summary, post_url=None) - post_data["content"] = f"{final_summary}\n\n{cta}" + # Generate viral share prompt + share_prompt = get_viral_share_prompt(post_data["title"], final_summary) + share_links_template = ( + f'' + ) + post_data["content"] = f"{final_summary}\n\n{cta}\n\n{share_links_template}" global is_posting is_posting = True @@ -327,8 +331,12 @@ def curate_from_reddit(): is_posting = False if post_id: + share_text = f"Check out this foodie gem! {post_data['title']}" + share_text_encoded = quote(share_text) + post_url_encoded = quote(post_url) + share_links = share_links_template.format(post_url=post_url_encoded, share_text=share_text_encoded) cta = select_best_cta(post_data["title"], final_summary, post_url=post_url) - post_data["content"] = f"{final_summary}\n\n{cta}" + post_data["content"] = f"{final_summary}\n\n{cta}\n\n{share_links}" is_posting = True try: post_to_wp( diff --git a/foodie_automator_rss.py b/foodie_automator_rss.py index 49eb3f7..d31360e 100644 --- a/foodie_automator_rss.py +++ b/foodie_automator_rss.py @@ -25,14 +25,13 @@ from foodie_utils import ( upload_image_to_wp, determine_paragraph_count, insert_link_naturally, is_interesting, generate_title_from_summary, summarize_with_gpt4o, generate_category_from_summary, post_to_wp, prepare_post_data, - select_best_author, smart_image_and_filter, get_flickr_image # Updated function name + select_best_author, smart_image_and_filter, get_flickr_image ) -from foodie_hooks import get_dynamic_hook, select_best_cta +from foodie_hooks import get_dynamic_hook, select_best_cta, get_viral_share_prompt # Added import from dotenv import load_dotenv load_dotenv() -# Flag to indicate if we're in the middle of posting is_posting = False def signal_handler(sig, frame): @@ -176,7 +175,7 @@ def fetch_rss_feeds(): continue logging.info(f"Filtered to {len(articles)} articles from {feed_url}") except Exception as e: - logging.error(f"Failed to fetch RSS feed {feed_url}: {e}") + logging.error(f"Failed to fetch RSS feed {feed |url}: {e}") continue articles.sort(key=lambda x: x["pub_date"], reverse=True) @@ -252,12 +251,10 @@ def curate_from_rss(): r'\*\*' + re.escape(title) + r'\*\*|' + re.escape(title), re.IGNORECASE ) - # Split into paragraphs, process each one, then rejoin paragraphs = final_summary.split('\n') cleaned_paragraphs = [] for para in paragraphs: if para.strip(): - # Remove the title and normalize spaces within the paragraph cleaned_para = title_pattern.sub('', para).strip() cleaned_para = re.sub(r'\s+', ' ', cleaned_para) cleaned_paragraphs.append(cleaned_para) @@ -277,7 +274,15 @@ def curate_from_rss(): hook = get_dynamic_hook(post_data["title"]).strip() cta = select_best_cta(post_data["title"], final_summary, post_url=None) - post_data["content"] = f"{final_summary}\n\n{cta}" + # Generate viral share prompt + share_prompt = get_viral_share_prompt(post_data["title"], final_summary) + share_links_template = ( + f'' + ) + post_data["content"] = f"{final_summary}\n\n{cta}\n\n{share_links_template}" + global is_posting is_posting = True try: @@ -298,8 +303,12 @@ def curate_from_rss(): is_posting = False if post_id: + share_text = f"Check out this foodie gem! {post_data['title']}" + share_text_encoded = quote(share_text) + post_url_encoded = quote(post_url) + share_links = share_links_template.format(post_url=post_url_encoded, share_text=share_text_encoded) cta = select_best_cta(post_data["title"], final_summary, post_url=post_url) - post_data["content"] = f"{final_summary}\n\n{cta}" + post_data["content"] = f"{final_summary}\n\n{cta}\n\n{share_links}" is_posting = True try: post_to_wp( diff --git a/foodie_hooks.py b/foodie_hooks.py index c4db60c..0fa45ba 100644 --- a/foodie_hooks.py +++ b/foodie_hooks.py @@ -28,14 +28,34 @@ def get_dynamic_hook(article_title): except Exception as e: logging.error(f"Dynamic hook generation failed: {e}") return "This food scoop will blow your mind!" + +def get_viral_share_prompt(article_title, content): + try: + prompt = ( + "Generate a short, viral share prompt (under 40 characters) to encourage sharing a food-related article on social media. " + "Make it bold, quirky, and engaging with a Buzzfeed/Upworthy flair (e.g., 'Obsessed? Share Now'). " + "Avoid clichés like 'game-changer' and words like 'elevate'. " + "Do not include emojis, the platform name (e.g., Twitter, Facebook), or any HTML. " + "Return only the prompt text." + ) + response = client.chat.completions.create( + model=LIGHT_TASK_MODEL, + messages=[ + {"role": "system", "content": prompt}, + {"role": "user", "content": f"Title: {article_title}\nContent: {content}"} + ], + max_tokens=20, + temperature=0.9 # Higher temperature for more creativity + ) + share_prompt = response.choices[0].message.content.strip() + logging.info(f"Generated viral share prompt: {share_prompt}") + return share_prompt + except Exception as e: + logging.error(f"Viral share prompt generation failed: {e}") + return "Love This? Share It" def select_best_cta(title, content, post_url=None): cta_templates = [ - # Share-focused CTAs - "Cant Get Enough Share This Now On ", - "This Blew Your Mind Right Tweet It On ", - "Ready to Spill the Tea Share On ", - "Too Wild to Keep Quiet Post It On ", # Like-focused CTAs "Love This Foodie Find Hit That Like Button", "Think This Dish Is a Game Changer Show Some Love With a Like", @@ -44,12 +64,5 @@ def select_best_cta(title, content, post_url=None): "Dont Miss Out on Foodie Updates Follow Us Today" ] - hook = get_dynamic_hook(title).strip() - hook = urllib.parse.quote(hook) - url = post_url if post_url else "https://insiderfoodie.com/placeholder" - url = urllib.parse.quote(url) - cta = random.choice(cta_templates) - if "https://x.com/intent/tweet" in cta: - cta = cta.format(url=url, text=hook) return cta \ No newline at end of file