try
This commit is contained in:
@@ -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,10 +273,24 @@ 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"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
for attempt in range(max_retries + 1):
|
||||||
|
try:
|
||||||
response = client.create_tweet(
|
response = client.create_tweet(
|
||||||
text=tweet,
|
text=tweet,
|
||||||
in_reply_to_tweet_id=reply_to_id
|
in_reply_to_tweet_id=reply_to_id
|
||||||
)
|
)
|
||||||
|
# Log rate limit headers on success
|
||||||
|
if hasattr(client, 'session') and client.session.last_response:
|
||||||
|
rate_limit_headers = {
|
||||||
|
"x-rate-limit-limit": client.session.last_response.headers.get("x-rate-limit-limit"),
|
||||||
|
"x-rate-limit-remaining": client.session.last_response.headers.get("x-rate-limit-remaining"),
|
||||||
|
"x-rate-limit-reset": client.session.last_response.headers.get("x-rate-limit-reset"),
|
||||||
|
}
|
||||||
|
logging.debug(f"Rate limit headers after posting for {username}: {rate_limit_headers}")
|
||||||
|
else:
|
||||||
|
logging.debug("No rate limit headers available in response")
|
||||||
|
|
||||||
# Update post counts
|
# Update post counts
|
||||||
author_count["monthly_count"] += 1
|
author_count["monthly_count"] += 1
|
||||||
author_count["daily_count"] += 1
|
author_count["daily_count"] += 1
|
||||||
@@ -278,13 +298,50 @@ def post_tweet(author, tweet, reply_to_id=None):
|
|||||||
logging.info(f"Posted tweet for {username} (handle: {credentials['x_username']}): {tweet}")
|
logging.info(f"Posted tweet for {username} (handle: {credentials['x_username']}): {tweet}")
|
||||||
logging.debug(f"Tweet ID: {response.data['id']}")
|
logging.debug(f"Tweet ID: {response.data['id']}")
|
||||||
return {"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:
|
except tweepy.TweepyException as e:
|
||||||
logging.error(f"Failed to post tweet for {username} (handle: {credentials['x_username']}): {e}")
|
logging.error(f"Failed to post tweet for {username} (handle: {credentials['x_username']}): {e}")
|
||||||
if hasattr(e, 'response') and e.response:
|
if hasattr(e, 'response') and e.response:
|
||||||
logging.error(f"Twitter API response: {e.response.text}")
|
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():
|
if "forbidden" in str(e).lower():
|
||||||
logging.error(f"Possible causes: invalid credentials, insufficient permissions, or account restrictions for {credentials['x_username']}")
|
logging.error(f"Possible causes: invalid credentials, insufficient permissions, or account restrictions for {credentials['x_username']}")
|
||||||
return False
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user