qrpa 1.0.10__py3-none-any.whl → 1.0.11__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.

Potentially problematic release.


This version of qrpa might be problematic. Click here for more details.

qrpa/RateLimitedSender.py CHANGED
@@ -6,15 +6,13 @@ from datetime import datetime
6
6
  import traceback
7
7
 
8
8
  class RateLimitedSender:
9
- def __init__(self, sender_func, interval=60, max_batch_size=10):
9
+ def __init__(self, sender_func, interval=60):
10
10
  """
11
11
  :param sender_func: 实际的发送函数,参数是字符串消息
12
- :param interval: 发送间隔(秒)
13
- :param max_batch_size: 队列消息条数超过此值时立即发送
12
+ :param interval: 最短发送间隔(秒)
14
13
  """
15
14
  self.sender_func = sender_func
16
15
  self.interval = interval
17
- self.max_batch_size = max_batch_size
18
16
  self.queue = deque()
19
17
  self.lock = threading.Lock()
20
18
  self.last_send_time = 0
@@ -26,9 +24,6 @@ class RateLimitedSender:
26
24
  """添加消息到队列(非阻塞)"""
27
25
  with self.lock:
28
26
  self.queue.append(message)
29
- # 如果超过批量上限,立即发送
30
- if len(self.queue) >= self.max_batch_size:
31
- self._flush()
32
27
 
33
28
  def _flush(self):
34
29
  """立即发送队列消息(内部调用)"""
@@ -47,4 +42,4 @@ class RateLimitedSender:
47
42
  with self.lock:
48
43
  if self.queue and (time.time() - self.last_send_time >= self.interval):
49
44
  self._flush()
50
- time.sleep(1)
45
+ time.sleep(1)
qrpa/__init__.py CHANGED
@@ -10,4 +10,5 @@ from .time_utils import TimeUtils
10
10
  from .fun_file import read_dict_from_file, read_dict_from_file_ex, write_dict_to_file, write_dict_to_file_ex
11
11
  from .fun_file import get_progress_json_ex, check_progress_json_ex, done_progress_json_ex
12
12
 
13
- from .fun_web import fetch, fetch_via_iframe, find_all_iframe, full_screen_shot
13
+ from .fun_web import fetch, fetch_via_iframe, find_all_iframe, full_screen_shot
14
+ from .fun_win import *
qrpa/fun_base.py CHANGED
@@ -49,8 +49,7 @@ def send_exception(msg=None):
49
49
 
50
50
  send_exception._wx_sender = RateLimitedSender(
51
51
  sender_func=wxwork_bot_send,
52
- interval=30, # 60 秒发一次
53
- max_batch_size=5 # 累积 5 条立即发
52
+ interval=30, # 10 秒发一次
54
53
  )
55
54
 
56
55
  # 构造异常消息
qrpa/fun_win.py CHANGED
@@ -1,7 +1,58 @@
1
1
  import os
2
2
  import win32com.client
3
+ import winreg
3
4
 
4
- from .fun_base import log
5
+ import requests, subprocess, time
6
+ from contextlib import contextmanager
7
+
8
+ from .fun_base import log, create_file_path
9
+
10
+ default_chrome_user_data = 'D:\chrome_user_data'
11
+
12
+ def set_chrome_system_path():
13
+ path = os.path.dirname(find_software_install_path('chrome'))
14
+ add_to_system_path(path)
15
+
16
+ def add_to_system_path(path: str, scope: str = "user"):
17
+ """
18
+ 将指定路径添加到系统环境变量 Path 中
19
+ :param path: 要添加的路径(应为绝对路径)
20
+ :param scope: 'user' 表示用户变量,'system' 表示系统变量(需要管理员权限)
21
+ """
22
+ if not os.path.isabs(path):
23
+ raise ValueError("必须提供绝对路径")
24
+
25
+ path = os.path.normpath(path)
26
+
27
+ if scope == "user":
28
+ root = winreg.HKEY_CURRENT_USER
29
+ subkey = r"Environment"
30
+ elif scope == "system":
31
+ root = winreg.HKEY_LOCAL_MACHINE
32
+ subkey = r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
33
+ else:
34
+ raise ValueError("scope 参数必须是 'user' 或 'system'")
35
+
36
+ try:
37
+ with winreg.OpenKey(root, subkey, 0, winreg.KEY_READ | winreg.KEY_WRITE) as key:
38
+ current_path, _ = winreg.QueryValueEx(key, "Path")
39
+ paths = current_path.split(";")
40
+
41
+ if path in paths:
42
+ print("路径已存在于 Path 中,无需添加: ", path)
43
+ return False
44
+
45
+ new_path = current_path + ";" + path
46
+ winreg.SetValueEx(key, "Path", 0, winreg.REG_EXPAND_SZ, new_path)
47
+ print("✅ 路径已成功添加到Path中: ", new_path)
48
+ return True
49
+
50
+ except PermissionError:
51
+ print("❌ 权限不足,系统变量修改需要管理员权限")
52
+ return False
53
+ except Exception as e:
54
+ print(f"❌ 添加失败: {e}")
55
+ return False
5
56
 
6
57
  def find_software_install_path(app_keyword: str):
7
58
  """从开始菜单或桌面查找指定软件的安装路径"""
@@ -29,3 +80,119 @@ def find_software_install_path(app_keyword: str):
29
80
 
30
81
  log(f'未能查找到{str}安装位置')
31
82
  return None
83
+
84
+ def init_chrome_env(account_list):
85
+ target = find_software_install_path('chrome')
86
+ for account in account_list:
87
+ store_key, port, *rest = account
88
+ user_data = rest[0] if rest and rest[0] else fr'{default_chrome_user_data}\{port}'
89
+ create_file_path(user_data)
90
+ args = fr'--remote-debugging-port={port} --user-data-dir="{user_data}"'
91
+ shortcut_name = f'{port}_{store_key}.lnk'
92
+ create_shortcut_on_desktop(target_path=target, arguments=args, shortcut_name=shortcut_name)
93
+
94
+ def create_shortcut_on_desktop(target_path, arguments='', shortcut_name='MyShortcut.lnk', icon_path=None):
95
+ """
96
+ 在桌面上创建快捷方式,若已存在指向相同 target + arguments 的快捷方式,则跳过创建。
97
+ """
98
+ # 获取当前用户桌面路径
99
+ desktop_path = os.path.join(os.environ['USERPROFILE'], 'Desktop')
100
+
101
+ shell = win32com.client.Dispatch('WScript.Shell')
102
+
103
+ # 检查是否已有相同目标的快捷方式
104
+ for file in os.listdir(desktop_path):
105
+ if file.lower().endswith('.lnk'):
106
+ shortcut_file = os.path.join(desktop_path, file)
107
+ shortcut = shell.CreateShortCut(shortcut_file)
108
+ if (os.path.normpath(shortcut.Targetpath) == os.path.normpath(target_path)
109
+ and shortcut.Arguments.strip() == arguments.strip()):
110
+ log("已存在指向该 target + args 的快捷方式,跳过创建")
111
+ return
112
+
113
+ # 创建新的快捷方式
114
+ shortcut_path = os.path.join(desktop_path, shortcut_name)
115
+ shortcut = shell.CreateShortCut(shortcut_path)
116
+ shortcut.Targetpath = target_path
117
+ shortcut.Arguments = arguments
118
+ shortcut.WorkingDirectory = os.path.dirname(target_path)
119
+ if icon_path:
120
+ shortcut.IconLocation = icon_path
121
+ shortcut.save()
122
+ log(f"已创建快捷方式:{shortcut_path}")
123
+
124
+ def check_chrome_dev(port=3000):
125
+ try:
126
+ url = f"http://127.0.0.1:{port}/json"
127
+ response = requests.get(url, timeout=5) # 设置超时,避免长时间等待
128
+ if response.status_code == 200:
129
+ try:
130
+ data = response.json()
131
+ if data:
132
+ # print("接口返回了数据:", data)
133
+ print("接口返回了数据:")
134
+ return True
135
+ else:
136
+ print("接口返回了空数据")
137
+ return False
138
+ except ValueError:
139
+ print("返回的不是有效的 JSON")
140
+ return False
141
+ else:
142
+ print(f"接口返回了错误状态码: {response.status_code}")
143
+ return False
144
+ except requests.RequestException as e:
145
+ print(f"请求接口时发生错误: {e}")
146
+ return False
147
+
148
+ def is_chrome_running():
149
+ try:
150
+ output = subprocess.check_output('tasklist', shell=True, text=True)
151
+ return 'chrome.exe' in output.lower()
152
+ except subprocess.CalledProcessError:
153
+ return False
154
+
155
+ @contextmanager
156
+ def get_chrome_page_v3(p, port=3000, user_data=None):
157
+ browser = context = page = None
158
+ is_custom_chrome_opened = False # 标记是否是程序自己开的浏览器
159
+
160
+ try:
161
+ if not check_chrome_dev(port):
162
+ set_chrome_system_path()
163
+ chrome_path = r'"chrome.exe"'
164
+ debugging_port = fr"--remote-debugging-port={port}"
165
+ if user_data is not None:
166
+ chrome_user_data = fr'--user-data-dir="{user_data}"'
167
+ else:
168
+ chrome_user_data = fr'--user-data-dir="{create_file_path(default_chrome_user_data)}\{port}"'
169
+
170
+ disable_webrtc = "--disable-features=WebRTC"
171
+ disable_webrtc_hw_encoder = "--disable-features=WebRTC-HW-ENCODER"
172
+ disable_webrtc_alt = "--disable-webrtc"
173
+ start_maximized = "--start-maximized"
174
+
175
+ command = f"{chrome_path} {debugging_port} {chrome_user_data} {disable_webrtc} {disable_webrtc_hw_encoder} {disable_webrtc_alt}"
176
+ subprocess.Popen(command, shell=True)
177
+ is_custom_chrome_opened = True
178
+ time.sleep(1)
179
+
180
+ browser = p.chromium.connect_over_cdp(f"http://127.0.0.1:{port}")
181
+ context = browser.contexts[0] if browser.contexts else browser.new_context()
182
+ page = context.pages[0] if context.pages else context.new_page()
183
+
184
+ yield browser, context, page
185
+
186
+ except Exception as e:
187
+ # 向上抛出错误,否则主函数感知不到错误
188
+ raise
189
+
190
+ finally:
191
+ for obj in [("page", page), ("context", context), ("browser", browser)]:
192
+ name, target = obj
193
+ try:
194
+ if target and is_custom_chrome_opened:
195
+ log(f'关闭: {name}')
196
+ target.close()
197
+ except Exception:
198
+ pass # 你可以在这里加日志记录关闭失败
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qrpa
3
- Version: 1.0.10
3
+ Version: 1.0.11
4
4
  Summary: qsir's rpa library
5
5
  Author: QSir
6
6
  Author-email: QSir <1171725650@qq.com>
@@ -1,16 +1,16 @@
1
- qrpa/RateLimitedSender.py,sha256=7-wDoVJbv-y3Z3dX85conBYNdUfpidZ7QgssdAKNlFU,1698
2
- qrpa/__init__.py,sha256=HW_Aqi4nHtb-tnPlPRq4vx5M8yr-hKGxxItGRS1Zn5A,608
1
+ qrpa/RateLimitedSender.py,sha256=hqvb1qspDFaW4RsLuVufylOrefkMgixANKeBaGEqYb4,1421
2
+ qrpa/__init__.py,sha256=OSN1T20XRX3uadTfmYrTbqETA6Y_tOca2bDodllnSkc,632
3
3
  qrpa/db_migrator.py,sha256=2VmhzcMsU0MKpl-mNCwKyV8tLTqyEysSpP27-S_rQZ8,21862
4
- qrpa/fun_base.py,sha256=j5EcC-1l6n9ZEb-xGj1sTGgQ-1zCaoSHl919iDku3Wo,3363
4
+ qrpa/fun_base.py,sha256=W_owEa8-yuGG18n9kX3remm9YTzym69ztQjtYCNMTw4,3308
5
5
  qrpa/fun_excel.py,sha256=aVSLcE3UJYnxvGi1XULM9qTbziUT7xlOcd2grnP6F3I,103615
6
6
  qrpa/fun_file.py,sha256=BInN-Iuxi8sYiJ031gXI_DzO_n170RsOf7fnpkl_etM,7100
7
7
  qrpa/fun_web.py,sha256=5QLQorAhRzMOGMRh4eCJ2UH8ZhVHvxkHwobWhmgU5qM,6286
8
- qrpa/fun_win.py,sha256=mPvojBYYvAcoXIGh_IXaNcA6x1qZAPGTRzwRvDb-WSU,1223
8
+ qrpa/fun_win.py,sha256=-LnTeocdTt72NVH6VgLdpAT9_C5oV9okeudXG6CftMA,8034
9
9
  qrpa/shein_ziniao.py,sha256=nSqqcEPh4nVQtUxUnIRzeZfTLyXywGPjPZn5pP-w57U,18309
10
10
  qrpa/time_utils.py,sha256=ef0hhbN_6b-gcnz5ETIVOoxemIMvcxGVGGIRnHnGaBo,29564
11
11
  qrpa/time_utils_example.py,sha256=shHOXKKF3QSzb0SHsNc34M61wEkkLuM30U9X1THKNS8,8053
12
12
  qrpa/wxwork.py,sha256=IeJq-1LwKCsCt_AdMSLvYrQJWNoQp_wqVtk6iqwJqf4,9200
13
- qrpa-1.0.10.dist-info/METADATA,sha256=wRmrV9leWSw8ap06Z7aIQ4ynlUte1xVm2m8B6XOhs_Q,231
14
- qrpa-1.0.10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
- qrpa-1.0.10.dist-info/top_level.txt,sha256=F6T5igi0fhXDucPPUbmmSC0qFCDEsH5eVijfVF48OFU,5
16
- qrpa-1.0.10.dist-info/RECORD,,
13
+ qrpa-1.0.11.dist-info/METADATA,sha256=TYJZ4HTibjVUL7-V67k_waJYRlohGYGVSbCWRVU3n-U,231
14
+ qrpa-1.0.11.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
+ qrpa-1.0.11.dist-info/top_level.txt,sha256=F6T5igi0fhXDucPPUbmmSC0qFCDEsH5eVijfVF48OFU,5
16
+ qrpa-1.0.11.dist-info/RECORD,,
File without changes