fix
This commit is contained in:
+35
-1
@@ -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']
|
||||||
|
|||||||
+21
-22
@@ -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
|
||||||
|
|||||||
+4
-3
@@ -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)
|
||||||
|
|
||||||
|
|||||||
+26
-20
@@ -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"What’s your take on {theme}? Let’s talk!"
|
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():
|
def main():
|
||||||
global is_posting
|
global is_posting
|
||||||
logging.info("***** X Poster Launched *****")
|
logging.info("***** X Poster Launched *****")
|
||||||
for author in AUTHORS:
|
|
||||||
# Check if the author can post before generating the tweet
|
# Get next available author using round-robin
|
||||||
can_post, remaining, reset = check_author_rate_limit(author)
|
author = get_next_author_round_robin()
|
||||||
if not can_post:
|
if not author:
|
||||||
reset_time = datetime.fromtimestamp(reset, tz=timezone.utc).strftime('%Y-%m-%d %H:%M:%S') if reset else "Unknown"
|
logging.info("No authors available due to rate limits")
|
||||||
logging.info(f"Skipping engagement tweet for {author['username']} due to rate limit. Remaining: {remaining}, Reset at: {reset_time}")
|
return random.randint(600, 1800)
|
||||||
continue
|
|
||||||
|
|
||||||
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)
|
||||||
|
|||||||
Reference in New Issue
Block a user