mobile-mcp-ai 2.3.1__py3-none-any.whl → 2.3.3__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.
@@ -813,6 +813,97 @@ class BasicMobileToolsLite:
813
813
  except Exception as e:
814
814
  return {"success": False, "message": f"❌ 断言失败: {e}"}
815
815
 
816
+ def close_ad(self, keywords: Optional[List[str]] = None, max_attempts: int = 3) -> Dict:
817
+ """关闭广告弹窗
818
+
819
+ 自动检测并点击广告关闭按钮,支持多种关闭方式:
820
+ 1. 文本匹配:关闭、跳过、Skip、Close 等
821
+ 2. 特殊符号:×、X、✕ 等
822
+ 3. content-desc 匹配
823
+
824
+ Args:
825
+ keywords: 自定义关键词列表,默认使用内置关键词
826
+ max_attempts: 最大尝试次数,默认3次(处理多层弹窗)
827
+
828
+ Returns:
829
+ 关闭结果,包含关闭的广告数量
830
+ """
831
+ # 默认关键词(按优先级排序)
832
+ default_keywords = [
833
+ '关闭', '跳过', 'Skip', 'Close', 'close',
834
+ '×', 'X', '✕', '╳',
835
+ '我知道了', '稍后再说', '不再提示', '取消',
836
+ '知道了', '好的', '确定',
837
+ 'Later', 'No thanks', 'Not now', 'Dismiss'
838
+ ]
839
+
840
+ search_keywords = keywords if keywords else default_keywords
841
+ closed_count = 0
842
+ closed_items = []
843
+
844
+ try:
845
+ for attempt in range(max_attempts):
846
+ found_in_this_round = False
847
+
848
+ for keyword in search_keywords:
849
+ try:
850
+ if self._is_ios():
851
+ ios_client = self._get_ios_client()
852
+ if ios_client and hasattr(ios_client, 'wda'):
853
+ # iOS: 尝试 name 和 label
854
+ elem = ios_client.wda(name=keyword)
855
+ if not elem.exists:
856
+ elem = ios_client.wda(label=keyword)
857
+ if not elem.exists:
858
+ elem = ios_client.wda(nameContains=keyword)
859
+
860
+ if elem.exists:
861
+ elem.click()
862
+ time.sleep(0.5)
863
+ closed_count += 1
864
+ closed_items.append(keyword)
865
+ found_in_this_round = True
866
+ break
867
+ else:
868
+ # Android: 尝试 text 和 content-desc
869
+ elem = self.client.u2(text=keyword)
870
+ if not elem.exists(timeout=0.2):
871
+ elem = self.client.u2(textContains=keyword)
872
+ if not elem.exists(timeout=0.2):
873
+ elem = self.client.u2(description=keyword)
874
+ if not elem.exists(timeout=0.2):
875
+ elem = self.client.u2(descriptionContains=keyword)
876
+
877
+ if elem.exists(timeout=0.2):
878
+ elem.click()
879
+ time.sleep(0.5)
880
+ closed_count += 1
881
+ closed_items.append(keyword)
882
+ found_in_this_round = True
883
+ break
884
+ except Exception:
885
+ continue
886
+
887
+ if not found_in_this_round:
888
+ # 这一轮没找到广告,退出
889
+ break
890
+
891
+ if closed_count > 0:
892
+ return {
893
+ "success": True,
894
+ "closed_count": closed_count,
895
+ "closed_items": closed_items,
896
+ "message": f"✅ 已关闭 {closed_count} 个广告弹窗: {', '.join(closed_items)}"
897
+ }
898
+ else:
899
+ return {
900
+ "success": True,
901
+ "closed_count": 0,
902
+ "message": "✅ 未发现广告弹窗(或已全部关闭)"
903
+ }
904
+ except Exception as e:
905
+ return {"success": False, "message": f"❌ 关闭广告失败: {e}"}
906
+
816
907
  # ==================== 脚本生成 ====================
817
908
 
818
909
  def get_operation_history(self, limit: Optional[int] = None) -> Dict:
@@ -548,3 +548,4 @@ class IOSClientWDA:
548
548
 
549
549
 
550
550
 
551
+
@@ -380,10 +380,12 @@ class MobileClient:
380
380
  break
381
381
 
382
382
  if not found:
383
- # 🎯 定位失败,自动使用Cursor AI视觉识别(截图分析)
384
- print(f" ⚠️ 元素'{ref}'未找到,自动使用Cursor AI视觉识别(截图分析)...", file=sys.stderr)
383
+ # 🎯 定位失败,提示用户
384
+ # 注意:CursorVisionHelper 是实验性功能,当前版本建议使用 MCP 方式
385
+ print(f" ⚠️ 元素'{ref}'未找到", file=sys.stderr)
385
386
  try:
386
387
  from .locator.cursor_vision_helper import CursorVisionHelper
388
+ print(f" 🔍 尝试使用Cursor AI视觉识别...", file=sys.stderr)
387
389
  cursor_helper = CursorVisionHelper(self)
388
390
  # 🎯 传递 auto_analyze=True,自动创建请求文件并等待结果
389
391
  cursor_result = await cursor_helper.analyze_with_cursor(element, auto_analyze=True)
@@ -415,12 +417,17 @@ class MobileClient:
415
417
  # 其他情况,抛出异常
416
418
  screenshot_path = cursor_result.get('screenshot_path', 'unknown') if cursor_result else 'unknown'
417
419
  raise ValueError(f"Cursor AI分析失败: {screenshot_path}")
420
+ except ImportError:
421
+ # CursorVisionHelper 模块不存在,跳过视觉识别
422
+ print(f" 💡 提示:建议使用 MCP 方式调用,Cursor AI 会自动进行视觉识别", file=sys.stderr)
418
423
  except ValueError as ve:
419
424
  if "Cursor AI" in str(ve):
420
425
  raise ve
421
426
  print(f" ⚠️ Cursor视觉识别失败: {ve}", file=sys.stderr)
427
+ except Exception as e:
428
+ print(f" ⚠️ 视觉识别异常: {e}", file=sys.stderr)
422
429
 
423
- raise ValueError(f"无法找到元素: {ref}(已等待3秒,并尝试Cursor视觉识别,可能元素不存在)")
430
+ raise ValueError(f"无法找到元素: {ref}(建议使用 MCP 方式,Cursor AI 会自动进行视觉识别)")
424
431
 
425
432
  # 验证点击(可选)
426
433
  page_changed = False
@@ -818,29 +825,34 @@ class MobileClient:
818
825
  return {"success": False, "reason": str(e)}
819
826
 
820
827
  # Android平台
821
- # 🎯 优先使用智能启动(推荐)
828
+ # 🎯 尝试使用智能启动(如果模块存在)
822
829
  if smart_wait:
823
- from .smart_app_launcher import SmartAppLauncher
824
- launcher = SmartAppLauncher(self)
825
- # 优化:快速模式,最多3秒
826
- smart_wait_time = min(wait_time, 3)
827
-
828
- # 🎯 从环境变量读取是否自动关闭广告(默认True)
829
- import os
830
- auto_close_ads = os.environ.get('AUTO_CLOSE_ADS', 'true').lower() in ['true', '1', 'yes']
831
-
832
- result = await launcher.launch_with_smart_wait(
833
- package_name,
834
- max_wait=smart_wait_time,
835
- auto_close_ads=auto_close_ads
836
- )
837
-
838
- # 打印截图路径(供Cursor AI查看验证)
839
- if result.get('screenshot_path'):
840
- print(f"\n📸 启动截图已保存: {result['screenshot_path']}", file=sys.stderr)
841
- print(f"💡 提示: 请查看截图确认App是否已正确进入主页", file=sys.stderr)
842
-
843
- return result
830
+ try:
831
+ from .smart_app_launcher import SmartAppLauncher
832
+ launcher = SmartAppLauncher(self)
833
+ # 优化:快速模式,最多3
834
+ smart_wait_time = min(wait_time, 3)
835
+
836
+ # 🎯 从环境变量读取是否自动关闭广告(默认True)
837
+ import os
838
+ auto_close_ads = os.environ.get('AUTO_CLOSE_ADS', 'true').lower() in ['true', '1', 'yes']
839
+
840
+ result = await launcher.launch_with_smart_wait(
841
+ package_name,
842
+ max_wait=smart_wait_time,
843
+ auto_close_ads=auto_close_ads
844
+ )
845
+
846
+ # 打印截图路径(供Cursor AI查看验证)
847
+ if result.get('screenshot_path'):
848
+ print(f"\n📸 启动截图已保存: {result['screenshot_path']}", file=sys.stderr)
849
+ print(f"💡 提示: 请查看截图确认App是否已正确进入主页", file=sys.stderr)
850
+
851
+ return result
852
+ except ImportError:
853
+ # SmartAppLauncher 模块不存在,使用传统方式
854
+ print(f" 💡 智能启动模块未安装,使用传统启动方式", file=sys.stderr)
855
+ # 继续执行下面的传统方式
844
856
 
845
857
  # 传统方式(快速启动,不等待加载)
846
858
  print(f" 📱 启动App: {package_name}", file=sys.stderr)
@@ -379,6 +379,32 @@ class MobileMCPServer:
379
379
  }
380
380
  ))
381
381
 
382
+ tools.append(Tool(
383
+ name="mobile_close_ad",
384
+ description="📢 关闭广告弹窗(自动检测并点击关闭按钮)\n\n"
385
+ "🎯 自动检测以下关闭方式:\n"
386
+ "- 文本:关闭、跳过、Skip、Close、我知道了、稍后再说\n"
387
+ "- 符号:×、X、✕ 等\n"
388
+ "- 无障碍描述(content-desc)\n\n"
389
+ "💡 支持多层弹窗,最多尝试3次\n"
390
+ "✅ 比视觉识别更准确,推荐使用!",
391
+ inputSchema={
392
+ "type": "object",
393
+ "properties": {
394
+ "keywords": {
395
+ "type": "array",
396
+ "items": {"type": "string"},
397
+ "description": "自定义关键词列表(可选,默认使用内置关键词)"
398
+ },
399
+ "max_attempts": {
400
+ "type": "number",
401
+ "description": "最大尝试次数(可选,默认3次,用于处理多层弹窗)"
402
+ }
403
+ },
404
+ "required": []
405
+ }
406
+ ))
407
+
382
408
  # ==================== pytest 脚本生成 ====================
383
409
  tools.append(Tool(
384
410
  name="mobile_get_operation_history",
@@ -514,6 +540,13 @@ class MobileMCPServer:
514
540
  result = self.tools.assert_text(arguments["text"])
515
541
  return [TextContent(type="text", text=self.format_response(result))]
516
542
 
543
+ elif name == "mobile_close_ad":
544
+ result = self.tools.close_ad(
545
+ arguments.get("keywords"),
546
+ arguments.get("max_attempts", 3)
547
+ )
548
+ return [TextContent(type="text", text=self.format_response(result))]
549
+
517
550
  # 脚本生成
518
551
  elif name == "mobile_get_operation_history":
519
552
  result = self.tools.get_operation_history(arguments.get("limit"))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mobile-mcp-ai
3
- Version: 2.3.1
3
+ Version: 2.3.3
4
4
  Summary: 移动端自动化 MCP Server - 支持 Android/iOS,AI 功能可选(基础工具不需要 AI)
5
5
  Home-page: https://github.com/test111ddff-hash/mobile-mcp-ai
6
6
  Author: douzi
@@ -1,25 +1,25 @@
1
1
  mobile_mcp/__init__.py,sha256=sQJZTL_sxQFzmcS7jOtS2AHCfUySz40vhX96N6u1qy4,816
2
2
  mobile_mcp/config.py,sha256=yaFLAV4bc2wX0GQPtZDo7OYF9E88tXV-av41fQsJwK4,4480
3
3
  mobile_mcp/core/__init__.py,sha256=ndMy-cLAIsQDG5op7gM_AIplycqZSZPWEkec1pEhvEY,170
4
- mobile_mcp/core/basic_tools_lite.py,sha256=_D8QmVO3UAYHvQ50C4vyd_N7LubEjt6zwUhLXfZffLE,47894
4
+ mobile_mcp/core/basic_tools_lite.py,sha256=7C2Z87vG69fqmR0FiOC3qxshdo9Sjl5jhADiIFTC8uk,52023
5
5
  mobile_mcp/core/device_manager.py,sha256=PX3-B5bJFnKNt6C8fT7FSY8JwD-ngZ3toF88bcOV9qA,8766
6
6
  mobile_mcp/core/dynamic_config.py,sha256=Ja1n1pfb0HspGByqk2_A472mYVniKmGtNEWyjUjmgK8,9811
7
- mobile_mcp/core/ios_client_wda.py,sha256=aE-j79hqIOF5_9LdEZ7hF8Fwm0pPwXRITsfRW9c9hhE,18751
7
+ mobile_mcp/core/ios_client_wda.py,sha256=St6nOeXW0wiolLm6iQ2Etuwr1cvzwnlnYwGUxA3-JD4,18752
8
8
  mobile_mcp/core/ios_device_manager_wda.py,sha256=A44glqI-24un7qST-E3w6BQD8mV92YVUbxy4rLlTScY,11264
9
- mobile_mcp/core/mobile_client.py,sha256=o8DMIspGhyo8U3HRb5C8I9i3tlh7M4NAe281I0oPc0M,61969
9
+ mobile_mcp/core/mobile_client.py,sha256=lmscB8-osGh_ngVG9XdNGe6fNJLsAwNI5qUSobE-Ln8,62964
10
10
  mobile_mcp/core/utils/__init__.py,sha256=RhMMsPszmEn8Q8GoNufypVSHJxyM9lio9U6jjpnuoPI,378
11
11
  mobile_mcp/core/utils/logger.py,sha256=XXQAHUwT1jc70pq_tYFmL6f_nKrFlYm3hcgl-5RYRg0,3402
12
12
  mobile_mcp/core/utils/operation_history_manager.py,sha256=gi8S8HJAMqvkUrY7_-kVbko3Xt7c4GAUziEujRd-N-Y,4792
13
13
  mobile_mcp/core/utils/smart_wait.py,sha256=PvKXImfN9Irru3bQJUjf4FLGn8LjY2VLzUNEl-i7xLE,8601
14
14
  mobile_mcp/mcp_tools/__init__.py,sha256=xkro8Rwqv_55YlVyhh-3DgRFSsLE3h1r31VIb3bpM6E,143
15
- mobile_mcp/mcp_tools/mcp_server.py,sha256=-1nXie3q4co9bAPhKI7QAO-hC4zflB-iEqVhozshP4w,24353
15
+ mobile_mcp/mcp_tools/mcp_server.py,sha256=pAph7z6ERezoK3aAVqc8OkZ7sPllQhPPsIW1HlDC12g,25892
16
16
  mobile_mcp/utils/__init__.py,sha256=8EH0i7UGtx1y_j_GEgdN-cZdWn2sRtZSEOLlNF9HRnY,158
17
17
  mobile_mcp/utils/logger.py,sha256=Sqq2Nr0Y4p03erqcrbYKVPCGiFaNGHMcE_JwCkeOfU4,3626
18
18
  mobile_mcp/utils/xml_formatter.py,sha256=uwTRb3vLbqhT8O-udzWT7s7LsV-DyDUz2DkofD3hXOE,4556
19
19
  mobile_mcp/utils/xml_parser.py,sha256=QhL8CWbdmNDzmBLjtx6mEnjHgMFZzJeHpCL15qfXSpI,3926
20
- mobile_mcp_ai-2.3.1.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
21
- mobile_mcp_ai-2.3.1.dist-info/METADATA,sha256=kA5qxvfDBVPeAXncNFCIFA9gTuecrIIr_VKxNrML-c4,9705
22
- mobile_mcp_ai-2.3.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
- mobile_mcp_ai-2.3.1.dist-info/entry_points.txt,sha256=KB_FglozgPHBprSM1vFbIzGyheFuHFmGanscRdMJ_8A,68
24
- mobile_mcp_ai-2.3.1.dist-info/top_level.txt,sha256=lLm6YpbTv855Lbh8BIA0rPxhybIrvYUzMEk9OErHT94,11
25
- mobile_mcp_ai-2.3.1.dist-info/RECORD,,
20
+ mobile_mcp_ai-2.3.3.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
21
+ mobile_mcp_ai-2.3.3.dist-info/METADATA,sha256=Ur70uKGsYUu_Ibg6jZ9XLpdx3bod8Ir_pLXSoGe6KD4,9705
22
+ mobile_mcp_ai-2.3.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
+ mobile_mcp_ai-2.3.3.dist-info/entry_points.txt,sha256=KB_FglozgPHBprSM1vFbIzGyheFuHFmGanscRdMJ_8A,68
24
+ mobile_mcp_ai-2.3.3.dist-info/top_level.txt,sha256=lLm6YpbTv855Lbh8BIA0rPxhybIrvYUzMEk9OErHT94,11
25
+ mobile_mcp_ai-2.3.3.dist-info/RECORD,,