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 foodie_utils import (
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 sys
@ -20,8 +21,41 @@ logging.basicConfig(
)
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):
"""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:
msg = MIMEMultipart()
msg['From'] = EMAIL_CONFIG['from_email']

@ -164,29 +164,28 @@ def post_engagement_tweet():
logging.info("Starting foodie_engagement_tweet.py")
posted = False
for author in AUTHORS:
# Check if the author can post before generating the tweet
can_post, remaining, reset = check_author_rate_limit(author)
if not can_post:
reset_time = datetime.fromtimestamp(reset, tz=timezone.utc).strftime('%Y-%m-%d %H:%M:%S') if reset else "Unknown"
logging.info(f"Skipping engagement tweet for {author['username']} due to rate limit. Reset at: {reset_time}")
continue
# 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")
sleep_time = random.randint(1200, 1800) # 20–30 minutes
return False, sleep_time
try:
tweet = generate_engagement_tweet(author)
if not tweet:
logging.error(f"Failed to generate engagement tweet for {author['username']}, skipping")
continue
logging.info(f"Posting engagement tweet for {author['username']}: {tweet}")
if post_tweet(author, tweet):
logging.info(f"Successfully posted engagement tweet for {author['username']}")
posted = True
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)
continue
try:
tweet = generate_engagement_tweet(author)
if not tweet:
logging.error(f"Failed to generate engagement tweet for {author['username']}, skipping")
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"Successfully posted engagement tweet for {author['username']}")
posted = True
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)
logging.info("Completed foodie_engagement_tweet.py")
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]
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['tweet_remaining'] = remaining - 1 # Decrement remaining tweets
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)
author_info['tweet_remaining'] = remaining
author_info['tweet_reset'] = reset
# Don't reset tweets_posted_in_run here
rate_limit_info[username] = author_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")
@ -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
reset = api_reset
# Update author info
# Update author info but preserve tweets_posted_in_run
author_info['tweet_remaining'] = remaining
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
save_json_file(rate_limit_file, rate_limit_info)

@ -93,31 +93,37 @@ def generate_engagement_tweet(author, persona):
return tweet
except Exception as 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():
global is_posting
logging.info("***** X Poster Launched *****")
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 not can_post:
reset_time = datetime.fromtimestamp(reset, tz=timezone.utc).strftime('%Y-%m-%d %H:%M:%S') if reset else "Unknown"
logging.info(f"Skipping engagement tweet for {author['username']} due to rate limit. Remaining: {remaining}, Reset at: {reset_time}")
continue
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
time.sleep(random.uniform(3600, 7200))
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)

Loading…
Cancel
Save