Shane 7 months ago
parent 7702434d41
commit c4e01d3d63
  1. 4
      foodie_engagement_tweet.py
  2. 93
      foodie_utils.py

@ -414,6 +414,10 @@ def post_engagement_tweet():
save_post_counts(post_counts) save_post_counts(post_counts)
else: else:
logging.warning(f"Failed to post engagement tweet for {username}") logging.warning(f"Failed to post engagement tweet for {username}")
# Add a 5-second delay between posts to avoid rate limits
time.sleep(5)
except Exception as e: except Exception as e:
logging.error(f"Error posting engagement tweet for {username}: {e}", exc_info=True) logging.error(f"Error posting engagement tweet for {username}: {e}", exc_info=True)
continue continue

@ -222,6 +222,9 @@ def post_tweet(author, tweet, reply_to_id=None):
import logging import logging
import tweepy import tweepy
from datetime import datetime, timezone from datetime import datetime, timezone
import time
import random
from tweepy.errors import TooManyRequests
username = author["username"] username = author["username"]
if username not in X_API_CREDENTIALS: if username not in X_API_CREDENTIALS:
@ -260,6 +263,9 @@ def post_tweet(author, tweet, reply_to_id=None):
logging.warning(f"Daily post limit (20) reached for {username}") logging.warning(f"Daily post limit (20) reached for {username}")
return False return False
max_retries = 3
retry_delay = 5 # Initial delay in seconds
try: try:
client = tweepy.Client( client = tweepy.Client(
consumer_key=credentials["api_key"], consumer_key=credentials["api_key"],
@ -267,24 +273,75 @@ def post_tweet(author, tweet, reply_to_id=None):
access_token=credentials["access_token"], access_token=credentials["access_token"],
access_token_secret=credentials["access_token_secret"] access_token_secret=credentials["access_token_secret"]
) )
response = client.create_tweet(
text=tweet, for attempt in range(max_retries + 1):
in_reply_to_tweet_id=reply_to_id try:
) response = client.create_tweet(
# Update post counts text=tweet,
author_count["monthly_count"] += 1 in_reply_to_tweet_id=reply_to_id
author_count["daily_count"] += 1 )
save_post_counts(post_counts) # Log rate limit headers on success
logging.info(f"Posted tweet for {username} (handle: {credentials['x_username']}): {tweet}") if hasattr(client, 'session') and client.session.last_response:
logging.debug(f"Tweet ID: {response.data['id']}") rate_limit_headers = {
return {"id": response.data["id"]} "x-rate-limit-limit": client.session.last_response.headers.get("x-rate-limit-limit"),
except tweepy.TweepyException as e: "x-rate-limit-remaining": client.session.last_response.headers.get("x-rate-limit-remaining"),
logging.error(f"Failed to post tweet for {username} (handle: {credentials['x_username']}): {e}") "x-rate-limit-reset": client.session.last_response.headers.get("x-rate-limit-reset"),
if hasattr(e, 'response') and e.response: }
logging.error(f"Twitter API response: {e.response.text}") logging.debug(f"Rate limit headers after posting for {username}: {rate_limit_headers}")
if "forbidden" in str(e).lower(): else:
logging.error(f"Possible causes: invalid credentials, insufficient permissions, or account restrictions for {credentials['x_username']}") logging.debug("No rate limit headers available in response")
return False
# Update post counts
author_count["monthly_count"] += 1
author_count["daily_count"] += 1
save_post_counts(post_counts)
logging.info(f"Posted tweet for {username} (handle: {credentials['x_username']}): {tweet}")
logging.debug(f"Tweet ID: {response.data['id']}")
return {"id": response.data["id"]}
except TooManyRequests as e:
if attempt == max_retries:
logging.error(f"Failed to post tweet for {username} after {max_retries} retries due to rate limit")
return False
# Log rate limit headers on failure
rate_limit_headers = {}
if hasattr(e, 'response') and e.response:
rate_limit_headers = {
"x-rate-limit-limit": e.response.headers.get("x-rate-limit-limit"),
"x-rate-limit-remaining": e.response.headers.get("x-rate-limit-remaining"),
"x-rate-limit-reset": e.response.headers.get("x-rate-limit-reset"),
}
logging.debug(f"Rate limit headers after failed post for {username}: {rate_limit_headers}")
# Determine wait time based on reset header or fallback to exponential backoff
reset_time = rate_limit_headers.get("x-rate-limit-reset")
if reset_time:
wait_time = int(reset_time) - int(time.time()) + 1
wait_time = max(wait_time, 1) # Ensure we wait at least 1 second
else:
wait_time = retry_delay * (2 ** attempt) + random.uniform(0, 1) # Exponential backoff with jitter
logging.warning(
f"Rate limit exceeded for {username}. Attempt {attempt + 1}/{max_retries}. "
f"Waiting {wait_time:.2f} seconds before retrying..."
)
time.sleep(wait_time)
except tweepy.TweepyException as e:
logging.error(f"Failed to post tweet for {username} (handle: {credentials['x_username']}): {e}")
if hasattr(e, 'response') and e.response:
logging.error(f"Twitter API response: {e.response.text}")
rate_limit_headers = {
"x-rate-limit-limit": e.response.headers.get("x-rate-limit-limit"),
"x-rate-limit-remaining": e.response.headers.get("x-rate-limit-remaining"),
"x-rate-limit-reset": e.response.headers.get("x-rate-limit-reset"),
}
logging.debug(f"Rate limit headers after failed post for {username}: {rate_limit_headers}")
if "forbidden" in str(e).lower():
logging.error(f"Possible causes: invalid credentials, insufficient permissions, or account restrictions for {credentials['x_username']}")
return False
except Exception as e: except Exception as e:
logging.error(f"Unexpected error posting tweet for {username} (handle: {credentials['x_username']}): {e}", exc_info=True) logging.error(f"Unexpected error posting tweet for {username} (handle: {credentials['x_username']}): {e}", exc_info=True)
return False return False

Loading…
Cancel
Save