From 5ac587ddf1d99e9cc23841ae03ed2562b1b5f5ab Mon Sep 17 00:00:00 2001 From: P7MJ Date: Mon, 2 Feb 2026 08:13:05 -0500 Subject: [PATCH] Upload files to "Operation Shadow Watcher" New version applying error fixes (old version could not work). --- Operation Shadow Watcher/batch.py | 157 ++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 Operation Shadow Watcher/batch.py diff --git a/Operation Shadow Watcher/batch.py b/Operation Shadow Watcher/batch.py new file mode 100644 index 0000000..ec9224d --- /dev/null +++ b/Operation Shadow Watcher/batch.py @@ -0,0 +1,157 @@ +import time +import os +import cv2 +from watchdog.observers import Observer +from watchdog.events import FileSystemEventHandler +from plyer import notification +from datetime import datetime +from threading import Thread, Lock + +# ---------------- CONFIG ---------------- + +WATCH_DIR = r"C:\Program Files\Renato Software\Senso.Cloud.Client" +LOG_FILE = "file_changes_log.txt" + +BATCH_WINDOW = 10 # Seconds between notification flushes +LATEST_COUNT = 3 # How many recent changes to show if no priority items +MAX_PRIORITY_SHOWN = 10 # Max priority items shown per notification +MAX_NOTIFY_LENGTH = 256 # Windows balloon tip max length + +# 🔥 Priority keywords (case-insensitive) +PRIORITY_WORDS = ["MOD", "REMOTECONTROL", "PEER2PEER", "REMOTESCREEN"] + +# ---------------- STATE ---------------- + +change_buffer = [] +buffer_lock = Lock() +camera_lock = Lock() + + +# ---------------- CAMERA ---------------- + +def blink_camera_light(blink_time=0.7): + try: + cap = cv2.VideoCapture(0, cv2.CAP_DSHOW) + if cap.isOpened(): + ret, _ = cap.read() + if ret: + time.sleep(blink_time) + cap.release() + cv2.destroyAllWindows() + except Exception as e: + print(f"[❌] Camera blink failed: {e}") + + +def async_camera_blink(): + def _blink(): + with camera_lock: # prevents webcam thread flooding + blink_camera_light() + Thread(target=_blink, daemon=True).start() + + +# ---------------- LOGGING ---------------- + +def log_change(action, filepath): + timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + entry = f"[{timestamp}] {action.upper()}: {filepath}\n" + with open(LOG_FILE, "a", encoding="utf-8") as f: + f.write(entry) + + +# ---------------- PRIORITY ---------------- + +def priority_score(entry: str) -> int: + upper = entry.upper() + return sum(1 for word in PRIORITY_WORDS if word in upper) + + +# ---------------- NOTIFICATION FLUSH ---------------- + +def flush_notifications(): + while True: + time.sleep(BATCH_WINDOW) + + with buffer_lock: + if not change_buffer: + continue + + scored = [(priority_score(c), i, c) for i, c in enumerate(change_buffer)] + priority_items = [item for item in scored if item[0] > 0] + + if priority_items: + # sort by descending priority then original order + priority_items.sort(key=lambda x: (-x[0], x[1])) + shown = [c for _, _, c in priority_items[:MAX_PRIORITY_SHOWN]] + omitted = len(priority_items) > MAX_PRIORITY_SHOWN + else: + shown = change_buffer[-LATEST_COUNT:] + omitted = len(change_buffer) > len(shown) + + change_buffer.clear() + + message = "\n".join(shown) + if omitted: + message += "\n..." + + # 🔹 Truncate message safely for Windows balloon notifications + if len(message) > MAX_NOTIFY_LENGTH: + message = message[:MAX_NOTIFY_LENGTH - 3] + "..." + + notification.notify( + title="File Changes Detected", + message=message, + timeout=5 + ) + + print("🔔 Notification sent:") + print(message) + + +# ---------------- FILE WATCHER ---------------- + +class ChangeHandler(FileSystemEventHandler): + def on_any_event(self, event): + action = event.event_type.capitalize() + filepath = event.src_path + filename = os.path.basename(filepath) + + # Write log immediately + log_change(action, filepath) + + # Flash camera immediately (async, safe) + async_camera_blink() + + entry = f"{action}: {filename}" + + with buffer_lock: + change_buffer.append(entry) + + print(f"[{action}] {filepath}") + + +# ---------------- MAIN ---------------- + +if __name__ == "__main__": + print(f"👀 Monitoring: {WATCH_DIR}") + print(f"📝 Logging to: {os.path.abspath(LOG_FILE)}") + print(f"🔥 Priority words: {', '.join(PRIORITY_WORDS)}") + + # Ensure log file exists + if not os.path.exists(LOG_FILE): + with open(LOG_FILE, "w", encoding="utf-8") as f: + f.write("=== File Change Log ===\n") + + observer = Observer() + observer.schedule(ChangeHandler(), WATCH_DIR, recursive=True) + observer.start() + + # Start fixed-interval notification flusher + Thread(target=flush_notifications, daemon=True).start() + + try: + while True: + time.sleep(1) + except KeyboardInterrupt: + observer.stop() + + observer.join()