Auto Temp Cleaner Service – Python Windows Background Cleaner | FuzzuTech

 Demo :


Click Video πŸ‘‡πŸ‘‡πŸ‘‡























πŸ”§ Features:

  • Auto-clean temp folders every 10 minutes

  • Logs each cleanup cycle

  • Safe dry-run testing

  • Runs as background Windows Service

πŸ“œ Source Code: temp_cleaner_service.py


Code :


"""

temp_cleaner_service.py


Windows Service & fallback daemon to auto-clean temp files.

Dependencies: psutil, pywin32 (Windows service)

Install: pip install psutil pywin32


Windows service usage:

    python temp_cleaner_service.py install

    python temp_cleaner_service.py start

    python temp_cleaner_service.py stop

    python temp_cleaner_service.py remove


Non-Windows usage (or for quick testing):

    python temp_cleaner_service.py --run


BE CAREFUL: This script deletes files. Use dry_run=True to test first.

"""


import os

import sys

import time

import argparse

import logging

import tempfile

import shutil

from datetime import datetime, timedelta


import psutil


# Try importing win32service only when available (Windows)

IS_WINDOWS = sys.platform.startswith("win")

if IS_WINDOWS:

    try:

        import win32serviceutil

        import win32service

        import win32event

        import servicemanager

    except Exception:

        # will run in fallback mode if not properly installed

        IS_WINDOWS = False


# -------------------------

# CONFIGURATION - edit as needed

# -------------------------

DRY_RUN = True                # True => only log what WOULD be deleted

AGE_DAYS = 7                  # delete files older than AGE_DAYS

CYCLE_SECONDS = 60 * 10      # how often to scan (10 minutes)

LOG_FILE = os.path.join(os.path.dirname(__file__), "temp_cleaner.log")

# Add directories to scan (order matters) - default common temp folders

SCAN_DIRS = [

    tempfile.gettempdir(),                  # system temp

    os.path.join(os.environ.get("USERPROFILE", ""), "AppData", "Local", "Temp"),  # user temp on Windows

    r"C:\Windows\Temp",                      # Windows temp (may require admin)

]

# Exclude sub-paths (absolute or relative substrings)

EXCLUDE_PATTERNS = [

    # e.g., r"C:\Windows\Temp\ImportantBackup" 

]

# Minimum free disk percent threshold: if disk free% < this, do more aggressive cleanup

MIN_FREE_PERCENT = 10.0


# Files/Extensions to keep (case-insensitive)

KEEP_EXTENSIONS = {".sys", ".dll", ".drv"}  # avoid deleting system files

KEEP_FILENAMES = {"pagefile.sys", "hiberfil.sys"}  # exact names to keep


# Logging setup

logger = logging.getLogger("TempCleaner")

logger.setLevel(logging.DEBUG)

fh = logging.FileHandler(LOG_FILE)

fh.setLevel(logging.DEBUG)

fmt = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s")

fh.setFormatter(fmt)

logger.addHandler(fh)

# also console

ch = logging.StreamHandler()

ch.setLevel(logging.INFO)

ch.setFormatter(fmt)

logger.addHandler(ch)


# -------------------------

# Helper functions

# -------------------------

def is_excluded(path):

    for pat in EXCLUDE_PATTERNS:

        if pat and pat in path:

            return True

    return False


def should_keep(path):

    name = os.path.basename(path).lower()

    if name in KEEP_FILENAMES:

        return True

    _, ext = os.path.splitext(name)

    if ext and ext.lower() in KEEP_EXTENSIONS:

        return True

    return False


def age_in_days(path):

    try:

        mtime = os.path.getmtime(path)

        return (time.time() - mtime) / (24 * 3600)

    except Exception:

        return 0


def safe_remove_file(path):

    try:

        if DRY_RUN:

            logger.info(f"[DRY] Remove file: {path}")

            return True

        os.remove(path)

        logger.info(f"Removed file: {path}")

        return True

    except PermissionError:

        logger.warning(f"Permission denied removing file: {path}")

    except FileNotFoundError:

        logger.debug(f"File not found during remove (ignored): {path}")

    except Exception as e:

        logger.exception(f"Error removing file {path}: {e}")

    return False


def safe_remove_dir(path):

    try:

        if DRY_RUN:

            logger.info(f"[DRY] Remove dir: {path}")

            return True

        shutil.rmtree(path)

        logger.info(f"Removed directory: {path}")

        return True

    except PermissionError:

        logger.warning(f"Permission denied removing dir: {path}")

    except FileNotFoundError:

        logger.debug(f"Dir not found (ignored): {path}")

    except Exception as e:

        logger.exception(f"Error removing dir {path}: {e}")

    return False


def disk_free_percent(path):

    try:

        du = shutil.disk_usage(path)

        free_percent = du.free / du.total * 100.0

        return free_percent

    except Exception:

        return 100.0


# -------------------------

# Core cleaning logic

# -------------------------

def scan_and_clean_once():

    logger.info("Starting temp scan cycle")

    now = time.time()

    total_deleted_files = 0

    total_deleted_dirs = 0


    # detect running processes that may hold locks (optional use)

    processes = {p.pid: p.name() for p in psutil.process_iter(attrs=['name'])}


    # adjust age threshold based on disk free

    aggressive = False

    for d in SCAN_DIRS:

        try:

            free_pct = disk_free_percent(d)

            if free_pct < MIN_FREE_PERCENT:

                aggressive = True

                logger.info(f"Low disk free on {d}: {free_pct:.1f}% -> aggressive cleanup")

        except Exception:

            continue


    effective_age = AGE_DAYS if not aggressive else max(1, AGE_DAYS // 3)


    for base in SCAN_DIRS:

        if not base or not os.path.exists(base):

            logger.debug(f"Scan dir does not exist: {base}")

            continue

        logger.info(f"Scanning directory: {base}")

        for root, dirs, files in os.walk(base, topdown=False):

            if is_excluded(root):

                logger.debug(f"Excluded folder: {root}")

                continue


            # files first

            for fname in files:

                fpath = os.path.join(root, fname)

                try:

                    if is_excluded(fpath) or should_keep(fpath):

                        logger.debug(f"Keeping (excluded/keeplist): {fpath}")

                        continue

                    age_days = age_in_days(fpath)

                    if age_days >= effective_age:

                        # attempt to check if file is in use by another process (Windows)

                        locked = False

                        try:

                            # psutil can check open files for processes - expensive, so minimal check

                            # skip explicit check in default mode for performance

                            pass

                        except Exception:

                            pass

                        if safe_remove_file(fpath):

                            total_deleted_files += 1

                except Exception as e:

                    logger.exception(f"Error processing file {fpath}: {e}")


            # then directories: remove empty dirs older than threshold

            for dname in dirs:

                dpath = os.path.join(root, dname)

                try:

                    if is_excluded(dpath):

                        continue

                    # only remove if directory is empty

                    try:

                        if not os.listdir(dpath):

                            age_days = age_in_days(dpath)

                            if age_days >= effective_age:

                                if safe_remove_dir(dpath):

                                    total_deleted_dirs += 1

                    except Exception:

                        # if cannot list, skip

                        continue

                except Exception:

                    logger.exception(f"Error processing dir {dpath}")


    logger.info(f"Scan complete. Files deleted: {total_deleted_files}, Dirs deleted: {total_deleted_dirs}")

    return total_deleted_files, total_deleted_dirs


# -------------------------

# Windows Service implementation

# -------------------------

if IS_WINDOWS:

    class TempCleanerService(win32serviceutil.ServiceFramework):

        _svc_name_ = "TempCleanerService"

        _svc_display_name_ = "Temp File Auto Cleaner (FuzzuTech)"

        _svc_description_ = "Background service that periodically deletes old temp files."


        def __init__(self, args):

            win32serviceutil.ServiceFramework.__init__(self, args)

            self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)

            self.stop_requested = False


        def SvcStop(self):

            self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)

            win32event.SetEvent(self.hWaitStop)

            self.stop_requested = True


        def SvcDoRun(self):

            servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,

                                  servicemanager.PYS_SERVICE_STARTED,

                                  (self._svc_name_, ''))

            self.main()


        def main(self):

            while not self.stop_requested:

                try:

                    scan_and_clean_once()

                except Exception:

                    logger.exception("Exception in service loop")

                # wait with ability to break early

                rc = win32event.WaitForSingleObject(self.hWaitStop, CYCLE_SECONDS * 1000)

                if rc == win32event.WAIT_OBJECT_0:

                    break


# -------------------------

# Fallback simple daemon (cross-platform)

# -------------------------

def run_daemon():

    logger.info("Running in daemon/background mode (CTRL+C to stop)")

    try:

        while True:

            scan_and_clean_once()

            time.sleep(CYCLE_SECONDS)

    except KeyboardInterrupt:

        logger.info("Daemon stopped by user")


# -------------------------

# CLI / Entrypoint

# -------------------------

def parse_args():

    p = argparse.ArgumentParser()

    p.add_argument("--run", action="store_true", help="Run in foreground daemon mode (non-service)")

    p.add_argument("--dry", action="store_true", help="Run with dry_run=True for testing")

    return p.parse_args()


def main():

    global DRY_RUN

    args = parse_args()

    if args.dry:

        DRY_RUN = True

        logger.info("Running in DRY RUN mode (no deletions will happen)")


    if args.run or not IS_WINDOWS:

        run_daemon()

        return


    # on Windows: allow service commands through win32serviceutil

    if len(sys.argv) > 1:

        # pass through to win32serviceutil

        try:

            win32serviceutil.HandleCommandLine(TempCleanerService)

        except Exception as e:

            logger.exception("Service command failed")

    else:

        # if no args: start the service (when launched by SCM)

        win32serviceutil.HandleCommandLine(TempCleanerService)


if __name__ == "__main__":

    main()

Comments

Popular posts from this blog

Is This News Real or Fake? πŸ€– AI Exposes the Truth | FuzzuTech Python App Demo

🚨 Python Intrusion Detection System (IDS) – Real-Time ML + Tkinter GUI Project | FuzzuTech

Educational File Encryptor GUI (Python AES Project) | FuzzuTech