|
|
#!/usr/bin/env python3 |
|
|
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 |
|
|
) |
|
|
import time |
|
|
import sys |
|
|
import os |
|
|
import smtplib |
|
|
from email.mime.text import MIMEText |
|
|
from email.mime.multipart import MIMEMultipart |
|
|
from foodie_config import EMAIL_CONFIG |
|
|
|
|
|
# Configure logging |
|
|
logging.basicConfig( |
|
|
level=logging.INFO, |
|
|
format='%(asctime)s - %(levelname)s - %(message)s' |
|
|
) |
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
def send_capacity_alert(username, remaining, reset_time): |
|
|
"""Send email alert when an author's tweet capacity is full.""" |
|
|
try: |
|
|
msg = MIMEMultipart() |
|
|
msg['From'] = EMAIL_CONFIG['from_email'] |
|
|
msg['To'] = EMAIL_CONFIG['to_email'] |
|
|
msg['Subject'] = f"⚠️ X Capacity Alert: {username}" |
|
|
|
|
|
body = f""" |
|
|
X Tweet Capacity Alert! |
|
|
|
|
|
Username: {username} |
|
|
Time: {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M:%S UTC')} |
|
|
Remaining Tweets: {remaining}/17 |
|
|
Reset Time: {reset_time} |
|
|
|
|
|
This author has reached their daily tweet limit. |
|
|
The quota will reset at the time shown above. |
|
|
|
|
|
This is an automated alert from your foodie_automator system. |
|
|
""" |
|
|
|
|
|
msg.attach(MIMEText(body, 'plain')) |
|
|
|
|
|
with smtplib.SMTP(EMAIL_CONFIG['smtp_server'], EMAIL_CONFIG['smtp_port']) as server: |
|
|
server.starttls() |
|
|
server.login(EMAIL_CONFIG['smtp_username'], EMAIL_CONFIG['smtp_password']) |
|
|
server.send_message(msg) |
|
|
|
|
|
logger.info(f"Sent capacity alert email for {username}") |
|
|
except Exception as e: |
|
|
logger.error(f"Failed to send capacity alert email: {e}") |
|
|
|
|
|
def display_author_status(author): |
|
|
"""Display detailed status for a single author.""" |
|
|
username = author['username'] |
|
|
can_post, remaining, reset = check_author_rate_limit(author) |
|
|
reset_time = datetime.fromtimestamp(reset, tz=timezone.utc).strftime('%Y-%m-%d %H:%M:%S') |
|
|
|
|
|
# Only check API if no scripts are running |
|
|
if not is_any_script_running(): |
|
|
api_remaining, api_reset = get_x_rate_limit_status(author) |
|
|
if api_remaining is not None: |
|
|
api_reset_time = datetime.fromtimestamp(api_reset, tz=timezone.utc).strftime('%Y-%m-%d %H:%M:%S') |
|
|
# Use API values as primary display |
|
|
remaining = api_remaining |
|
|
reset_time = api_reset_time |
|
|
can_post = remaining > 0 |
|
|
|
|
|
status = "✅" if can_post else "❌" |
|
|
print(f"\n{status} {username}:") |
|
|
print(f" • Remaining tweets: {remaining}/17") |
|
|
print(f" • Reset time: {reset_time}") |
|
|
print(f" • Can post: {'Yes' if can_post else 'No'}") |
|
|
|
|
|
# Send alert if capacity is full |
|
|
if remaining == 0: |
|
|
send_capacity_alert(username, remaining, reset_time) |
|
|
|
|
|
# Show API status for verification |
|
|
if not is_any_script_running(): |
|
|
api_remaining, api_reset = get_x_rate_limit_status(author) |
|
|
if api_remaining is not None: |
|
|
api_reset_time = datetime.fromtimestamp(api_reset, tz=timezone.utc).strftime('%Y-%m-%d %H:%M:%S') |
|
|
print(f" • API Status: {api_remaining} remaining, resets at {api_reset_time}") |
|
|
|
|
|
def display_total_capacity(): |
|
|
"""Display total capacity across all authors.""" |
|
|
total_capacity = len(AUTHORS) * 17 |
|
|
total_used = 0 |
|
|
available_authors = 0 |
|
|
|
|
|
print("\n=== X Posting Capacity Status ===") |
|
|
print(f"Total daily capacity: {total_capacity} tweets ({len(AUTHORS)} authors × 17 tweets)") |
|
|
print("\nAuthor Status:") |
|
|
|
|
|
for author in AUTHORS: |
|
|
can_post, remaining, _ = check_author_rate_limit(author) |
|
|
# Only check API if no scripts are running |
|
|
if not is_any_script_running(): |
|
|
api_remaining, _ = get_x_rate_limit_status(author) |
|
|
if api_remaining is not None: |
|
|
remaining = api_remaining |
|
|
can_post = remaining > 0 |
|
|
|
|
|
used = 17 - remaining |
|
|
total_used += used |
|
|
if can_post: |
|
|
available_authors += 1 |
|
|
display_author_status(author) |
|
|
|
|
|
print("\n=== Summary ===") |
|
|
print(f"Total tweets used today: {total_used}") |
|
|
print(f"Total tweets remaining: {total_capacity - total_used}") |
|
|
print(f"Authors available to post: {available_authors}/{len(AUTHORS)}") |
|
|
|
|
|
# Calculate percentage used |
|
|
percent_used = (total_used / total_capacity) * 100 |
|
|
print(f"Capacity used: {percent_used:.1f}%") |
|
|
|
|
|
if percent_used > 80: |
|
|
print("\n⚠️ Warning: High capacity usage! Consider adding more authors.") |
|
|
elif percent_used > 60: |
|
|
print("\nℹ️ Note: Moderate capacity usage. Monitor usage patterns.") |
|
|
|
|
|
def main(): |
|
|
try: |
|
|
# Update system activity |
|
|
update_system_activity("check_x_capacity", "running", os.getpid()) |
|
|
|
|
|
# Display capacity status |
|
|
display_total_capacity() |
|
|
|
|
|
# Update system activity |
|
|
update_system_activity("check_x_capacity", "stopped") |
|
|
|
|
|
except KeyboardInterrupt: |
|
|
print("\nScript interrupted by user") |
|
|
update_system_activity("check_x_capacity", "stopped") |
|
|
sys.exit(0) |
|
|
except Exception as e: |
|
|
logger.error(f"Error: {e}") |
|
|
update_system_activity("check_x_capacity", "stopped") |
|
|
sys.exit(1) |
|
|
|
|
|
if __name__ == "__main__": |
|
|
main() |