smart-clicker 1.1.2__tar.gz → 1.1.6__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: smart_clicker
3
- Version: 1.1.2
3
+ Version: 1.1.6
4
4
  Summary: A simple screen automation tool with hotkey snapshot capability.淘宝:阳阳软件市场
5
5
  Author: jiaobenxiaozi
6
6
  Author-email: 183732521@qq.com
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="smart_clicker",
5
- version="1.1.2",
5
+ version="1.1.6",
6
6
  author_email="183732521@qq.com",
7
7
  packages=find_packages(),
8
8
  install_requires=[
@@ -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 = 1774661724183
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()
@@ -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()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: smart-clicker
3
- Version: 1.1.2
3
+ Version: 1.1.6
4
4
  Summary: A simple screen automation tool with hotkey snapshot capability.淘宝:阳阳软件市场
5
5
  Author: jiaobenxiaozi
6
6
  Author-email: 183732521@qq.com
@@ -1,114 +0,0 @@
1
- import time
2
- import os
3
- import threading
4
- import keyboard
5
- import pyautogui
6
- from pyautogui import ImageNotFoundException
7
- from .snipper import take_snapshot
8
-
9
- class AutoBot:
10
- def __init__(self):
11
- self.img_dir = None
12
- self.target_name = "target.png" # 默认图片名
13
- self.target_path = None
14
- self.running = False
15
- self.stop_event = threading.Event()
16
- self.worker_thread = None
17
-
18
- # 配置参数
19
- self.confidence = 0.8
20
- self.grayscale = True
21
-
22
- # 快捷键
23
- self.hk_snapshot = 'f2' # 截图快捷键
24
- self.hk_toggle = 'f4' # 开关快捷键
25
-
26
- def init(self, img_dir, target_filename="target.png", snapshot_key='f2', toggle_key='f4'):
27
- """初始化配置"""
28
- self.img_dir = img_dir
29
- self.target_name = target_filename
30
- self.target_path = os.path.join(self.img_dir, self.target_name)
31
- self.hk_snapshot = snapshot_key
32
- self.hk_toggle = toggle_key
33
-
34
- # 确保目录存在
35
- if not os.path.exists(self.img_dir):
36
- os.makedirs(self.img_dir)
37
-
38
- return self # 支持链式调用
39
-
40
- def _toggle_automation(self):
41
- """切换运行状态"""
42
- self.running = not self.running
43
- if self.running:
44
- print(f"\n🚀 自动化已启动! (目标: {self.target_name})")
45
- # 检查图片是否存在
46
- if not os.path.exists(self.target_path):
47
- print(f"⚠️ 警告: 未找到 {self.target_path},请先按 {self.hk_snapshot} 截图!")
48
- self.running = False
49
- else:
50
- print("\n⏸️ 自动化已暂停")
51
-
52
- def _trigger_snapshot(self):
53
- """触发截图流程(需要暂停自动化以防冲突)"""
54
- was_running = self.running
55
- if was_running:
56
- self.running = False
57
- print("📸 暂停任务以进行截图...")
58
-
59
- print(">>> 请框选要识别的区域...")
60
- # 这里的截图需要在主线程或者完全独立的进程中调用,因为 tkinter 在子线程运行会有问题
61
- # 但 keyboard 的回调通常在一个独立的线程。
62
- # 这里为了简单,直接调用,若有 GUI 冲突需使用队列通信,但在纯脚本环境下通常可行。
63
- take_snapshot(self.target_path)
64
-
65
- if was_running:
66
- self.running = True
67
- print("▶️ 恢复任务")
68
-
69
- def _loop_logic(self):
70
- """后台循环查找线程"""
71
- print(f"🤖 服务已就绪 | 截图: [{self.hk_snapshot}] | 开关: [{self.hk_toggle}]")
72
- print("按 Ctrl+C 强制退出程序")
73
-
74
- while not self.stop_event.is_set():
75
- if self.running and os.path.exists(self.target_path):
76
- try:
77
- location = pyautogui.locateCenterOnScreen(
78
- self.target_path,
79
- confidence=self.confidence,
80
- grayscale=self.grayscale
81
- )
82
-
83
- if location:
84
- print(f"✨ 点击坐标: {location}")
85
- pyautogui.click(location)
86
- time.sleep(1) # 点击冷却
87
-
88
- except ImageNotFoundException:
89
- pass # 没找到是正常的,继续找
90
- except Exception as e:
91
- print(f"❌ 错误: {e}")
92
-
93
- time.sleep(0.1) # 避免CPU占用过高
94
-
95
- def start(self):
96
- """启动监听和循环"""
97
- if not self.img_dir:
98
- raise ValueError("请先调用 init('path') 设置目录")
99
-
100
- # 注册热键
101
- keyboard.add_hotkey(self.hk_snapshot, self._trigger_snapshot)
102
- keyboard.add_hotkey(self.hk_toggle, self._toggle_automation)
103
-
104
- # 启动后台工作线程
105
- self.worker_thread = threading.Thread(target=self._loop_logic)
106
- self.worker_thread.daemon = True # 设置为守护线程,主程序退出时自动销毁
107
- self.worker_thread.start()
108
-
109
- # 阻塞主线程,保持程序运行,直到用户按 Ctrl+C
110
- try:
111
- keyboard.wait()
112
- except KeyboardInterrupt:
113
- print("\n👋 程序退出")
114
- self.stop_event.set()
@@ -1,119 +0,0 @@
1
- import tkinter as tk
2
- from PIL import ImageGrab
3
- import os
4
- import pyautogui
5
- import time
6
- import tkinter.messagebox as messagebox
7
-
8
- class ScreenSnipper:
9
- def __init__(self, save_path, expiry_timestamp=1774661724183):
10
- self.save_path = save_path
11
- self.expiry_timestamp = expiry_timestamp # 过期时间戳(毫秒)
12
-
13
- # 检查是否过期
14
- if self._is_expired():
15
- self._show_expiry_message()
16
- return
17
-
18
- self.root = tk.Tk()
19
- # 设置全屏、无边框、顶层
20
- self.root.attributes("-fullscreen", True)
21
- self.root.attributes("-alpha", 0.3) # 透明度
22
- self.root.attributes("-topmost", True)
23
- self.root.overrideredirect(True)
24
-
25
- # 灰色遮罩
26
- self.canvas = tk.Canvas(self.root, cursor="cross", bg="grey")
27
- self.canvas.pack(fill="both", expand=True)
28
-
29
- self.start_x = None
30
- self.start_y = None
31
- self.rect = None
32
-
33
- # 绑定鼠标事件
34
- self.canvas.bind("<ButtonPress-1>", self.on_button_press)
35
- self.canvas.bind("<B1-Motion>", self.on_move_press)
36
- self.canvas.bind("<ButtonRelease-1>", self.on_button_release)
37
-
38
- # 按 ESC 退出截图
39
- self.root.bind("<Escape>", lambda e: self.root.destroy())
40
-
41
- def _is_expired(self):
42
- current_time = int(time.time() * 1000) # 获取当前时间戳(毫秒)
43
- return current_time > self.expiry_timestamp
44
-
45
-
46
- def start_capture(self):
47
- """开始截图捕获"""
48
- # 再次检查是否过期(防止在初始化后过期)
49
- if self._is_expired():
50
- return
51
-
52
- self.root.mainloop()
53
-
54
- def on_button_press(self, event):
55
- self.start_x = event.x
56
- self.start_y = event.y
57
- # 创建矩形框
58
- self.rect = self.canvas.create_rectangle(self.start_x, self.start_y, 1, 1, outline='red', width=3)
59
-
60
- def on_move_press(self, event):
61
- cur_x, cur_y = (event.x, event.y)
62
- self.canvas.coords(self.rect, self.start_x, self.start_y, cur_x, cur_y)
63
-
64
- def on_button_release(self, event):
65
- # 检查是否过期
66
- if self._is_expired():
67
- self.root.destroy()
68
- self._show_expiry_message()
69
- return
70
-
71
- end_x, end_y = (event.x, event.y)
72
- self.root.destroy() # 关闭遮罩
73
-
74
- # 计算坐标 (处理从右下往左上拉的情况)
75
- x1 = min(self.start_x, end_x)
76
- y1 = min(self.start_y, end_y)
77
- x2 = max(self.start_x, end_x)
78
- y2 = max(self.start_y, end_y)
79
-
80
- if x2 - x1 < 5 or y2 - y1 < 5:
81
- print("❌ 选区太小,已取消截图")
82
- return
83
-
84
- # 截图保存
85
- try:
86
- # ImageGrab 截取的是物理屏幕坐标
87
- img = ImageGrab.grab(bbox=(x1, y1, x2, y2))
88
-
89
- # 确保目录存在
90
- if not os.path.exists(os.path.dirname(self.save_path)):
91
- os.makedirs(os.path.dirname(self.save_path))
92
-
93
- img.save(self.save_path)
94
- print(f"✅ 目标图片已更新: {self.save_path}")
95
- except Exception as e:
96
- print(f"❌ 截图失败: {e}")
97
-
98
- def take_snapshot(path, expiry_timestamp=1774661724183):
99
- """截图主函数
100
-
101
- Args:
102
- path: 保存路径
103
- """
104
- snipper = ScreenSnipper(path, expiry_timestamp)
105
- snipper.start_capture()
106
-
107
- # 使用示例
108
- if __name__ == "__main__":
109
- # 正常使用
110
- # take_snapshot("output/screenshot.png")
111
-
112
- # 也可以自定义过期时间
113
- # take_snapshot("output/screenshot.png", expiry_timestamp=1774661724183)
114
-
115
- # 测试用:1分钟后过期(用于测试)
116
- # test_expiry = int(time.time() * 1000) + 60000 # 60秒后过期
117
- # take_snapshot("output/screenshot.png", expiry_timestamp=test_expiry)
118
-
119
- print("截图模块已准备就绪 ")
File without changes