Jarvis-Brain 0.1.11.11__tar.gz → 0.1.12.1__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.4
2
2
  Name: Jarvis_Brain
3
- Version: 0.1.11.11
3
+ Version: 0.1.12.1
4
4
  Summary: Jarvis brain mcp
5
5
  Requires-Python: >=3.10
6
6
  Requires-Dist: beautifulsoup4
@@ -314,6 +314,39 @@ def register_scroll_action(mcp: FastMCP, browser_manager):
314
314
  )
315
315
 
316
316
 
317
+ def register_mouse_hover(mcp: FastMCP, browser_manager):
318
+ @mcp.tool(name="scroll_action", description="将鼠标悬停在元素上【这个功能使用了Drissionpage的action行为链功能】")
319
+ async def mouse_hover(browser_port: int, tab_id: str, css_selector: str, target_element_index: int = 0) -> dict[
320
+ str, Any]:
321
+ _browser = browser_manager.get_browser(browser_port)
322
+ target_tab = _browser.get_tab(tab_id)
323
+ css_selector = css_selector
324
+ if "css:" not in css_selector:
325
+ css_selector = "css:" + css_selector
326
+ target_eles = target_tab.eles(css_selector)
327
+ target_element = target_eles[target_element_index]
328
+ try:
329
+ target_tab.actions.move_to(target_element)
330
+ target_element.hover()
331
+ hover_success = True
332
+ except Exception as e:
333
+ hover_success = False
334
+ if target_element_index > 0:
335
+ message = f"tab页:【{tab_id}】hover【{css_selector}】【index={target_element_index}】的元素 {'成功' if hover_success else '失败'} 了"
336
+ else:
337
+ message = f"tab页:【{tab_id}】hover【{css_selector}】 {'成功' if hover_success else '失败'} 了"
338
+ # else:
339
+ # message = f"tab页:【{tab_id}】传入的css_selector找到了{len(target_eles)}个元素,请确保传入的css_selector可以找到唯一的一个元素"
340
+ return dp_mcp_message_pack(
341
+ message=message,
342
+ browser_port=browser_port,
343
+ tab_id=tab_id,
344
+ css_selector=css_selector,
345
+ hoversuccess=hover_success,
346
+ extra_message="hover成功,页面可能有更新,请重新获取页面html,并重新分析页面Selector" if hover_success else ""
347
+ )
348
+
349
+
317
350
  def register_get_screenshot(mcp: FastMCP, browser_manager):
318
351
  @mcp.tool(name="get_tab_screenshot",
319
352
  description="尝试对传入tab页进行截图,并将截图压缩为1M大小png图片,会返回截图保存路径")
@@ -325,9 +358,9 @@ def register_get_screenshot(mcp: FastMCP, browser_manager):
325
358
  os.makedirs(html_source_code_local_save_path)
326
359
  timestamp = int(time.time() * 1000)
327
360
  # time.sleep(1)
328
- origin_png = target_tab.get_screenshot(as_bytes="jpg", full_page=True)
329
- compress_png = compress_image_bytes(origin_png, 0.5)
330
- image_path = os.path.join(html_source_code_local_save_path, f"{browser_port}_{tab_id}_{timestamp}.jpg")
361
+ origin_png = target_tab.get_screenshot(as_bytes="png")
362
+ compress_png = compress_image_bytes(origin_png)
363
+ image_path = os.path.join(html_source_code_local_save_path, f"{browser_port}_{tab_id}_{timestamp}.png")
331
364
  with open(image_path, "wb") as f:
332
365
  f.write(compress_png)
333
366
  return dp_mcp_message_pack(
@@ -24,6 +24,7 @@ if "TeamNode-Dp" in enabled_modules:
24
24
  # 页面交互
25
25
  register_click_action(mcp, browser_manager)
26
26
  register_scroll_action(mcp, browser_manager)
27
+ register_mouse_hover(mcp, browser_manager)
27
28
 
28
29
  if "JarvisNode" in enabled_modules:
29
30
  register_assert_waf(mcp, browser_manager)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "Jarvis_Brain" # 别人下载时用的名字,必须在 PyPI 上唯一
3
- version = "0.1.11.11"
3
+ version = "0.1.12.1"
4
4
  description = "Jarvis brain mcp"
5
5
  dependencies = [
6
6
  "fastmcp",
@@ -212,102 +212,62 @@ def btyes2Base64Img(target_byte):
212
212
  return "data:image/png;base64," + base64.b64encode(target_byte).decode()
213
213
 
214
214
 
215
- # def compress_image_bytes(input_bytes, target_size_mb=1):
216
- # """
217
- # 压缩图片字节数据到目标大小
218
- #
219
- # 参数:
220
- # input_bytes: 输入图片的字节数据
221
- # target_size_mb: 目标大小(MB),默认1MB
222
- #
223
- # 返回:
224
- # 压缩后的图片字节数据
225
- # """
226
- # target_size = target_size_mb * 1024 * 1024 # 转换为字节
227
- #
228
- # # 从字节数据打开图片
229
- # img = Image.open(io.BytesIO(input_bytes))
230
- #
231
- # # 如果是PNG或其他格式,转换为RGB
232
- # if img.mode in ('RGBA', 'LA', 'P'):
233
- # img = img.convert('RGB')
234
- #
235
- # # 初始质量设置
236
- # quality = 95
237
- #
238
- # # 先尝试压缩
239
- # output_buffer = io.BytesIO()
240
- # img.save(output_buffer, 'JPEG', quality=quality, optimize=True)
241
- # output_bytes = output_buffer.getvalue()
242
- #
243
- # # 如果文件仍然太大,逐步降低质量
244
- # while len(output_bytes) > target_size and quality > 10:
245
- # quality -= 5
246
- # output_buffer = io.BytesIO()
247
- # img.save(output_buffer, 'JPEG', quality=quality, optimize=True)
248
- # output_bytes = output_buffer.getvalue()
249
- #
250
- # # 如果降低质量还不够,尝试缩小尺寸
251
- # if len(output_bytes) > target_size:
252
- # width, height = img.size
253
- #
254
- # while len(output_bytes) > target_size and quality > 10:
255
- # # 缩小10%
256
- # width = int(width * 0.9)
257
- # height = int(height * 0.9)
258
- # img_resized = img.resize((width, height), Image.Resampling.LANCZOS)
259
- # output_buffer = io.BytesIO()
260
- # img_resized.save(output_buffer, 'JPEG', quality=quality, optimize=True)
261
- # output_bytes = output_buffer.getvalue()
262
- #
263
- # final_size = len(output_bytes) / (1024 * 1024)
264
- # # print(f"压缩完成!")
265
- # # print(f"原始大小: {len(input_bytes) / (1024 * 1024):.2f}MB")
266
- # # print(f"压缩后大小: {final_size:.2f}MB")
267
- # # print(f"最终质量: {quality}")
268
- #
269
- # return output_bytes
270
- #
271
-
215
+ def compress_image_bytes(input_bytes, target_size_mb=1):
216
+ """
217
+ 压缩图片字节数据到目标大小
272
218
 
219
+ 参数:
220
+ input_bytes: 输入图片的字节数据
221
+ target_size_mb: 目标大小(MB),默认1MB
273
222
 
274
- def compress_image_bytes(input_bytes, target_size_mb=1):
275
- target_size = target_size_mb * 1024 * 1024
223
+ 返回:
224
+ 压缩后的图片字节数据
225
+ """
226
+ target_size = target_size_mb * 1024 * 1024 # 转换为字节
276
227
 
228
+ # 从字节数据打开图片
277
229
  img = Image.open(io.BytesIO(input_bytes))
230
+
231
+ # 如果是PNG或其他格式,转换为RGB
278
232
  if img.mode in ('RGBA', 'LA', 'P'):
279
233
  img = img.convert('RGB')
280
234
 
235
+ # 初始质量设置
281
236
  quality = 95
237
+
238
+ # 先尝试压缩
282
239
  output_buffer = io.BytesIO()
283
240
  img.save(output_buffer, 'JPEG', quality=quality, optimize=True)
284
241
  output_bytes = output_buffer.getvalue()
285
242
 
286
- # 1. 优先降低质量
243
+ # 如果文件仍然太大,逐步降低质量
287
244
  while len(output_bytes) > target_size and quality > 10:
288
245
  quality -= 5
289
246
  output_buffer = io.BytesIO()
290
247
  img.save(output_buffer, 'JPEG', quality=quality, optimize=True)
291
248
  output_bytes = output_buffer.getvalue()
292
249
 
293
- # 2. 如果质量降到底(10)还是太大,开始缩小尺寸
250
+ # 如果降低质量还不够,尝试缩小尺寸
294
251
  if len(output_bytes) > target_size:
295
252
  width, height = img.size
296
253
 
297
- # 修改点:这里去掉了 'and quality > 10',改为防止图片缩得太小 (width > 100)
298
- while len(output_bytes) > target_size and width > 100:
254
+ while len(output_bytes) > target_size and quality > 10:
255
+ # 缩小10%
299
256
  width = int(width * 0.9)
300
257
  height = int(height * 0.9)
301
-
302
- # 重新调整尺寸
303
258
  img_resized = img.resize((width, height), Image.Resampling.LANCZOS)
304
-
305
259
  output_buffer = io.BytesIO()
306
- # 注意:这里继续使用当前的最低质量(10)进行保存,或者你可以适当回调一点质量
307
260
  img_resized.save(output_buffer, 'JPEG', quality=quality, optimize=True)
308
261
  output_bytes = output_buffer.getvalue()
309
262
 
263
+ final_size = len(output_bytes) / (1024 * 1024)
264
+ # print(f"压缩完成!")
265
+ # print(f"原始大小: {len(input_bytes) / (1024 * 1024):.2f}MB")
266
+ # print(f"压缩后大小: {final_size:.2f}MB")
267
+ # print(f"最终质量: {quality}")
268
+
310
269
  return output_bytes
270
+
311
271
  # todo: 大致盘一下各种判定的逻辑【以下的所有压缩比之间的差距均取“绝对值”】
312
272
  # 1. 如果requests、无头、有头获取到的压缩比之间从差距都在15%以内,则认定该页面是静态页面,此时优先使用requests请求
313
273
  # 2. 如果requests的status_code为特定的412,或者521,则判定是瑞数和jsl。[此时还有一个特点:requests的压缩比会与其他两种方式获取到的压缩比差距非常大(一两千的那种)]