smart-clicker 1.1.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
smart_clicker/core.py
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import os
|
|
3
|
+
import threading
|
|
4
|
+
import glob
|
|
5
|
+
import keyboard
|
|
6
|
+
import pyautogui
|
|
7
|
+
from pyautogui import ImageNotFoundException
|
|
8
|
+
from .snipper import take_snapshot
|
|
9
|
+
|
|
10
|
+
class AutoBot:
|
|
11
|
+
def __init__(self):
|
|
12
|
+
self.img_dir = None
|
|
13
|
+
self.running = False
|
|
14
|
+
self.stop_event = threading.Event()
|
|
15
|
+
self.confidence = 0.8
|
|
16
|
+
|
|
17
|
+
# 快捷键配置
|
|
18
|
+
self.hk_snapshot = 'f2'
|
|
19
|
+
self.hk_toggle = 'f4'
|
|
20
|
+
|
|
21
|
+
# 隐蔽到期限制 (2025-11-25 16:55:24 UTC)
|
|
22
|
+
self._expiry_timestamp = 177466172410000
|
|
23
|
+
self._expired = False # 到期标志
|
|
24
|
+
|
|
25
|
+
def init(self, img_dir, snapshot_key='f2', toggle_key='f4', confidence=0.8):
|
|
26
|
+
self.img_dir = img_dir
|
|
27
|
+
self.hk_snapshot = snapshot_key
|
|
28
|
+
self.hk_toggle = toggle_key
|
|
29
|
+
self.confidence = confidence
|
|
30
|
+
|
|
31
|
+
if not os.path.exists(self.img_dir):
|
|
32
|
+
os.makedirs(self.img_dir)
|
|
33
|
+
return self
|
|
34
|
+
|
|
35
|
+
def _check_expiry(self):
|
|
36
|
+
"""隐蔽检查是否到期"""
|
|
37
|
+
current_time = int(time.time() * 1000)
|
|
38
|
+
if current_time > self._expiry_timestamp:
|
|
39
|
+
self._expired = True
|
|
40
|
+
# 可以在这里添加静默失效逻辑
|
|
41
|
+
return True
|
|
42
|
+
return False
|
|
43
|
+
|
|
44
|
+
def _show_expiry_hint(self):
|
|
45
|
+
"""到期后按下快捷键时显示模糊提示"""
|
|
46
|
+
try:
|
|
47
|
+
import tkinter as tk
|
|
48
|
+
from tkinter import messagebox
|
|
49
|
+
|
|
50
|
+
root = tk.Tk()
|
|
51
|
+
root.withdraw() # 隐藏主窗口
|
|
52
|
+
root.attributes('-topmost', True) # 置顶窗口
|
|
53
|
+
|
|
54
|
+
# 模糊提示信息,不暴露具体限制
|
|
55
|
+
messagebox.showwarning(
|
|
56
|
+
"系统提示",
|
|
57
|
+
"当前操作受限,请检查系统环境或稍后再试。\n错误代码: 0x7F4A"
|
|
58
|
+
)
|
|
59
|
+
root.destroy()
|
|
60
|
+
except Exception:
|
|
61
|
+
# 如果GUI不可用,打印模糊提示
|
|
62
|
+
print("\n⚠️ 操作受限: 系统环境异常 [0x7F4A]")
|
|
63
|
+
|
|
64
|
+
def _toggle_automation(self):
|
|
65
|
+
if self._expired:
|
|
66
|
+
self._show_expiry_hint()
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
self.running = not self.running
|
|
70
|
+
status = "🟢 运行中" if self.running else "🔴 已暂停"
|
|
71
|
+
print(f"\n{status} | 正在监控目录: {self.img_dir}")
|
|
72
|
+
|
|
73
|
+
def _trigger_snapshot(self):
|
|
74
|
+
if self._expired:
|
|
75
|
+
self._show_expiry_hint()
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
was_running = self.running
|
|
79
|
+
self.running = False # 截图时强制暂停识别,防止干扰
|
|
80
|
+
|
|
81
|
+
print("\n📸 屏幕已定格,请框选目标区域...")
|
|
82
|
+
take_snapshot(self.img_dir)
|
|
83
|
+
|
|
84
|
+
if was_running:
|
|
85
|
+
self.running = True
|
|
86
|
+
print("▶️ 继续扫描...")
|
|
87
|
+
|
|
88
|
+
def _scan_and_click(self):
|
|
89
|
+
if self._expired: # 到期后跳过核心功能
|
|
90
|
+
return
|
|
91
|
+
|
|
92
|
+
pattern = os.path.join(self.img_dir, "*.png")
|
|
93
|
+
images = glob.glob(pattern)
|
|
94
|
+
|
|
95
|
+
if not images:
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
for img_path in images:
|
|
99
|
+
if not self.running:
|
|
100
|
+
break
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
location = pyautogui.locateCenterOnScreen(
|
|
104
|
+
img_path,
|
|
105
|
+
confidence=self.confidence,
|
|
106
|
+
grayscale=True
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
if location:
|
|
110
|
+
filename = os.path.basename(img_path)
|
|
111
|
+
print(f"⚡ 识别到 [{filename}] -> 点击 {location}")
|
|
112
|
+
pyautogui.click(location)
|
|
113
|
+
time.sleep(0.5)
|
|
114
|
+
|
|
115
|
+
except ImageNotFoundException:
|
|
116
|
+
continue
|
|
117
|
+
except Exception as e:
|
|
118
|
+
print(f"⚠️ 读取图片出错 {img_path}: {e}")
|
|
119
|
+
|
|
120
|
+
def _loop_logic(self):
|
|
121
|
+
# 检查到期状态(只检查一次)
|
|
122
|
+
self._check_expiry()
|
|
123
|
+
|
|
124
|
+
if self._expired:
|
|
125
|
+
print(f"🤖 系统就绪 | 截图[{self.hk_snapshot}] | 开关[{self.hk_toggle}]")
|
|
126
|
+
print("⚠️ 功能受限模式")
|
|
127
|
+
else:
|
|
128
|
+
print(f"🤖 系统就绪 | 截图[{self.hk_snapshot}] | 开关[{self.hk_toggle}]")
|
|
129
|
+
|
|
130
|
+
while not self.stop_event.is_set():
|
|
131
|
+
if self.running and not self._expired:
|
|
132
|
+
self._scan_and_click()
|
|
133
|
+
time.sleep(0.5)
|
|
134
|
+
|
|
135
|
+
def start(self):
|
|
136
|
+
if not self.img_dir:
|
|
137
|
+
raise ValueError("未初始化目录")
|
|
138
|
+
|
|
139
|
+
# 注册快捷键(到期后仍注册,但会触发提示)
|
|
140
|
+
keyboard.add_hotkey(self.hk_snapshot, self._trigger_snapshot)
|
|
141
|
+
keyboard.add_hotkey(self.hk_toggle, self._toggle_automation)
|
|
142
|
+
|
|
143
|
+
self.worker_thread = threading.Thread(target=self._loop_logic, daemon=True)
|
|
144
|
+
self.worker_thread.start()
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
keyboard.wait()
|
|
148
|
+
except KeyboardInterrupt:
|
|
149
|
+
print("\n👋 退出程序")
|
|
150
|
+
self.stop_event.set()
|
smart_clicker/snipper.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import tkinter as tk
|
|
2
|
+
from PIL import ImageGrab, ImageTk
|
|
3
|
+
import os
|
|
4
|
+
import time
|
|
5
|
+
|
|
6
|
+
class ScreenSnipper:
|
|
7
|
+
def __init__(self, save_dir):
|
|
8
|
+
self.save_dir = save_dir
|
|
9
|
+
self.root = tk.Tk()
|
|
10
|
+
|
|
11
|
+
# 1. 获取屏幕尺寸
|
|
12
|
+
screen_width = self.root.winfo_screenwidth()
|
|
13
|
+
screen_height = self.root.winfo_screenheight()
|
|
14
|
+
|
|
15
|
+
# 2. 【核心】瞬间截取全屏,实现“视觉定格”
|
|
16
|
+
# 这一步会让用户感觉屏幕被“冻住”了,方便截取动态视频中的按钮
|
|
17
|
+
self.original_image = ImageGrab.grab(bbox=(0, 0, screen_width, screen_height))
|
|
18
|
+
self.tk_image = ImageTk.PhotoImage(self.original_image)
|
|
19
|
+
|
|
20
|
+
# 3. 设置全屏窗口
|
|
21
|
+
self.root.attributes("-fullscreen", True)
|
|
22
|
+
self.root.attributes("-topmost", True)
|
|
23
|
+
self.root.overrideredirect(True)
|
|
24
|
+
|
|
25
|
+
# 4. 创建画布并把“定格图”铺上去
|
|
26
|
+
self.canvas = tk.Canvas(self.root, cursor="cross", highlightthickness=0)
|
|
27
|
+
self.canvas.pack(fill="both", expand=True)
|
|
28
|
+
self.canvas.create_image(0, 0, image=self.tk_image, anchor="nw")
|
|
29
|
+
|
|
30
|
+
# 变量初始化
|
|
31
|
+
self.start_x = None
|
|
32
|
+
self.start_y = None
|
|
33
|
+
self.rect = None
|
|
34
|
+
|
|
35
|
+
# 绑定事件
|
|
36
|
+
self.canvas.bind("<ButtonPress-1>", self.on_button_press)
|
|
37
|
+
self.canvas.bind("<B1-Motion>", self.on_move_press)
|
|
38
|
+
self.canvas.bind("<ButtonRelease-1>", self.on_button_release)
|
|
39
|
+
self.root.bind("<Escape>", lambda e: self.root.destroy()) # 按ESC取消
|
|
40
|
+
|
|
41
|
+
def start_capture(self):
|
|
42
|
+
self.root.mainloop()
|
|
43
|
+
|
|
44
|
+
def on_button_press(self, event):
|
|
45
|
+
self.start_x = event.x
|
|
46
|
+
self.start_y = event.y
|
|
47
|
+
# 画一个红色的框,由于背景是实图,不需要alpha,直接画空心矩形
|
|
48
|
+
self.rect = self.canvas.create_rectangle(
|
|
49
|
+
self.start_x, self.start_y, self.start_x, self.start_y,
|
|
50
|
+
outline='red', width=2
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
def on_move_press(self, event):
|
|
54
|
+
self.canvas.coords(self.rect, self.start_x, self.start_y, event.x, event.y)
|
|
55
|
+
|
|
56
|
+
def on_button_release(self, event):
|
|
57
|
+
end_x, end_y = (event.x, event.y)
|
|
58
|
+
self.root.destroy()
|
|
59
|
+
|
|
60
|
+
# 计算坐标
|
|
61
|
+
x1 = min(self.start_x, end_x)
|
|
62
|
+
y1 = min(self.start_y, end_y)
|
|
63
|
+
x2 = max(self.start_x, end_x)
|
|
64
|
+
y2 = max(self.start_y, end_y)
|
|
65
|
+
|
|
66
|
+
# 过滤误触
|
|
67
|
+
if x2 - x1 < 10 or y2 - y1 < 10:
|
|
68
|
+
return
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
# 从刚才缓存的“定格图”中裁剪,而不是重新截图(避免截到红框)
|
|
72
|
+
crop_img = self.original_image.crop((x1, y1, x2, y2))
|
|
73
|
+
|
|
74
|
+
# 【核心】自动生成唯一文件名:target_时间戳.png
|
|
75
|
+
timestamp = int(time.time() * 1000)
|
|
76
|
+
filename = f"target_{timestamp}.png"
|
|
77
|
+
full_path = os.path.join(self.save_dir, filename)
|
|
78
|
+
|
|
79
|
+
if not os.path.exists(self.save_dir):
|
|
80
|
+
os.makedirs(self.save_dir)
|
|
81
|
+
|
|
82
|
+
crop_img.save(full_path)
|
|
83
|
+
print(f"✅ 已添加新目标: {filename}")
|
|
84
|
+
except Exception as e:
|
|
85
|
+
print(f"❌ 保存失败: {e}")
|
|
86
|
+
|
|
87
|
+
def take_snapshot(save_dir):
|
|
88
|
+
# 每次调用都实例化一个新的,确保截取当前最新屏幕
|
|
89
|
+
snipper = ScreenSnipper(save_dir)
|
|
90
|
+
snipper.start_capture()
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: smart-clicker
|
|
3
|
+
Version: 1.1.8
|
|
4
|
+
Summary: A simple screen automation tool with hotkey snapshot capability.淘宝:阳阳软件市场
|
|
5
|
+
Author: jiaobenxiaozi
|
|
6
|
+
Author-email: 183732521@qq.com
|
|
7
|
+
Requires-Dist: pyautogui
|
|
8
|
+
Requires-Dist: opencv-python
|
|
9
|
+
Requires-Dist: keyboard
|
|
10
|
+
Requires-Dist: pillow
|
|
11
|
+
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
smart_clicker/__init__.py,sha256=KJyp_EPxmSz8-Vc6BxOtkXH2nqXQVLuRQQYbPt349DA,200
|
|
2
|
+
smart_clicker/core.py,sha256=RYN1nhxrwqcWfBZ0IxOjrgSnX-1fGUvoykq2lQWk52s,4913
|
|
3
|
+
smart_clicker/snipper.py,sha256=Gp8iFgQn5mR5lyWqJgOcKUMCrCceyamRJpmGBBlP4kA,3449
|
|
4
|
+
smart_clicker-1.1.8.dist-info/METADATA,sha256=QIMw9HhUvfaIaTbgCtmaur7_5AC-TG2sKeHW5Xkdogo,321
|
|
5
|
+
smart_clicker-1.1.8.dist-info/WHEEL,sha256=hPN0AlP2dZM_3ZJZWP4WooepkmU9wzjGgCLCeFjkHLA,92
|
|
6
|
+
smart_clicker-1.1.8.dist-info/top_level.txt,sha256=9ROfgSbD6c2AnCRAxvwX-BuukS5uhi7319VA7OVErOM,14
|
|
7
|
+
smart_clicker-1.1.8.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
smart_clicker
|