fix
This commit is contained in:
+53
-10
@@ -8,6 +8,7 @@ import fcntl
|
|||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
|
import tweepy
|
||||||
from openai import OpenAI
|
from openai import OpenAI
|
||||||
from foodie_utils import post_tweet, load_post_counts, save_post_counts
|
from foodie_utils import post_tweet, load_post_counts, save_post_counts
|
||||||
from foodie_config import (
|
from foodie_config import (
|
||||||
@@ -61,6 +62,7 @@ def setup_logging():
|
|||||||
console_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
|
console_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
|
||||||
logging.getLogger().addHandler(console_handler)
|
logging.getLogger().addHandler(console_handler)
|
||||||
logging.getLogger("openai").setLevel(logging.WARNING)
|
logging.getLogger("openai").setLevel(logging.WARNING)
|
||||||
|
logging.getLogger("tweepy").setLevel(logging.WARNING)
|
||||||
logging.info("Logging initialized for foodie_engagement_tweet.py")
|
logging.info("Logging initialized for foodie_engagement_tweet.py")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Failed to setup logging: {e}")
|
print(f"Failed to setup logging: {e}")
|
||||||
@@ -101,10 +103,39 @@ except Exception as e:
|
|||||||
try:
|
try:
|
||||||
with open(AUTHOR_BACKGROUNDS_FILE, 'r') as f:
|
with open(AUTHOR_BACKGROUNDS_FILE, 'r') as f:
|
||||||
AUTHOR_BACKGROUNDS = json.load(f)
|
AUTHOR_BACKGROUNDS = json.load(f)
|
||||||
|
logging.debug(f"Loaded author backgrounds: {[bg['username'] for bg in AUTHOR_BACKGROUNDS]}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Failed to load author_backgrounds.json: {e}", exc_info=True)
|
logging.error(f"Failed to load author_backgrounds.json: {e}", exc_info=True)
|
||||||
|
AUTHOR_BACKGROUNDS = []
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
def validate_twitter_credentials(author):
|
||||||
|
"""Validate Twitter API credentials for a specific author."""
|
||||||
|
username = author["username"]
|
||||||
|
credentials = X_API_CREDENTIALS.get(username)
|
||||||
|
if not credentials:
|
||||||
|
logging.error(f"No X credentials found for {username}")
|
||||||
|
return False
|
||||||
|
for attempt in range(MAX_RETRIES):
|
||||||
|
try:
|
||||||
|
twitter_client = tweepy.Client(
|
||||||
|
consumer_key=credentials["api_key"],
|
||||||
|
consumer_secret=credentials["api_secret"],
|
||||||
|
access_token=credentials["access_token"],
|
||||||
|
access_token_secret=credentials["access_token_secret"]
|
||||||
|
)
|
||||||
|
user = twitter_client.get_me()
|
||||||
|
logging.info(f"Credentials valid for {username} (handle: {credentials['x_username']})")
|
||||||
|
return True
|
||||||
|
except tweepy.TweepyException as e:
|
||||||
|
logging.warning(f"Failed to validate credentials for {username} (attempt {attempt + 1}): {e}")
|
||||||
|
if attempt < MAX_RETRIES - 1:
|
||||||
|
time.sleep(RETRY_BACKOFF * (2 ** attempt))
|
||||||
|
else:
|
||||||
|
logging.error(f"Credentials invalid for {username} after {MAX_RETRIES} attempts")
|
||||||
|
return False
|
||||||
|
return False
|
||||||
|
|
||||||
def get_reference_date():
|
def get_reference_date():
|
||||||
"""Load or initialize the reference date for the 2-day interval."""
|
"""Load or initialize the reference date for the 2-day interval."""
|
||||||
os.makedirs(os.path.dirname(REFERENCE_DATE_FILE), exist_ok=True)
|
os.makedirs(os.path.dirname(REFERENCE_DATE_FILE), exist_ok=True)
|
||||||
@@ -130,20 +161,26 @@ def get_reference_date():
|
|||||||
def generate_engagement_tweet(author):
|
def generate_engagement_tweet(author):
|
||||||
"""Generate an engagement tweet using author background themes and persona."""
|
"""Generate an engagement tweet using author background themes and persona."""
|
||||||
username = author["username"]
|
username = author["username"]
|
||||||
credentials = X_API_CREDENTIALS.get(username)
|
if not validate_twitter_credentials(author):
|
||||||
if not credentials:
|
logging.error(f"Skipping tweet generation for {username} due to invalid credentials")
|
||||||
logging.error(f"No X credentials found for {username}")
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
credentials = X_API_CREDENTIALS.get(username)
|
||||||
author_handle = credentials["x_username"]
|
author_handle = credentials["x_username"]
|
||||||
persona = author["persona"]
|
persona = author["persona"]
|
||||||
persona_config = PERSONA_CONFIGS.get(persona, PERSONA_CONFIGS["Visionary Editor"])
|
persona_config = PERSONA_CONFIGS.get(persona, PERSONA_CONFIGS["Visionary Editor"])
|
||||||
|
|
||||||
background = next((bg for bg in AUTHOR_BACKGROUNDS if bg["username"] == username), {})
|
# Case-insensitive lookup for background
|
||||||
|
background = next(
|
||||||
|
(bg for bg in AUTHOR_BACKGROUNDS if bg["username"].lower() == username.lower()),
|
||||||
|
{}
|
||||||
|
)
|
||||||
if not background or "engagement_themes" not in background:
|
if not background or "engagement_themes" not in background:
|
||||||
logging.warning(f"No background or engagement themes found for {username}, using default theme")
|
logging.warning(f"No background or engagement themes found for {username}, using default theme")
|
||||||
theme = "food trends"
|
theme = "food trends"
|
||||||
else:
|
else:
|
||||||
theme = random.choice(background["engagement_themes"])
|
theme = random.choice(background["engagement_themes"])
|
||||||
|
logging.debug(f"Selected engagement theme '{theme}' for {username}")
|
||||||
|
|
||||||
base_prompt = persona_config["x_prompt"].format(
|
base_prompt = persona_config["x_prompt"].format(
|
||||||
description=persona_config["description"],
|
description=persona_config["description"],
|
||||||
@@ -152,7 +189,8 @@ def generate_engagement_tweet(author):
|
|||||||
prompt = (
|
prompt = (
|
||||||
f"{base_prompt}\n\n"
|
f"{base_prompt}\n\n"
|
||||||
f"Generate an engagement tweet for {author_handle} asking a question about {theme} to engage the public. "
|
f"Generate an engagement tweet for {author_handle} asking a question about {theme} to engage the public. "
|
||||||
f"Keep it under 280 characters, using {persona_config['tone']}. "
|
f"Keep it under 230 characters to ensure room for the URL. "
|
||||||
|
f"Use {persona_config['tone']}. "
|
||||||
f"Include a call to action to follow {author_handle} or like the tweet, and mention InsiderFoodie.com with a link to https://insiderfoodie.com. "
|
f"Include a call to action to follow {author_handle} or like the tweet, and mention InsiderFoodie.com with a link to https://insiderfoodie.com. "
|
||||||
f"Avoid using the word 'elevate'—use more humanized language like 'level up' or 'bring to life'. "
|
f"Avoid using the word 'elevate'—use more humanized language like 'level up' or 'bring to life'. "
|
||||||
f"Do not include emojis, hashtags, or reward-driven incentives (e.g., giveaways). "
|
f"Do not include emojis, hashtags, or reward-driven incentives (e.g., giveaways). "
|
||||||
@@ -167,12 +205,14 @@ def generate_engagement_tweet(author):
|
|||||||
{"role": "system", "content": "You are a social media expert crafting engaging tweets."},
|
{"role": "system", "content": "You are a social media expert crafting engaging tweets."},
|
||||||
{"role": "user", "content": prompt}
|
{"role": "user", "content": prompt}
|
||||||
],
|
],
|
||||||
max_tokens=100,
|
max_tokens=80, # Reduced to ensure shorter tweets
|
||||||
temperature=0.7
|
temperature=0.7
|
||||||
)
|
)
|
||||||
tweet = response.choices[0].message.content.strip()
|
tweet = response.choices[0].message.content.strip()
|
||||||
if len(tweet) > 280:
|
# Ensure tweet length is within limits (accounting for URL)
|
||||||
tweet = tweet[:277] + "..."
|
url_length = 23 # Twitter shortens URLs
|
||||||
|
if len(tweet) > (280 - url_length):
|
||||||
|
tweet = tweet[:(280 - url_length - 3)] + "..."
|
||||||
logging.debug(f"Generated engagement tweet for {username}: {tweet}")
|
logging.debug(f"Generated engagement tweet for {username}: {tweet}")
|
||||||
return tweet
|
return tweet
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -182,11 +222,14 @@ def generate_engagement_tweet(author):
|
|||||||
else:
|
else:
|
||||||
logging.error(f"Failed to generate engagement tweet after {MAX_RETRIES} attempts")
|
logging.error(f"Failed to generate engagement tweet after {MAX_RETRIES} attempts")
|
||||||
fallback = (
|
fallback = (
|
||||||
f"What's the hottest {theme} you're into? Share and follow {author_handle} for more on InsiderFoodie.com! "
|
f"What's the hottest {theme}? Share and follow {author_handle} for more on InsiderFoodie.com! "
|
||||||
f"Link: https://insiderfoodie.com"
|
f"https://insiderfoodie.com"
|
||||||
)
|
)
|
||||||
|
if len(fallback) > (280 - url_length):
|
||||||
|
fallback = fallback[:(280 - url_length - 3)] + "..."
|
||||||
logging.info(f"Using fallback engagement tweet: {fallback}")
|
logging.info(f"Using fallback engagement tweet: {fallback}")
|
||||||
return fallback
|
return fallback
|
||||||
|
return None
|
||||||
|
|
||||||
def post_engagement_tweet():
|
def post_engagement_tweet():
|
||||||
"""Post engagement tweets for authors every 2 days."""
|
"""Post engagement tweets for authors every 2 days."""
|
||||||
|
|||||||
Reference in New Issue
Block a user