This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

add more logging

+79 -29
+79 -29
src/main.py
··· 1 1 import os 2 - import json 2 + import sys 3 + import subprocess 3 4 import logging 5 + import json 4 6 import requests 5 7 import magic 6 8 from datetime import datetime 7 9 from dotenv import load_dotenv 8 10 from atproto import Client, models 9 11 from atproto.exceptions import BadRequestError 10 - import sys 11 12 13 + # Ensure the script is run inside a virtual environment 12 14 if not hasattr(sys, 'real_prefix') and not (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix): 13 15 print("Error: This script must be run inside a virtual environment.") 14 16 sys.exit(1) 15 17 else: 16 18 print("Virtual environment detected.") 17 19 18 - # Define the paths 19 - BASE_DIR = os.path.abspath(os.path.dirname(__file__)) 20 + # Define paths 21 + BASE_DIR = os.path.abspath(os.path.dirname(__file__)) # /src/ 22 + REQ_PATH = os.path.abspath(os.path.join(BASE_DIR, "../requirements.txt")) # /requirements.txt 20 23 ASSETS_DIR = os.path.join(BASE_DIR, "../assets") 21 24 ENV_PATH = os.path.join(ASSETS_DIR, ".env") 22 25 JSON_PATH = os.path.join(ASSETS_DIR, "cids.json") 26 + LOG_PATH = os.path.join(BASE_DIR, "avatar_update.log") 23 27 24 28 # Configure logging 25 29 logging.basicConfig( 26 - filename="avatar_update.log", 30 + filename=LOG_PATH, 27 31 level=logging.DEBUG, 28 32 format="%(asctime)s - %(levelname)s - %(message)s", 29 33 ) ··· 34 38 logging.getLogger().addHandler(console_handler) 35 39 36 40 41 + def install_and_rerun(): 42 + """Install missing packages from requirements.txt and re-run the script.""" 43 + if os.path.exists(REQ_PATH): 44 + logging.info(f"Installing missing packages from {REQ_PATH}...") 45 + try: 46 + subprocess.check_call([sys.executable, "-m", "pip", "install", "-r", REQ_PATH]) 47 + logging.info("Packages installed successfully. Restarting script...") 48 + os.execv(sys.executable, [sys.executable] + sys.argv) 49 + except subprocess.CalledProcessError as e: 50 + logging.error(f"Failed to install packages: {e}") 51 + sys.exit(1) 52 + else: 53 + logging.error(f"requirements.txt not found at {REQ_PATH}, cannot install missing packages.") 54 + sys.exit(1) 55 + 56 + 57 + # Try to import external packages; if any are missing, install them. 58 + try: 59 + import requests 60 + import magic 61 + from atproto import Client, models 62 + from atproto.exceptions import BadRequestError 63 + from dotenv import load_dotenv 64 + except ImportError as e: 65 + logging.error(f"Missing package(s): {e}") 66 + install_and_rerun() 67 + 68 + 37 69 def ensure_https(url): 70 + """Ensure the URL starts with https://""" 38 71 if not url.startswith("http://") and not url.startswith("https://"): 39 72 return "https://" + url 40 73 if url.startswith("http://"): ··· 43 76 44 77 45 78 def is_endpoint_alive(url): 79 + """Check if the endpoint is alive by making a health check request.""" 46 80 health_url = f"{url.rstrip('/')}/xrpc/_health" 81 + logging.info(f"Checking endpoint health: {health_url}") 47 82 try: 48 83 response = requests.get(health_url, timeout=5) 49 - return response.status_code == 200 84 + if response.status_code == 200: 85 + logging.info("Endpoint is alive.") 86 + return True 87 + else: 88 + logging.warning(f"Endpoint returned status code {response.status_code}") 89 + return False 50 90 except requests.RequestException as e: 51 91 logging.error(f"Health check failed for {health_url}: {e}") 52 92 return False 53 93 54 94 55 95 def fetch_blob(did, cid, endpoint): 96 + """Fetch blob data from the given endpoint.""" 56 97 url = f"{endpoint}/xrpc/com.atproto.sync.getBlob?did={did}&cid={cid}" 98 + logging.info(f"Fetching blob: {url}") 57 99 try: 58 100 response = requests.get(url, timeout=5) 59 101 response.raise_for_status() 102 + logging.info(f"Successfully fetched blob {cid} for DID {did}.") 60 103 return response.content 61 104 except requests.RequestException as e: 62 105 logging.error(f"Failed to fetch blob {cid} for DID {did}: {e}") ··· 64 107 65 108 66 109 def get_blob_metadata(cid, did, endpoint): 110 + """Retrieve metadata for a given blob CID.""" 67 111 blob_data = fetch_blob(did, cid, endpoint) 68 112 if blob_data is None: 113 + logging.error("Blob data is empty.") 69 114 return None 70 115 71 116 mime = magic.Magic(mime=True) 72 117 mime_type = mime.from_buffer(blob_data) 73 118 size = len(blob_data) 74 119 120 + logging.info(f"Retrieved metadata - MIME Type: {mime_type}, Size: {size} bytes") 121 + 75 122 return { 76 123 "$type": "blob", 77 124 "ref": {"$link": cid}, ··· 79 126 "size": size, 80 127 } 81 128 129 + 130 + def setup_cron_job(): 131 + """Set up a cron job to run the script hourly.""" 132 + cron_job_command = f"0 * * * * {sys.executable} {os.path.abspath(__file__)} >> {LOG_PATH} 2>&1" 133 + logging.info("Setting up cron job...") 134 + 135 + try: 136 + result = subprocess.run(["crontab", "-l"], capture_output=True, text=True) 137 + cron_jobs = result.stdout.strip() if result.returncode == 0 else "" 138 + 139 + if cron_job_command in cron_jobs: 140 + logging.info("Cron job is already set.") 141 + return 142 + 143 + new_cron_jobs = cron_jobs + "\n" + cron_job_command if cron_jobs else cron_job_command 144 + subprocess.run(["crontab"], input=new_cron_jobs, text=True, check=True) 145 + logging.info("Cron job added successfully.") 146 + except Exception as e: 147 + logging.error(f"Failed to set up cron job: {e}") 148 + 149 + 82 150 def main(): 83 151 logging.info("Starting avatar update script...") 84 152 85 153 if os.path.exists(ENV_PATH): 86 154 load_dotenv(ENV_PATH) 155 + logging.info("Loaded .env file successfully.") 87 156 else: 88 157 logging.error(f"Missing .env file at {ENV_PATH}") 89 158 return ··· 94 163 did = os.getenv("DID") 95 164 96 165 if not (endpoint and handle and password and did): 97 - logging.error( 98 - "Missing environment variables. Ensure ENDPOINT, HANDLE, PASSWORD, and DID are set in .env file." 99 - ) 166 + logging.error("Missing environment variables. Check .env file.") 100 167 return 101 168 102 169 endpoint = ensure_https(endpoint) ··· 129 196 logging.error(f"Authentication failed: {e}") 130 197 return 131 198 132 - try: 133 - current_profile_record = client.app.bsky.actor.profile.get( 134 - client.me.did, "self" 135 - ) 136 - current_profile = current_profile_record.value 137 - swap_record_cid = current_profile_record.cid 138 - except BadRequestError: 139 - current_profile = swap_record_cid = None 140 - 141 - old_description = old_display_name = None 142 - if current_profile: 143 - old_description = current_profile.description 144 - old_display_name = current_profile.display_name 145 - 146 199 blob_metadata = get_blob_metadata(new_blob_cid, did, endpoint) 147 - 148 200 if blob_metadata is None: 149 - logging.error(f"Could not retrieve metadata for blob CID: {new_blob_cid}") 201 + logging.error("Blob metadata retrieval failed.") 150 202 return 151 203 152 204 try: ··· 155 207 collection=models.ids.AppBskyActorProfile, 156 208 repo=client.me.did, 157 209 rkey="self", 158 - swap_record=swap_record_cid, 159 210 record=models.AppBskyActorProfile.Record( 160 211 avatar=blob_metadata, 161 - banner=current_profile.banner if current_profile else None, 162 - description=old_description, 163 - display_name=old_display_name, 164 212 ), 165 213 ) 166 214 ) ··· 170 218 171 219 172 220 if __name__ == "__main__": 221 + setup_cron_job() 173 222 main() 223 +