mobile-mcp-ai 2.5.4__tar.gz → 2.5.5__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.
- {mobile_mcp_ai-2.5.4/mobile_mcp_ai.egg-info → mobile_mcp_ai-2.5.5}/PKG-INFO +14 -1
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/README.md +13 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/__init__.py +1 -1
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/core/basic_tools_lite.py +42 -7
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/mcp_tools/mcp_server.py +46 -7
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5/mobile_mcp_ai.egg-info}/PKG-INFO +14 -1
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/mobile_mcp_ai.egg-info/SOURCES.txt +0 -10
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/setup.py +1 -1
- mobile_mcp_ai-2.5.4/tests/test_mind_cloud_my_space.py +0 -80
- mobile_mcp_ai-2.5.4/tests/test_mind_correct.py +0 -73
- mobile_mcp_ai-2.5.4/tests/test_mind_improved.py +0 -83
- mobile_mcp_ai-2.5.4/tests/test_mind_optimized.py +0 -77
- mobile_mcp_ai-2.5.4/tests/test_open_mind.py +0 -37
- mobile_mcp_ai-2.5.4/tests/test_priority_demo.py +0 -81
- mobile_mcp_ai-2.5.4/tests/test_simple.py +0 -76
- mobile_mcp_ai-2.5.4/tests/test_/344/270/276/346/212/245.py +0 -136
- mobile_mcp_ai-2.5.4/tests/test_/345/210/207/346/215/242/350/257/255/350/250/200/345/210/260English.py +0 -158
- mobile_mcp_ai-2.5.4/tests/test_/346/265/213/350/257/225.py +0 -114
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/LICENSE +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/MANIFEST.in +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/config.py +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/core/__init__.py +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/core/device_manager.py +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/core/dynamic_config.py +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/core/ios_client_wda.py +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/core/ios_device_manager_wda.py +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/core/mobile_client.py +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/core/template_matcher.py +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/core/templates/close_buttons/auto_x_0112_151217.png +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/core/templates/close_buttons/auto_x_0112_152037.png +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/core/templates/close_buttons/auto_x_0112_152840.png +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/core/templates/close_buttons/auto_x_0112_153256.png +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/core/templates/close_buttons/auto_x_0112_154847.png +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/core/templates/close_buttons/gray_x_stock_ad.png +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/core/utils/__init__.py +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/core/utils/logger.py +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/core/utils/operation_history_manager.py +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/core/utils/smart_wait.py +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/docs/iOS_SETUP_GUIDE.md +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/mcp_tools/__init__.py +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/mobile_mcp_ai.egg-info/dependency_links.txt +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/mobile_mcp_ai.egg-info/entry_points.txt +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/mobile_mcp_ai.egg-info/not-zip-safe +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/mobile_mcp_ai.egg-info/requires.txt +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/mobile_mcp_ai.egg-info/top_level.txt +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/requirements.txt +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/setup.cfg +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/templates/close_buttons/auto_x_0112_151217.png +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/templates/close_buttons/auto_x_0112_152037.png +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/templates/close_buttons/auto_x_0112_152840.png +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/templates/close_buttons/auto_x_0112_153256.png +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/templates/close_buttons/auto_x_0112_154847.png +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/templates/close_buttons/gray_x_stock_ad.png +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/utils/__init__.py +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/utils/logger.py +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/utils/xml_formatter.py +0 -0
- {mobile_mcp_ai-2.5.4 → mobile_mcp_ai-2.5.5}/utils/xml_parser.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mobile-mcp-ai
|
|
3
|
-
Version: 2.5.
|
|
3
|
+
Version: 2.5.5
|
|
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
|
|
@@ -408,10 +408,18 @@ tidevice list
|
|
|
408
408
|
|:---:|------|------|
|
|
409
409
|
| 📋 | `mobile_list_elements` | 列出页面元素 |
|
|
410
410
|
| 📸 | `mobile_take_screenshot` | 截图 |
|
|
411
|
+
| 📸 | `mobile_screenshot_with_som` | Set-of-Mark 截图(智能标注) |
|
|
412
|
+
| 📸 | `mobile_screenshot_with_grid` | 带网格坐标的截图 |
|
|
411
413
|
| 📐 | `mobile_get_screen_size` | 屏幕尺寸 |
|
|
412
414
|
| 👆 | `mobile_click_by_text` | 文本点击 |
|
|
413
415
|
| 👆 | `mobile_click_by_id` | ID 点击 |
|
|
414
416
|
| 👆 | `mobile_click_at_coords` | 坐标点击 |
|
|
417
|
+
| 👆 | `mobile_click_by_percent` | 百分比点击 |
|
|
418
|
+
| 👆 | `mobile_click_by_som` | SoM 编号点击 |
|
|
419
|
+
| 👆 | `mobile_long_press_by_id` | ID 长按 |
|
|
420
|
+
| 👆 | `mobile_long_press_by_text` | 文本长按 |
|
|
421
|
+
| 👆 | `mobile_long_press_by_percent` | 百分比长按 |
|
|
422
|
+
| 👆 | `mobile_long_press_at_coords` | 坐标长按 |
|
|
415
423
|
| ⌨️ | `mobile_input_text_by_id` | ID 输入 |
|
|
416
424
|
| ⌨️ | `mobile_input_at_coords` | 坐标输入 |
|
|
417
425
|
| 👆 | `mobile_swipe` | 滑动 |
|
|
@@ -422,6 +430,11 @@ tidevice list
|
|
|
422
430
|
| 📦 | `mobile_list_apps` | 列出应用 |
|
|
423
431
|
| 📱 | `mobile_list_devices` | 列出设备 |
|
|
424
432
|
| 🔌 | `mobile_check_connection` | 检查连接 |
|
|
433
|
+
| 🔍 | `mobile_find_close_button` | 查找关闭按钮 |
|
|
434
|
+
| 🚫 | `mobile_close_popup` | 关闭弹窗 |
|
|
435
|
+
| 🚫 | `mobile_close_ad` | 智能关闭广告弹窗 |
|
|
436
|
+
| 🎯 | `mobile_template_close` | 模板匹配关闭弹窗 |
|
|
437
|
+
| ➕ | `mobile_template_add` | 添加 X 号模板 |
|
|
425
438
|
| ✅ | `mobile_assert_text` | 断言文本 |
|
|
426
439
|
| 📜 | `mobile_get_operation_history` | 操作历史 |
|
|
427
440
|
| 🗑️ | `mobile_clear_operation_history` | 清空历史 |
|
|
@@ -337,10 +337,18 @@ tidevice list
|
|
|
337
337
|
|:---:|------|------|
|
|
338
338
|
| 📋 | `mobile_list_elements` | 列出页面元素 |
|
|
339
339
|
| 📸 | `mobile_take_screenshot` | 截图 |
|
|
340
|
+
| 📸 | `mobile_screenshot_with_som` | Set-of-Mark 截图(智能标注) |
|
|
341
|
+
| 📸 | `mobile_screenshot_with_grid` | 带网格坐标的截图 |
|
|
340
342
|
| 📐 | `mobile_get_screen_size` | 屏幕尺寸 |
|
|
341
343
|
| 👆 | `mobile_click_by_text` | 文本点击 |
|
|
342
344
|
| 👆 | `mobile_click_by_id` | ID 点击 |
|
|
343
345
|
| 👆 | `mobile_click_at_coords` | 坐标点击 |
|
|
346
|
+
| 👆 | `mobile_click_by_percent` | 百分比点击 |
|
|
347
|
+
| 👆 | `mobile_click_by_som` | SoM 编号点击 |
|
|
348
|
+
| 👆 | `mobile_long_press_by_id` | ID 长按 |
|
|
349
|
+
| 👆 | `mobile_long_press_by_text` | 文本长按 |
|
|
350
|
+
| 👆 | `mobile_long_press_by_percent` | 百分比长按 |
|
|
351
|
+
| 👆 | `mobile_long_press_at_coords` | 坐标长按 |
|
|
344
352
|
| ⌨️ | `mobile_input_text_by_id` | ID 输入 |
|
|
345
353
|
| ⌨️ | `mobile_input_at_coords` | 坐标输入 |
|
|
346
354
|
| 👆 | `mobile_swipe` | 滑动 |
|
|
@@ -351,6 +359,11 @@ tidevice list
|
|
|
351
359
|
| 📦 | `mobile_list_apps` | 列出应用 |
|
|
352
360
|
| 📱 | `mobile_list_devices` | 列出设备 |
|
|
353
361
|
| 🔌 | `mobile_check_connection` | 检查连接 |
|
|
362
|
+
| 🔍 | `mobile_find_close_button` | 查找关闭按钮 |
|
|
363
|
+
| 🚫 | `mobile_close_popup` | 关闭弹窗 |
|
|
364
|
+
| 🚫 | `mobile_close_ad` | 智能关闭广告弹窗 |
|
|
365
|
+
| 🎯 | `mobile_template_close` | 模板匹配关闭弹窗 |
|
|
366
|
+
| ➕ | `mobile_template_add` | 添加 X 号模板 |
|
|
354
367
|
| ✅ | `mobile_assert_text` | 断言文本 |
|
|
355
368
|
| 📜 | `mobile_get_operation_history` | 操作历史 |
|
|
356
369
|
| 🗑️ | `mobile_clear_operation_history` | 清空历史 |
|
|
@@ -1587,8 +1587,14 @@ class BasicMobileToolsLite:
|
|
|
1587
1587
|
|
|
1588
1588
|
# ==================== 导航操作 ====================
|
|
1589
1589
|
|
|
1590
|
-
async def swipe(self, direction: str) -> Dict:
|
|
1591
|
-
"""滑动屏幕
|
|
1590
|
+
async def swipe(self, direction: str, y: Optional[int] = None, y_percent: Optional[float] = None) -> Dict:
|
|
1591
|
+
"""滑动屏幕
|
|
1592
|
+
|
|
1593
|
+
Args:
|
|
1594
|
+
direction: 滑动方向 (up/down/left/right)
|
|
1595
|
+
y: 左右滑动时指定的高度坐标(像素)
|
|
1596
|
+
y_percent: 左右滑动时指定的高度百分比 (0-100)
|
|
1597
|
+
"""
|
|
1592
1598
|
try:
|
|
1593
1599
|
if self._is_ios():
|
|
1594
1600
|
ios_client = self._get_ios_client()
|
|
@@ -1602,11 +1608,26 @@ class BasicMobileToolsLite:
|
|
|
1602
1608
|
|
|
1603
1609
|
center_x, center_y = width // 2, height // 2
|
|
1604
1610
|
|
|
1611
|
+
# 对于左右滑动,如果指定了 y 或 y_percent,使用指定的高度
|
|
1612
|
+
if direction in ['left', 'right']:
|
|
1613
|
+
if y_percent is not None:
|
|
1614
|
+
if not (0 <= y_percent <= 100):
|
|
1615
|
+
return {"success": False, "message": f"❌ y_percent 必须在 0-100 之间: {y_percent}"}
|
|
1616
|
+
swipe_y = int(height * y_percent / 100)
|
|
1617
|
+
elif y is not None:
|
|
1618
|
+
if not (0 <= y <= height):
|
|
1619
|
+
return {"success": False, "message": f"❌ y 坐标超出屏幕范围 (0-{height}): {y}"}
|
|
1620
|
+
swipe_y = y
|
|
1621
|
+
else:
|
|
1622
|
+
swipe_y = center_y
|
|
1623
|
+
else:
|
|
1624
|
+
swipe_y = center_y
|
|
1625
|
+
|
|
1605
1626
|
swipe_map = {
|
|
1606
1627
|
'up': (center_x, int(height * 0.8), center_x, int(height * 0.2)),
|
|
1607
1628
|
'down': (center_x, int(height * 0.2), center_x, int(height * 0.8)),
|
|
1608
|
-
'left': (int(width * 0.8),
|
|
1609
|
-
'right': (int(width * 0.2),
|
|
1629
|
+
'left': (int(width * 0.8), swipe_y, int(width * 0.2), swipe_y),
|
|
1630
|
+
'right': (int(width * 0.2), swipe_y, int(width * 0.8), swipe_y),
|
|
1610
1631
|
}
|
|
1611
1632
|
|
|
1612
1633
|
if direction not in swipe_map:
|
|
@@ -1619,9 +1640,23 @@ class BasicMobileToolsLite:
|
|
|
1619
1640
|
else:
|
|
1620
1641
|
self.client.u2.swipe(x1, y1, x2, y2, duration=0.5)
|
|
1621
1642
|
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1643
|
+
# 记录操作信息
|
|
1644
|
+
record_info = {'direction': direction}
|
|
1645
|
+
if y is not None:
|
|
1646
|
+
record_info['y'] = y
|
|
1647
|
+
if y_percent is not None:
|
|
1648
|
+
record_info['y_percent'] = y_percent
|
|
1649
|
+
self._record_operation('swipe', **record_info)
|
|
1650
|
+
|
|
1651
|
+
# 构建返回消息
|
|
1652
|
+
msg = f"✅ 滑动成功: {direction}"
|
|
1653
|
+
if direction in ['left', 'right']:
|
|
1654
|
+
if y_percent is not None:
|
|
1655
|
+
msg += f" (高度: {y_percent}% = {swipe_y}px)"
|
|
1656
|
+
elif y is not None:
|
|
1657
|
+
msg += f" (高度: {y}px)"
|
|
1658
|
+
|
|
1659
|
+
return {"success": True, "message": msg}
|
|
1625
1660
|
except Exception as e:
|
|
1626
1661
|
return {"success": False, "message": f"❌ 滑动失败: {e}"}
|
|
1627
1662
|
|
|
@@ -34,9 +34,21 @@ from pathlib import Path
|
|
|
34
34
|
from typing import Optional
|
|
35
35
|
|
|
36
36
|
# 添加项目根目录到 Python 路径
|
|
37
|
-
#
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
# 支持两种运行方式:
|
|
38
|
+
# 1. 从源码运行:__file__ 在 mcp_tools/ 目录下,往上两级到项目根目录
|
|
39
|
+
# 2. 从已安装包运行:包已安装时,mobile_mcp 应该可以直接导入
|
|
40
|
+
# 先尝试从已安装的包导入,如果失败则从源码路径导入
|
|
41
|
+
try:
|
|
42
|
+
# 尝试导入已安装的包
|
|
43
|
+
import mobile_mcp.core.mobile_client
|
|
44
|
+
import mobile_mcp.core.basic_tools_lite
|
|
45
|
+
# 如果成功,说明包已安装,不需要添加路径
|
|
46
|
+
except ImportError:
|
|
47
|
+
# 包未安装或导入失败,从源码运行
|
|
48
|
+
# __file__ 在 mcp_tools/ 目录下,往上两级到项目根目录
|
|
49
|
+
project_root = Path(__file__).parent.parent
|
|
50
|
+
if str(project_root) not in sys.path:
|
|
51
|
+
sys.path.insert(0, str(project_root))
|
|
40
52
|
|
|
41
53
|
# 尝试导入 MCP,处理可能的路径冲突
|
|
42
54
|
try:
|
|
@@ -115,8 +127,19 @@ class MobileMCPServer:
|
|
|
115
127
|
platform = self._detect_platform()
|
|
116
128
|
|
|
117
129
|
try:
|
|
118
|
-
|
|
119
|
-
|
|
130
|
+
# 尝试导入,如果失败会抛出 ImportError
|
|
131
|
+
try:
|
|
132
|
+
from mobile_mcp.core.mobile_client import MobileClient
|
|
133
|
+
from mobile_mcp.core.basic_tools_lite import BasicMobileToolsLite
|
|
134
|
+
except ImportError as import_err:
|
|
135
|
+
# 如果导入失败,尝试从源码路径导入
|
|
136
|
+
# 这通常发生在开发模式下,包未安装时
|
|
137
|
+
project_root = Path(__file__).parent.parent
|
|
138
|
+
if str(project_root) not in sys.path:
|
|
139
|
+
sys.path.insert(0, str(project_root))
|
|
140
|
+
# 再次尝试导入
|
|
141
|
+
from mobile_mcp.core.mobile_client import MobileClient
|
|
142
|
+
from mobile_mcp.core.basic_tools_lite import BasicMobileToolsLite
|
|
120
143
|
|
|
121
144
|
self.client = MobileClient(platform=platform)
|
|
122
145
|
self.tools = BasicMobileToolsLite(self.client)
|
|
@@ -480,7 +503,11 @@ class MobileMCPServer:
|
|
|
480
503
|
# ==================== 导航操作 ====================
|
|
481
504
|
tools.append(Tool(
|
|
482
505
|
name="mobile_swipe",
|
|
483
|
-
description="👆 滑动屏幕。方向:up/down/left/right"
|
|
506
|
+
description="👆 滑动屏幕。方向:up/down/left/right\n\n"
|
|
507
|
+
"💡 左右滑动时,可指定高度坐标或百分比:\n"
|
|
508
|
+
"- y: 指定高度坐标(像素)\n"
|
|
509
|
+
"- y_percent: 指定高度百分比 (0-100)\n"
|
|
510
|
+
"- 两者都未指定时,使用屏幕中心高度",
|
|
484
511
|
inputSchema={
|
|
485
512
|
"type": "object",
|
|
486
513
|
"properties": {
|
|
@@ -488,6 +515,14 @@ class MobileMCPServer:
|
|
|
488
515
|
"type": "string",
|
|
489
516
|
"enum": ["up", "down", "left", "right"],
|
|
490
517
|
"description": "滑动方向"
|
|
518
|
+
},
|
|
519
|
+
"y": {
|
|
520
|
+
"type": "integer",
|
|
521
|
+
"description": "左右滑动时指定的高度坐标(像素,0-屏幕高度)"
|
|
522
|
+
},
|
|
523
|
+
"y_percent": {
|
|
524
|
+
"type": "number",
|
|
525
|
+
"description": "左右滑动时指定的高度百分比 (0-100)"
|
|
491
526
|
}
|
|
492
527
|
},
|
|
493
528
|
"required": ["direction"]
|
|
@@ -880,7 +915,11 @@ class MobileMCPServer:
|
|
|
880
915
|
|
|
881
916
|
# 导航
|
|
882
917
|
elif name == "mobile_swipe":
|
|
883
|
-
result = await self.tools.swipe(
|
|
918
|
+
result = await self.tools.swipe(
|
|
919
|
+
arguments["direction"],
|
|
920
|
+
y=arguments.get("y"),
|
|
921
|
+
y_percent=arguments.get("y_percent")
|
|
922
|
+
)
|
|
884
923
|
return [TextContent(type="text", text=self.format_response(result))]
|
|
885
924
|
|
|
886
925
|
elif name == "mobile_press_key":
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mobile-mcp-ai
|
|
3
|
-
Version: 2.5.
|
|
3
|
+
Version: 2.5.5
|
|
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
|
|
@@ -408,10 +408,18 @@ tidevice list
|
|
|
408
408
|
|:---:|------|------|
|
|
409
409
|
| 📋 | `mobile_list_elements` | 列出页面元素 |
|
|
410
410
|
| 📸 | `mobile_take_screenshot` | 截图 |
|
|
411
|
+
| 📸 | `mobile_screenshot_with_som` | Set-of-Mark 截图(智能标注) |
|
|
412
|
+
| 📸 | `mobile_screenshot_with_grid` | 带网格坐标的截图 |
|
|
411
413
|
| 📐 | `mobile_get_screen_size` | 屏幕尺寸 |
|
|
412
414
|
| 👆 | `mobile_click_by_text` | 文本点击 |
|
|
413
415
|
| 👆 | `mobile_click_by_id` | ID 点击 |
|
|
414
416
|
| 👆 | `mobile_click_at_coords` | 坐标点击 |
|
|
417
|
+
| 👆 | `mobile_click_by_percent` | 百分比点击 |
|
|
418
|
+
| 👆 | `mobile_click_by_som` | SoM 编号点击 |
|
|
419
|
+
| 👆 | `mobile_long_press_by_id` | ID 长按 |
|
|
420
|
+
| 👆 | `mobile_long_press_by_text` | 文本长按 |
|
|
421
|
+
| 👆 | `mobile_long_press_by_percent` | 百分比长按 |
|
|
422
|
+
| 👆 | `mobile_long_press_at_coords` | 坐标长按 |
|
|
415
423
|
| ⌨️ | `mobile_input_text_by_id` | ID 输入 |
|
|
416
424
|
| ⌨️ | `mobile_input_at_coords` | 坐标输入 |
|
|
417
425
|
| 👆 | `mobile_swipe` | 滑动 |
|
|
@@ -422,6 +430,11 @@ tidevice list
|
|
|
422
430
|
| 📦 | `mobile_list_apps` | 列出应用 |
|
|
423
431
|
| 📱 | `mobile_list_devices` | 列出设备 |
|
|
424
432
|
| 🔌 | `mobile_check_connection` | 检查连接 |
|
|
433
|
+
| 🔍 | `mobile_find_close_button` | 查找关闭按钮 |
|
|
434
|
+
| 🚫 | `mobile_close_popup` | 关闭弹窗 |
|
|
435
|
+
| 🚫 | `mobile_close_ad` | 智能关闭广告弹窗 |
|
|
436
|
+
| 🎯 | `mobile_template_close` | 模板匹配关闭弹窗 |
|
|
437
|
+
| ➕ | `mobile_template_add` | 添加 X 号模板 |
|
|
425
438
|
| ✅ | `mobile_assert_text` | 断言文本 |
|
|
426
439
|
| 📜 | `mobile_get_operation_history` | 操作历史 |
|
|
427
440
|
| 🗑️ | `mobile_clear_operation_history` | 清空历史 |
|
|
@@ -65,16 +65,6 @@ templates/close_buttons/auto_x_0112_152840.png
|
|
|
65
65
|
templates/close_buttons/auto_x_0112_153256.png
|
|
66
66
|
templates/close_buttons/auto_x_0112_154847.png
|
|
67
67
|
templates/close_buttons/gray_x_stock_ad.png
|
|
68
|
-
tests/test_mind_cloud_my_space.py
|
|
69
|
-
tests/test_mind_correct.py
|
|
70
|
-
tests/test_mind_improved.py
|
|
71
|
-
tests/test_mind_optimized.py
|
|
72
|
-
tests/test_open_mind.py
|
|
73
|
-
tests/test_priority_demo.py
|
|
74
|
-
tests/test_simple.py
|
|
75
|
-
tests/test_举报.py
|
|
76
|
-
tests/test_切换语言到English.py
|
|
77
|
-
tests/test_测试.py
|
|
78
68
|
utils/__init__.py
|
|
79
69
|
utils/logger.py
|
|
80
70
|
utils/xml_formatter.py
|
|
@@ -25,7 +25,7 @@ if requirements_file.exists():
|
|
|
25
25
|
|
|
26
26
|
setup(
|
|
27
27
|
name="mobile-mcp-ai",
|
|
28
|
-
version="2.5.
|
|
28
|
+
version="2.5.5", # 支持左右滑动指定高度坐标和百分比
|
|
29
29
|
author="douzi",
|
|
30
30
|
author_email="1492994674@qq.com",
|
|
31
31
|
description="移动端自动化 MCP Server - 支持 Android/iOS,AI 功能可选(基础工具不需要 AI)",
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
测试用例: Mind云文档我的空间
|
|
5
|
-
生成时间: 2025-12-17 11:00:00
|
|
6
|
-
"""
|
|
7
|
-
import time
|
|
8
|
-
import uiautomator2 as u2
|
|
9
|
-
|
|
10
|
-
PACKAGE_NAME = "com.im30.mind"
|
|
11
|
-
|
|
12
|
-
# 广告关闭按钮关键词(可自定义)
|
|
13
|
-
AD_CLOSE_KEYWORDS = ['关闭', '跳过', 'Skip', 'Close', '×', 'X', '我知道了', '稍后再说']
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def smart_wait(d, timeout=10):
|
|
17
|
-
"""智能等待页面稳定"""
|
|
18
|
-
d.implicitly_wait(timeout)
|
|
19
|
-
time.sleep(0.5) # 额外等待动画
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def close_ad_if_exists(d):
|
|
23
|
-
"""尝试关闭广告弹窗"""
|
|
24
|
-
for keyword in AD_CLOSE_KEYWORDS:
|
|
25
|
-
elem = d(textContains=keyword)
|
|
26
|
-
if elem.exists(timeout=0.5):
|
|
27
|
-
try:
|
|
28
|
-
elem.click()
|
|
29
|
-
print(f' 📢 关闭广告: {keyword}')
|
|
30
|
-
time.sleep(0.5)
|
|
31
|
-
return True
|
|
32
|
-
except:
|
|
33
|
-
pass
|
|
34
|
-
return False
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def safe_click(d, selector, timeout=5):
|
|
38
|
-
"""安全点击(带等待和重试)"""
|
|
39
|
-
try:
|
|
40
|
-
if selector.exists(timeout=timeout):
|
|
41
|
-
selector.click()
|
|
42
|
-
return True
|
|
43
|
-
return False
|
|
44
|
-
except Exception as e:
|
|
45
|
-
print(f' ⚠️ 点击失败: {e}')
|
|
46
|
-
return False
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def test_main():
|
|
50
|
-
# 连接设备
|
|
51
|
-
d = u2.connect()
|
|
52
|
-
d.implicitly_wait(10) # 设置全局等待
|
|
53
|
-
|
|
54
|
-
# 启动应用
|
|
55
|
-
d.app_start(PACKAGE_NAME)
|
|
56
|
-
smart_wait(d)
|
|
57
|
-
|
|
58
|
-
# 尝试关闭启动广告
|
|
59
|
-
close_ad_if_exists(d)
|
|
60
|
-
|
|
61
|
-
# 步骤1: 点击文本 'Mind'
|
|
62
|
-
safe_click(d, d(text='Mind'))
|
|
63
|
-
smart_wait(d)
|
|
64
|
-
close_ad_if_exists(d) # 检查广告
|
|
65
|
-
|
|
66
|
-
# 步骤2: 点击坐标 (756, 2277)
|
|
67
|
-
d.click(756, 2277)
|
|
68
|
-
smart_wait(d)
|
|
69
|
-
close_ad_if_exists(d) # 检查广告
|
|
70
|
-
|
|
71
|
-
# 步骤3: 点击坐标 (815, 285)
|
|
72
|
-
d.click(815, 285)
|
|
73
|
-
smart_wait(d)
|
|
74
|
-
close_ad_if_exists(d) # 检查广告
|
|
75
|
-
|
|
76
|
-
print('✅ 测试完成')
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if __name__ == '__main__':
|
|
80
|
-
test_main()
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
测试用例: Mind云文档我的空间_正确版
|
|
5
|
-
生成时间: 2025-12-17 11:08:10
|
|
6
|
-
"""
|
|
7
|
-
import time
|
|
8
|
-
import uiautomator2 as u2
|
|
9
|
-
|
|
10
|
-
PACKAGE_NAME = "com.im30.mind"
|
|
11
|
-
|
|
12
|
-
# 广告关闭按钮关键词(可自定义)
|
|
13
|
-
AD_CLOSE_KEYWORDS = ['关闭', '跳过', 'Skip', 'Close', '×', 'X', '我知道了', '稍后再说']
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def smart_wait(d, seconds=1):
|
|
17
|
-
"""等待页面稳定"""
|
|
18
|
-
time.sleep(seconds)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def close_ad_if_exists(d, quick=False):
|
|
22
|
-
"""尝试关闭广告弹窗(quick=True 时只检查常见的)"""
|
|
23
|
-
keywords = AD_CLOSE_KEYWORDS[:3] if quick else AD_CLOSE_KEYWORDS
|
|
24
|
-
for keyword in keywords:
|
|
25
|
-
elem = d(textContains=keyword)
|
|
26
|
-
if elem.exists(timeout=0.3): # 缩短超时
|
|
27
|
-
try:
|
|
28
|
-
elem.click()
|
|
29
|
-
print(f' 📢 关闭广告: {keyword}')
|
|
30
|
-
time.sleep(0.3)
|
|
31
|
-
return True
|
|
32
|
-
except:
|
|
33
|
-
pass
|
|
34
|
-
return False
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def safe_click(d, selector, timeout=3):
|
|
38
|
-
"""安全点击(带等待)"""
|
|
39
|
-
try:
|
|
40
|
-
if selector.exists(timeout=timeout):
|
|
41
|
-
selector.click()
|
|
42
|
-
return True
|
|
43
|
-
return False
|
|
44
|
-
except Exception as e:
|
|
45
|
-
print(f' ⚠️ 点击失败: {e}')
|
|
46
|
-
return False
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def test_main():
|
|
50
|
-
# 连接设备
|
|
51
|
-
d = u2.connect()
|
|
52
|
-
d.implicitly_wait(10) # 设置全局等待
|
|
53
|
-
|
|
54
|
-
# 启动应用
|
|
55
|
-
d.app_start(PACKAGE_NAME)
|
|
56
|
-
smart_wait(d)
|
|
57
|
-
|
|
58
|
-
# 尝试关闭启动广告
|
|
59
|
-
close_ad_if_exists(d)
|
|
60
|
-
|
|
61
|
-
# 步骤1: 点击坐标 (756, 2277)
|
|
62
|
-
d.click(756, 2277)
|
|
63
|
-
time.sleep(0.5) # 等待响应
|
|
64
|
-
|
|
65
|
-
# 步骤2: 点击坐标 (815, 285)
|
|
66
|
-
d.click(815, 285)
|
|
67
|
-
time.sleep(0.5) # 等待响应
|
|
68
|
-
|
|
69
|
-
print('✅ 测试完成')
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if __name__ == '__main__':
|
|
73
|
-
test_main()
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
测试用例: 打开Mind应用测试
|
|
5
|
-
生成时间: 2025-12-17 10:52:37
|
|
6
|
-
"""
|
|
7
|
-
import time
|
|
8
|
-
import uiautomator2 as u2
|
|
9
|
-
|
|
10
|
-
PACKAGE_NAME = "com.im30.mind"
|
|
11
|
-
|
|
12
|
-
# 广告关闭按钮关键词(可自定义)
|
|
13
|
-
AD_CLOSE_KEYWORDS = ['关闭', '跳过', 'Skip', 'Close', '×', 'X', '我知道了', '稍后再说']
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def smart_wait(d, timeout=10):
|
|
17
|
-
"""智能等待页面稳定"""
|
|
18
|
-
d.implicitly_wait(timeout)
|
|
19
|
-
time.sleep(0.5) # 额外等待动画
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def close_ad_if_exists(d):
|
|
23
|
-
"""尝试关闭广告弹窗"""
|
|
24
|
-
for keyword in AD_CLOSE_KEYWORDS:
|
|
25
|
-
elem = d(textContains=keyword)
|
|
26
|
-
if elem.exists(timeout=0.5):
|
|
27
|
-
try:
|
|
28
|
-
elem.click()
|
|
29
|
-
print(f' 📢 关闭广告: {keyword}')
|
|
30
|
-
time.sleep(0.5)
|
|
31
|
-
return True
|
|
32
|
-
except:
|
|
33
|
-
pass
|
|
34
|
-
return False
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def safe_click(d, selector, timeout=5):
|
|
38
|
-
"""安全点击(带等待和重试)"""
|
|
39
|
-
try:
|
|
40
|
-
if selector.exists(timeout=timeout):
|
|
41
|
-
selector.click()
|
|
42
|
-
return True
|
|
43
|
-
return False
|
|
44
|
-
except Exception as e:
|
|
45
|
-
print(f' ⚠️ 点击失败: {e}')
|
|
46
|
-
return False
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def test_main():
|
|
50
|
-
# 连接设备
|
|
51
|
-
d = u2.connect()
|
|
52
|
-
d.implicitly_wait(10) # 设置全局等待
|
|
53
|
-
|
|
54
|
-
# 启动应用
|
|
55
|
-
d.app_start(PACKAGE_NAME)
|
|
56
|
-
smart_wait(d)
|
|
57
|
-
|
|
58
|
-
# 尝试关闭启动广告
|
|
59
|
-
close_ad_if_exists(d)
|
|
60
|
-
|
|
61
|
-
# 步骤1: 点击文本 'Mind'
|
|
62
|
-
safe_click(d, d(text='Mind'))
|
|
63
|
-
smart_wait(d)
|
|
64
|
-
close_ad_if_exists(d) # 检查广告
|
|
65
|
-
|
|
66
|
-
# 步骤2: 点击坐标 (540, 1200)
|
|
67
|
-
d.click(540, 1200)
|
|
68
|
-
smart_wait(d)
|
|
69
|
-
close_ad_if_exists(d) # 检查广告
|
|
70
|
-
|
|
71
|
-
# 步骤3: 输入文本 '测试'
|
|
72
|
-
d(resourceId='com.im30.mind:id/search').set_text('测试')
|
|
73
|
-
smart_wait(d)
|
|
74
|
-
|
|
75
|
-
# 步骤4: 滑动 up
|
|
76
|
-
d.swipe_ext('up')
|
|
77
|
-
smart_wait(d)
|
|
78
|
-
|
|
79
|
-
print('✅ 测试完成')
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if __name__ == '__main__':
|
|
83
|
-
test_main()
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
测试用例: Mind云文档我的空间_优化版
|
|
5
|
-
生成时间: 2025-12-17 11:04:53
|
|
6
|
-
"""
|
|
7
|
-
import time
|
|
8
|
-
import uiautomator2 as u2
|
|
9
|
-
|
|
10
|
-
PACKAGE_NAME = "com.im30.mind"
|
|
11
|
-
|
|
12
|
-
# 广告关闭按钮关键词(可自定义)
|
|
13
|
-
AD_CLOSE_KEYWORDS = ['关闭', '跳过', 'Skip', 'Close', '×', 'X', '我知道了', '稍后再说']
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def smart_wait(d, seconds=1):
|
|
17
|
-
"""等待页面稳定"""
|
|
18
|
-
time.sleep(seconds)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def close_ad_if_exists(d, quick=False):
|
|
22
|
-
"""尝试关闭广告弹窗(quick=True 时只检查常见的)"""
|
|
23
|
-
keywords = AD_CLOSE_KEYWORDS[:3] if quick else AD_CLOSE_KEYWORDS
|
|
24
|
-
for keyword in keywords:
|
|
25
|
-
elem = d(textContains=keyword)
|
|
26
|
-
if elem.exists(timeout=0.3): # 缩短超时
|
|
27
|
-
try:
|
|
28
|
-
elem.click()
|
|
29
|
-
print(f' 📢 关闭广告: {keyword}')
|
|
30
|
-
time.sleep(0.3)
|
|
31
|
-
return True
|
|
32
|
-
except:
|
|
33
|
-
pass
|
|
34
|
-
return False
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def safe_click(d, selector, timeout=3):
|
|
38
|
-
"""安全点击(带等待)"""
|
|
39
|
-
try:
|
|
40
|
-
if selector.exists(timeout=timeout):
|
|
41
|
-
selector.click()
|
|
42
|
-
return True
|
|
43
|
-
return False
|
|
44
|
-
except Exception as e:
|
|
45
|
-
print(f' ⚠️ 点击失败: {e}')
|
|
46
|
-
return False
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def test_main():
|
|
50
|
-
# 连接设备
|
|
51
|
-
d = u2.connect()
|
|
52
|
-
d.implicitly_wait(10) # 设置全局等待
|
|
53
|
-
|
|
54
|
-
# 启动应用
|
|
55
|
-
d.app_start(PACKAGE_NAME)
|
|
56
|
-
smart_wait(d)
|
|
57
|
-
|
|
58
|
-
# 尝试关闭启动广告
|
|
59
|
-
close_ad_if_exists(d)
|
|
60
|
-
|
|
61
|
-
# 步骤1: 点击文本 'Mind'
|
|
62
|
-
safe_click(d, d(text='Mind'))
|
|
63
|
-
time.sleep(0.5) # 等待响应
|
|
64
|
-
|
|
65
|
-
# 步骤2: 点击坐标 (756, 2277)
|
|
66
|
-
d.click(756, 2277)
|
|
67
|
-
time.sleep(0.5) # 等待响应
|
|
68
|
-
|
|
69
|
-
# 步骤3: 点击坐标 (815, 285)
|
|
70
|
-
d.click(815, 285)
|
|
71
|
-
time.sleep(0.5) # 等待响应
|
|
72
|
-
|
|
73
|
-
print('✅ 测试完成')
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if __name__ == '__main__':
|
|
77
|
-
test_main()
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
测试用例: 打开Mind应用测试
|
|
5
|
-
生成时间: 2025-12-17 10:50:37
|
|
6
|
-
"""
|
|
7
|
-
import time
|
|
8
|
-
import uiautomator2 as u2
|
|
9
|
-
|
|
10
|
-
PACKAGE_NAME = "com.im30.mind"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def test_main():
|
|
14
|
-
# 连接设备
|
|
15
|
-
d = u2.connect()
|
|
16
|
-
|
|
17
|
-
# 启动应用
|
|
18
|
-
d.app_start(PACKAGE_NAME)
|
|
19
|
-
time.sleep(3)
|
|
20
|
-
|
|
21
|
-
# 步骤1: 点击文本 Mind
|
|
22
|
-
d(text='Mind').click()
|
|
23
|
-
time.sleep(1)
|
|
24
|
-
|
|
25
|
-
# 步骤2: 点击坐标
|
|
26
|
-
d.click(540, 1200)
|
|
27
|
-
time.sleep(1)
|
|
28
|
-
|
|
29
|
-
# 步骤3: 输入文本
|
|
30
|
-
d(resourceId='com.im30.mind:id/search').set_text('测试')
|
|
31
|
-
time.sleep(1)
|
|
32
|
-
|
|
33
|
-
print('✅ 测试完成')
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if __name__ == '__main__':
|
|
37
|
-
test_main()
|