You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

134 lines
4.9 KiB

# foodie_x_poster.py
import json
import logging
import random
import time
import sys
import signal
import os
from datetime import datetime, timezone, timedelta
from openai import OpenAI
from foodie_config import OPENAI_API_KEY, AUTHORS, LIGHT_TASK_MODEL, PERSONA_CONFIGS, AUTHOR_BACKGROUNDS_FILE
from foodie_utils import load_json_file, post_tweet, check_author_rate_limit
from dotenv import load_dotenv
load_dotenv()
LOG_FILE = "/home/shane/foodie_automator/foodie_x_poster.log"
LOG_PRUNE_DAYS = 30
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 = [line for line in lines if datetime.strptime(line[:19], '%Y-%m-%d %H:%M:%S').replace(tzinfo=timezone.utc) > cutoff]
with open(LOG_FILE, 'w') as f:
f.writelines(pruned_lines)
logging.basicConfig(
filename=LOG_FILE,
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
console_handler = logging.StreamHandler()
console_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
logging.getLogger().addHandler(console_handler)
logging.info("Logging initialized for foodie_x_poster.py")
setup_logging()
client = OpenAI(api_key=OPENAI_API_KEY)
try:
with open(AUTHOR_BACKGROUNDS_FILE, 'r') as f:
AUTHOR_BACKGROUNDS = json.load(f)
except Exception as e:
logging.error(f"Failed to load author_backgrounds.json: {e}")
sys.exit(1)
is_posting = False
def signal_handler(sig, frame):
logging.info("Received termination signal, checking if safe to exit...")
if is_posting:
logging.info("Currently posting, will exit after completion.")
else:
logging.info("Safe to exit immediately.")
sys.exit(0)
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler)
def generate_engagement_tweet(author, persona):
background = next((bg for bg in AUTHOR_BACKGROUNDS if bg["username"] == author["username"]), {})
if not background or "engagement_themes" not in background:
logging.warning(f"No background or engagement themes found for {author['username']}")
return "What food trends are you loving right now? Share your thoughts! #FoodieTrends"
theme = random.choice(background["engagement_themes"])
persona_config = PERSONA_CONFIGS[persona]
base_prompt = persona_config["x_prompt"].format(
description=persona_config["description"],
tone=persona_config["tone"]
)
prompt = base_prompt.replace(
"For engagement tweets, ask a question about food trends, foods, or articles to engage the public.",
f"Generate an engagement tweet asking a question about {theme} to engage the public."
)
try:
response = client.chat.completions.create(
model=LIGHT_TASK_MODEL,
messages=[
{"role": "system", "content": prompt},
{"role": "user", "content": f"Generate engagement tweet for {author['username']} about {theme}."}
],
max_tokens=100,
temperature=0.9
)
tweet = response.choices[0].message.content.strip()
if len(tweet) > 280:
tweet = tweet[:277] + "..."
logging.info(f"Generated engagement tweet for {author['username']}: {tweet}")
return tweet
except Exception as e:
logging.error(f"Failed to generate engagement tweet for {author['username']}: {e}")
return f"What's your take on {theme}? Let's talk!"
def get_next_author_round_robin():
for author in AUTHORS:
# Check if the author can post before generating the tweet
can_post, remaining, reset = check_author_rate_limit(author)
if can_post:
return author
return None
def main():
global is_posting
logging.info("***** X Poster Launched *****")
# Get next available author using round-robin
author = get_next_author_round_robin()
if not author:
logging.info("No authors available due to rate limits")
return random.randint(600, 1800)
is_posting = True
try:
tweet = generate_engagement_tweet(author, author["persona"])
if post_tweet(author, tweet):
logging.info(f"Successfully posted engagement tweet for {author['username']}")
else:
logging.warning(f"Failed to post engagement tweet for {author['username']}")
except Exception as e:
logging.error(f"Error posting engagement tweet for {author['username']}: {e}", exc_info=True)
finally:
is_posting = False
logging.info("X posting completed")
return random.randint(600, 1800)
if __name__ == "__main__":
sleep_time = main()
logging.info(f"Sleeping for {sleep_time} seconds")
time.sleep(sleep_time)