# _ __________________ # | | / / _/ ____/ _/ / # | | / // // / __ / // / # | |/ // // /_/ // // /___ # |___/___/\____/___/_____/ # © Uthmn 2025 under MIT license import time import services.apt import services.mail from socket import gethostname from dotenv import load_dotenv from os import getenv from os.path import dirname, join import typer # Load environment variables from .env file dotenv_path = join(dirname(__file__), ".env") load_dotenv(dotenv_path) # FIXME: Error handling # FIXME: Add json email list that if not added will use parameters # FIXME: Handle no updates available app = typer.Typer() @app.command() def serve(): """ Checks for updates and emails hourly for security, daily for general as a daemon. """ @app.command() def now(receiver_email: str): """ Checks for apt upgrades and emails them then exits. """ generate_email(receiver_email) def generate_email(receiver_email: str): services.apt.require_root() if not services.apt.detect_apt(): print("Apt not found on this system.") exit(1) updates = services.apt.check_updates() # Get how many security updates are available security_updates = 0 for package in updates: if updates[package]["security"]: security_updates += 1 # Get how many total updates are available total_updates = len(updates) # Get how many general updates are available general_updates = total_updates - security_updates # Check if there are any updates at all if total_updates == 0: print("No updates available.") return # Get system hostname hostname = gethostname() # Create email subject subject = f"{hostname}> {security_updates} security updates, {general_updates} general updates available" # Build security updates section security_chunks = "" for package in updates: if not updates[package]["security"]: continue chunk = f''' {package} {updates[package]["installed_version"]} {updates[package]["latest_version"]} {updates[package]["repo"]} ''' security_chunks += chunk security = "" if security_updates > 0: security = f'''

Security

{security_chunks}
Package Installed Latest Repository
''' # Build general updates section general_chunks = "" for package in updates: if updates[package]["security"]: continue chunk = f''' {package} {updates[package]["installed_version"]} {updates[package]["latest_version"]} {updates[package]["repo"]} ''' general_chunks += chunk general = "" if general_updates > 0: general = f'''

General

{general_chunks}
Package Installed Latest Repository
''' html = security + general services.mail.send_email(receiver_email, subject, html) @app.callback() def callback(): """ Checks apt for upgrades and emails them """ if __name__ == "__main__": app()