Shane 7 months ago
parent 05f2dfed06
commit eff6f585bb
  1. 36
      check_x_capacity.py
  2. 43
      foodie_engagement_tweet.py
  3. 7
      foodie_utils.py
  4. 46
      foodie_x_poster.py

@ -3,7 +3,8 @@ import logging
from datetime import datetime, timezone from datetime import datetime, timezone
from foodie_utils import ( from foodie_utils import (
AUTHORS, check_author_rate_limit, load_json_file, AUTHORS, check_author_rate_limit, load_json_file,
get_x_rate_limit_status, update_system_activity, is_any_script_running get_x_rate_limit_status, update_system_activity, is_any_script_running,
save_json_file
) )
import time import time
import sys import sys
@ -20,8 +21,41 @@ logging.basicConfig(
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# File to track sent notifications
NOTIFICATION_FILE = '/home/shane/foodie_automator/notification_tracking.json'
def load_notification_tracking():
"""Load notification tracking data."""
return load_json_file(NOTIFICATION_FILE, default={})
def save_notification_tracking(tracking_data):
"""Save notification tracking data."""
save_json_file(NOTIFICATION_FILE, tracking_data)
def should_send_notification(username, reset_time):
"""Check if we should send a notification for this author."""
tracking = load_notification_tracking()
author_data = tracking.get(username, {})
# If we've never notified for this author or the reset time has changed
if not author_data or author_data.get('reset_time') != reset_time:
# Update tracking
tracking[username] = {
'last_notification': datetime.now(timezone.utc).isoformat(),
'reset_time': reset_time
}
save_notification_tracking(tracking)
return True
return False
def send_capacity_alert(username, remaining, reset_time): def send_capacity_alert(username, remaining, reset_time):
"""Send email alert when an author's tweet capacity is full.""" """Send email alert when an author's tweet capacity is full."""
# Check if we should send notification
if not should_send_notification(username, reset_time):
logger.info(f"Skipping duplicate notification for {username}")
return
try: try:
msg = MIMEMultipart() msg = MIMEMultipart()
msg['From'] = EMAIL_CONFIG['from_email'] msg['From'] = EMAIL_CONFIG['from_email']

@ -164,29 +164,28 @@ def post_engagement_tweet():
logging.info("Starting foodie_engagement_tweet.py") logging.info("Starting foodie_engagement_tweet.py")
posted = False posted = False
for author in AUTHORS: # Get next available author using round-robin
# Check if the author can post before generating the tweet author = get_next_author_round_robin()
can_post, remaining, reset = check_author_rate_limit(author) if not author:
if not can_post: logging.info("No authors available due to rate limits")
reset_time = datetime.fromtimestamp(reset, tz=timezone.utc).strftime('%Y-%m-%d %H:%M:%S') if reset else "Unknown" sleep_time = random.randint(1200, 1800) # 20–30 minutes
logging.info(f"Skipping engagement tweet for {author['username']} due to rate limit. Reset at: {reset_time}") return False, sleep_time
continue
try: try:
tweet = generate_engagement_tweet(author) tweet = generate_engagement_tweet(author)
if not tweet: if not tweet:
logging.error(f"Failed to generate engagement tweet for {author['username']}, skipping") logging.error(f"Failed to generate engagement tweet for {author['username']}, skipping")
continue sleep_time = random.randint(1200, 1800) # 20–30 minutes
return False, sleep_time
logging.info(f"Posting engagement tweet for {author['username']}: {tweet}")
if post_tweet(author, tweet): logging.info(f"Posting engagement tweet for {author['username']}: {tweet}")
logging.info(f"Successfully posted engagement tweet for {author['username']}") if post_tweet(author, tweet):
posted = True logging.info(f"Successfully posted engagement tweet for {author['username']}")
else: posted = True
logging.warning(f"Failed to post engagement tweet for {author['username']}") else:
except Exception as e: logging.warning(f"Failed to post engagement tweet for {author['username']}")
logging.error(f"Error posting engagement tweet for {author['username']}: {e}", exc_info=True) except Exception as e:
continue logging.error(f"Error posting engagement tweet for {author['username']}: {e}", exc_info=True)
logging.info("Completed foodie_engagement_tweet.py") logging.info("Completed foodie_engagement_tweet.py")
sleep_time = random.randint(1200, 1800) # 20–30 minutes sleep_time = random.randint(1200, 1800) # 20–30 minutes

@ -235,7 +235,7 @@ def post_tweet(author, content, media_ids=None, reply_to_id=None, tweet_type="rs
author_info = rate_limit_info[username] author_info = rate_limit_info[username]
if response.status_code == 201: if response.status_code == 201:
# Successful post - update remaining tweets # Successful post - update remaining tweets and increment posted count
author_info['tweets_posted_in_run'] = author_info.get('tweets_posted_in_run', 0) + 1 author_info['tweets_posted_in_run'] = author_info.get('tweets_posted_in_run', 0) + 1
author_info['tweet_remaining'] = remaining - 1 # Decrement remaining tweets author_info['tweet_remaining'] = remaining - 1 # Decrement remaining tweets
rate_limit_info[username] = author_info rate_limit_info[username] = author_info
@ -251,6 +251,7 @@ def post_tweet(author, content, media_ids=None, reply_to_id=None, tweet_type="rs
reset = int(reset_str) reset = int(reset_str)
author_info['tweet_remaining'] = remaining author_info['tweet_remaining'] = remaining
author_info['tweet_reset'] = reset author_info['tweet_reset'] = reset
# Don't reset tweets_posted_in_run here
rate_limit_info[username] = author_info rate_limit_info[username] = author_info
save_json_file(rate_limit_file, rate_limit_info) save_json_file(rate_limit_file, rate_limit_info)
logger.info(f"Updated rate limit info from API for {username}: {remaining}/17 tweets remaining") logger.info(f"Updated rate limit info from API for {username}: {remaining}/17 tweets remaining")
@ -1798,10 +1799,10 @@ def check_author_rate_limit(author, max_tweets=17, tweet_window_seconds=86400):
remaining = min(remaining, max_tweets) # Ensure within Free tier limit remaining = min(remaining, max_tweets) # Ensure within Free tier limit
reset = api_reset reset = api_reset
# Update author info # Update author info but preserve tweets_posted_in_run
author_info['tweet_remaining'] = remaining author_info['tweet_remaining'] = remaining
author_info['tweet_reset'] = reset author_info['tweet_reset'] = reset
author_info['tweets_posted_in_run'] = 0 # Don't reset tweets_posted_in_run here
rate_limit_info[username] = author_info rate_limit_info[username] = author_info
save_json_file(rate_limit_file, rate_limit_info) save_json_file(rate_limit_file, rate_limit_info)

@ -93,31 +93,37 @@ def generate_engagement_tweet(author, persona):
return tweet return tweet
except Exception as e: except Exception as e:
logging.error(f"Failed to generate engagement tweet for {author['username']}: {e}") logging.error(f"Failed to generate engagement tweet for {author['username']}: {e}")
return f"Whats your take on {theme}? Lets talk!" return f"What's your take on {theme}? Let's talk!"
def main(): def get_next_author_round_robin():
global is_posting
logging.info("***** X Poster Launched *****")
for author in AUTHORS: for author in AUTHORS:
# Check if the author can post before generating the tweet # Check if the author can post before generating the tweet
can_post, remaining, reset = check_author_rate_limit(author) can_post, remaining, reset = check_author_rate_limit(author)
if not can_post: if can_post:
reset_time = datetime.fromtimestamp(reset, tz=timezone.utc).strftime('%Y-%m-%d %H:%M:%S') if reset else "Unknown" return author
logging.info(f"Skipping engagement tweet for {author['username']} due to rate limit. Remaining: {remaining}, Reset at: {reset_time}") return None
continue
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 is_posting = True
try: try:
tweet = generate_engagement_tweet(author, author["persona"]) tweet = generate_engagement_tweet(author, author["persona"])
if post_tweet(author, tweet): if post_tweet(author, tweet):
logging.info(f"Successfully posted engagement tweet for {author['username']}") logging.info(f"Successfully posted engagement tweet for {author['username']}")
else: else:
logging.warning(f"Failed to post engagement tweet for {author['username']}") logging.warning(f"Failed to post engagement tweet for {author['username']}")
except Exception as e: except Exception as e:
logging.error(f"Error posting engagement tweet for {author['username']}: {e}", exc_info=True) logging.error(f"Error posting engagement tweet for {author['username']}: {e}", exc_info=True)
finally: finally:
is_posting = False is_posting = False
time.sleep(random.uniform(3600, 7200))
logging.info("X posting completed") logging.info("X posting completed")
return random.randint(600, 1800) return random.randint(600, 1800)

Loading…
Cancel
Save