新增插件: - terminal: 持久交互式终端,纯标准库,全平台,13个测试 - operit_bridge: Operit AI工具格式桥接,47个脚本288个工具,12个测试 其他: - plugins/operit/: 47个Operit官方脚本 - 更新plugin_registry.json/plugins_config.py - 更新about.txt/CHANGELOG.md至v5.4.2
633 lines
28 KiB
TypeScript
633 lines
28 KiB
TypeScript
/*
|
|
METADATA
|
|
{
|
|
// 百度地图智能助手
|
|
name: "Automatic_baiduMap_assistant",
|
|
display_name: {
|
|
zh: "百度地图自动化助手"
|
|
en: "Baidu Map Automation Assistant"
|
|
}description: { zh: "高级百度地图智能助手,通过UI自动化技术实现地图交互,支持地点搜索、路线规划、周边查询等功能,为AI提供强大的地理位置服务能力。适用于出行规划、信息查询、智能问答等场景。", en: "Advanced Baidu Maps assistant powered by UI automation. Supports place search, navigation/route planning, and nearby discovery, enabling AI-driven location services. Useful for trip planning, information lookup, and Q&A." },
|
|
|
|
// Tools in this package
|
|
category: "Automatic"
|
|
tools: [
|
|
{
|
|
name: "workflow_guide",
|
|
description: { zh: "百度地图助手工具使用流程指南。要完成复杂任务,请按以下顺序组合使用工具:\n1. **搜索地点**: 使用 `search_location` 查找地点、餐馆、公司等,这是多数操作的起点。\n2. **选择地点**: 如果 `search_location` 返回多个结果,使用 `select_location_from_list` 从列表中选择一个。\n3. **开始导航**: 搜索到唯一地点或从列表中选择地点后,使用 `go_to_location` 来规划到该地的路线。\n4. **周边搜索**: 使用 `search_nearby` 查找当前位置或指定地点附近的设施。\n- **随时导航**: `navigate_to_home` 可随时返回地图主页。", en: "Workflow guide for the Baidu Maps assistant. For complex tasks, combine tools in this order:\n1. **Search**: use `search_location` to find places/restaurants/companies (starting point for most tasks).\n2. **Select**: if `search_location` returns multiple results, use `select_location_from_list` to pick one from the list.\n3. **Navigate**: after a unique result is found or selected, use `go_to_location` to plan a route.\n4. **Nearby**: use `search_nearby` to find facilities near your current location or a specified place.\n- **Go home anytime**: use `navigate_to_home` to return to the map home screen." }
|
|
parameters: []
|
|
advice: true
|
|
},
|
|
{
|
|
name: "search_location",
|
|
description: { zh: "在百度地图中搜索地点、餐馆、公司等信息。", en: "Search places, restaurants, companies, and other POIs in Baidu Maps." },
|
|
parameters: [
|
|
{
|
|
name: "keyword",
|
|
description: { zh: "要搜索的地点关键词", en: "Keyword of the place to search for." },
|
|
type: "string",
|
|
required: true
|
|
}
|
|
]
|
|
},
|
|
{
|
|
name: "get_directions",
|
|
description: { zh: "规划从起点到终点的路线。", en: "Plan a route from a start point to a destination." },
|
|
parameters: [
|
|
{
|
|
name: "start_point",
|
|
description: { zh: "路线起点,默认为'我的位置'", en: "Route start point (default: 'My location')." },
|
|
type: "string",
|
|
required: false
|
|
},
|
|
{
|
|
name: "end_point",
|
|
description: { zh: "路线终点", en: "Route destination." },
|
|
type: "string",
|
|
required: true
|
|
},
|
|
{
|
|
name: "transport_mode",
|
|
description: { zh: "交通方式:driving(驾车), transit(公交), walking(步行), cycling(骑行)", en: "Transport mode: driving, transit, walking, cycling." },
|
|
type: "string",
|
|
required: false
|
|
}
|
|
]
|
|
},
|
|
{
|
|
name: "select_location_from_list",
|
|
description: { zh: "从 'search_location' 返回的搜索结果列表中选择一个地点。", en: "Select a place from the result list returned by `search_location`." },
|
|
parameters: [
|
|
{
|
|
name: "keyword",
|
|
description: { zh: "要选择的地点名称关键词", en: "Keyword to match the place name to select." },
|
|
type: "string",
|
|
required: false
|
|
},
|
|
{
|
|
name: "index",
|
|
description: { zh: "要选择的地点在列表中的索引位置(从1开始)", en: "Index of the item in the list (1-based)." },
|
|
type: "number",
|
|
required: false
|
|
}
|
|
]
|
|
},
|
|
{
|
|
name: "go_to_location",
|
|
description: { zh: "在地点详情页点击“到这去”,以开启导航或路线规划。可以选择交通方式。", en: "On the place detail page, tap \"Go\" (到这去) to start navigation/route planning. You can optionally select a transport mode." },
|
|
parameters: [
|
|
{
|
|
name: "transport_mode",
|
|
description: { zh: "交通方式:新能源, 驾车, 打车, 公共交通, 代驾, 骑行, 步行, 拼车, 飞机, 火车, 客车, 摩托车", en: "Transport mode tab label in the app (e.g., 新能源, 驾车, 打车, 公共交通, 代驾, 骑行, 步行, 拼车, 飞机, 火车, 客车, 摩托车)." },
|
|
type: "string",
|
|
required: false
|
|
}
|
|
]
|
|
},
|
|
{
|
|
name: "search_nearby",
|
|
description: { zh: "搜索中心点附近的地点信息。", en: "Search places around a center point." },
|
|
parameters: [
|
|
{
|
|
name: "keyword",
|
|
description: { zh: "要搜索的周边设施关键词(如:银行, 加油站)", en: "Nearby keyword (e.g., bank, gas station)." },
|
|
type: "string",
|
|
required: true
|
|
},
|
|
{
|
|
name: "center_point",
|
|
description: { zh: "搜索的中心点,默认为'我的位置'", en: "Center point for the search (default: 'My location')." },
|
|
type: "string",
|
|
required: false
|
|
}
|
|
]
|
|
},
|
|
{
|
|
name: "navigate_to_home",
|
|
description: { zh: "返回百度地图的主界面。", en: "Return to the Baidu Maps home screen." },
|
|
parameters: []
|
|
},
|
|
{
|
|
name: "activate_voice_assistant",
|
|
description: { zh: "在主界面点击语音搜索按钮,启动“小度”语音助手。", en: "Tap the voice search button on the home screen to launch the Xiaodu voice assistant." },
|
|
parameters: []
|
|
}
|
|
]
|
|
}*/
|
|
|
|
interface Array<T> {
|
|
at(index: number): T | undefined;
|
|
}
|
|
|
|
const BaiduMapAssistant = (function () {
|
|
|
|
// 添加 Array.prototype.at 支持
|
|
if (!Array.prototype.at) {
|
|
Object.defineProperty(Array.prototype, 'at', {
|
|
value: function (index: number) {
|
|
if (index < 0) {
|
|
index += this.length;
|
|
}
|
|
return this[index];
|
|
},
|
|
enumerable: false,
|
|
configurable: true,
|
|
writable: true
|
|
});
|
|
}
|
|
|
|
// 百度地图应用包名
|
|
const BAIDU_MAP_PACKAGE = "com.baidu.BaiduMap";
|
|
const MAIN_ACTIVITY = "com.baidu.baidumaps.MapsActivity";
|
|
|
|
// Helper to create response objects
|
|
function createResponse(success: boolean, message: string, data: object | string = {}) {
|
|
if (typeof data === 'string') {
|
|
return { success, message, data };
|
|
}
|
|
return { success, message, ...data };
|
|
}
|
|
|
|
// Helper to find a UI element and click it
|
|
async function findAndClick(finder: () => Promise<any>): Promise<boolean> {
|
|
const element = await finder();
|
|
if (element) {
|
|
await element.click();
|
|
await Tools.System.sleep(1000);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Helper to find a UI element by text and click it
|
|
async function findAndClickByText(text: string): Promise<boolean> {
|
|
return findAndClick(async () => (await UINode.getCurrentPage()).findByText(text));
|
|
}
|
|
|
|
async function isOnMainPage(): Promise<boolean> {
|
|
const page = await UINode.getCurrentPage();
|
|
// Check for the presence of key elements that indicate the main map screen.
|
|
const drivingButton = page.findByText("驾车");
|
|
const transitButton = page.findByText("公共交通");
|
|
const taxiButton = page.findByText("打车");
|
|
const hotelButton = page.findByText("订酒店");
|
|
|
|
if (drivingButton && transitButton && taxiButton && hotelButton) {
|
|
console.log("Detected Baidu Map main page by key elements.");
|
|
return true;
|
|
}
|
|
console.log("Not on main page, key elements not found.");
|
|
return false;
|
|
}
|
|
|
|
async function ensureMain(packageName: string = BAIDU_MAP_PACKAGE): Promise<boolean> {
|
|
let pageInfo = await Tools.UI.getPageInfo();
|
|
|
|
// 1. If not in the correct app, start it.
|
|
if (pageInfo.packageName !== packageName) {
|
|
console.log(`Not in the correct app. Current: ${pageInfo.packageName}. Starting ${packageName}.`);
|
|
await Tools.System.startApp(packageName);
|
|
await Tools.System.sleep(5000); // Wait for app to load
|
|
pageInfo = await Tools.UI.getPageInfo(); // Re-check page info
|
|
if (pageInfo.packageName !== packageName) {
|
|
console.error(`Failed to start ${packageName}.`);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// 2. Check if we are already on the main page.
|
|
if (await isOnMainPage()) {
|
|
console.log("Already on the main page.");
|
|
return true;
|
|
}
|
|
|
|
// 3. If not on the main page, try pressing back up to 4 times.
|
|
console.log("Not on the main page. Attempting to go back.");
|
|
for (let i = 0; i < 4; i++) {
|
|
await Tools.UI.pressKey("KEYCODE_BACK");
|
|
await Tools.System.sleep(1000); // Wait for UI to settle
|
|
|
|
pageInfo = await Tools.UI.getPageInfo();
|
|
// If we have exited the app, stop trying.
|
|
if (pageInfo.packageName !== packageName) {
|
|
console.log("Exited the app while trying to go back.");
|
|
break; // Exit the loop, will proceed to restart
|
|
}
|
|
|
|
if (await isOnMainPage()) {
|
|
console.log(`Successfully returned to main page on attempt ${i + 1}.`);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// 4. If pressing back failed, perform a full app restart.
|
|
console.log("Pressing back failed or exited app. Performing a full app restart.");
|
|
await Tools.System.stopApp(packageName);
|
|
await Tools.System.sleep(1000);
|
|
await Tools.System.startApp(packageName);
|
|
await Tools.System.sleep(5000); // Longer wait for cold start
|
|
|
|
pageInfo = await Tools.UI.getPageInfo();
|
|
if (pageInfo.packageName !== packageName) {
|
|
console.error(`Failed to restart ${packageName}.`);
|
|
return false;
|
|
}
|
|
|
|
if (await isOnMainPage()) {
|
|
console.log("Successfully reached main page after restart.");
|
|
return true;
|
|
}
|
|
|
|
console.error(`Failed to navigate to the main page of ${packageName} even after restart.`);
|
|
return false;
|
|
}
|
|
|
|
async function search_location(params: { keyword: string }): Promise<any> {
|
|
const { keyword } = params;
|
|
console.log(`Searching for location: ${keyword}`);
|
|
|
|
// 确保百度地图已在前台运行
|
|
if (!await ensureMain()) {
|
|
return createResponse(false, "无法启动或切换到百度地图");
|
|
}
|
|
|
|
await Tools.System.sleep(2000); // 等待首页完全加载
|
|
|
|
// 查找并点击搜索框
|
|
const page = await UINode.getCurrentPage();
|
|
const searchBox = page.findById('serachbox_container');
|
|
if (!searchBox) {
|
|
return createResponse(false, "在地图主页未找到搜索框 (ID: serachbox_container)");
|
|
}
|
|
await searchBox.click();
|
|
await Tools.System.sleep(300);
|
|
await Tools.UI.setText(keyword);
|
|
await Tools.System.sleep(300);
|
|
|
|
|
|
// 点击搜索按钮
|
|
const searchButton = (await UINode.getCurrentPage()).findByText("搜索");
|
|
if (searchButton) {
|
|
await searchButton.click();
|
|
}
|
|
|
|
await Tools.System.sleep(5000); // 等待搜索结果加载
|
|
|
|
// 上拉以显示完整的结果列表,这在某些情况下是必要的
|
|
console.log("执行上拉手势以确保结果列表完全可见...");
|
|
await Tools.UI.swipe(540, 1800, 540, 900); // 从屏幕底部向上滑动
|
|
await Tools.System.sleep(1500); // 等待动画完成
|
|
|
|
// 检查页面是唯一结果还是列表
|
|
const pageAfterSearch = await UINode.getCurrentPage();
|
|
const listContainer = pageAfterSearch.findById("com.baidu.BaiduMap:id/talosListContainer");
|
|
|
|
if (listContainer) {
|
|
// 情况2: 结果列表
|
|
console.log("检测到列表容器,开始解析结果列表...");
|
|
const results = await get_map_search_results();
|
|
|
|
return createResponse(true, `搜索到 ${results.length} 个相关地点。请使用 'select_location_from_list' 选择一个。`, {
|
|
keyword: keyword,
|
|
results: results.map(({ element, ...rest }) => rest), // 移除 element 属性
|
|
result_count: results.length
|
|
});
|
|
}
|
|
|
|
const goToButton = pageAfterSearch.findByText("到这去");
|
|
if (goToButton) {
|
|
// 情况1: 唯一结果
|
|
return createResponse(true, `已找到唯一结果 "${keyword}"。您现在可以使用 'go_to_location' 进行导航。`, { keyword });
|
|
} else {
|
|
return createResponse(false, `搜索 "${keyword}" 后既未找到结果列表,也未找到唯一结果。`);
|
|
}
|
|
}
|
|
|
|
async function get_map_search_results(desiredCount: number = 20): Promise<any[]> {
|
|
const results: any[] = [];
|
|
const seenTitles = new Set<string>();
|
|
const maxScrolls = 1;
|
|
let lastResultCount = -1;
|
|
|
|
for (let i = 0; i < maxScrolls; i++) {
|
|
let page = await UINode.getCurrentPage();
|
|
const resultsContainer = page.findById('com.baidu.BaiduMap:id/talosListContainer');
|
|
if (!resultsContainer) {
|
|
console.log("未找到搜索结果容器 (ID: com.baidu.BaiduMap:id/talosListContainer)");
|
|
break;
|
|
}
|
|
|
|
const recyclerView = resultsContainer.findByClass("RecyclerView");
|
|
if (!recyclerView) {
|
|
console.log("未在结果容器中找到 RecyclerView。");
|
|
break;
|
|
}
|
|
|
|
// 通过查找所有“到这去”按钮来定位每个列表项
|
|
const goToButtons = recyclerView.findAllByText("到这去");
|
|
|
|
for (const button of goToButtons) {
|
|
// 找到按钮所属的、作为RecyclerView直接子节点的那个父节点
|
|
const item = button.closest((node) =>
|
|
node.parent !== undefined && node.parent.equals(recyclerView)
|
|
);
|
|
if (!item) continue;
|
|
|
|
const textViews = item.findAllByClass('TextView');
|
|
const relevantTextViews = textViews.filter(tv => tv.text && tv.text.trim() !== '到这去' && tv.text.trim() !== '');
|
|
|
|
if (relevantTextViews.length === 0) continue;
|
|
|
|
// 按纵坐标排序,最上面的通常是标题
|
|
relevantTextViews.sort((a, b) => {
|
|
try {
|
|
const [, y1a] = a.bounds!.match(/\[\d+,(\d+)\]/)!;
|
|
const [, y1b] = b.bounds!.match(/\[\d+,(\d+)\]/)!;
|
|
return parseInt(y1a) - parseInt(y1b);
|
|
} catch (e) {
|
|
return 0;
|
|
}
|
|
});
|
|
|
|
const title = relevantTextViews[0].text;
|
|
if (!title || seenTitles.has(title)) {
|
|
continue;
|
|
}
|
|
seenTitles.add(title);
|
|
|
|
// 查找包含地址特征的文本作为地址
|
|
const addressNode = relevantTextViews.find(tv =>
|
|
tv.text && (tv.text.includes('省') || tv.text.includes('市') || tv.text.includes('区') || tv.text.includes('路') || tv.text.includes('号'))
|
|
);
|
|
const address = addressNode ? addressNode.text : "N/A";
|
|
|
|
results.push({
|
|
index: results.length + 1, // 临时索引
|
|
title: title,
|
|
address: address,
|
|
element: item
|
|
});
|
|
}
|
|
|
|
// 滚动前去重并检查是否需要停止
|
|
const uniqueResults = Array.from(new Map(results.map(r => [r.title, r])).values());
|
|
|
|
if (uniqueResults.length >= desiredCount) {
|
|
console.log(`已找到 ${uniqueResults.length} 条不重复结果,满足要求。`);
|
|
break;
|
|
}
|
|
if (uniqueResults.length === lastResultCount) {
|
|
console.log("滚动未产生新结果,停止滚动。");
|
|
break;
|
|
}
|
|
lastResultCount = uniqueResults.length;
|
|
|
|
// 向下滚动加载更多
|
|
await Tools.UI.swipe(540, 1800, 540, 600);
|
|
await Tools.System.sleep(2000); // 增加等待时间以确保加载
|
|
}
|
|
|
|
// 最终去重并重新编号索引
|
|
const finalResults = Array.from(new Map(results.map(item => [item.title, item])).values())
|
|
.map((item, index) => ({ ...item, index: index + 1 }));
|
|
|
|
return finalResults.slice(0, desiredCount);
|
|
}
|
|
|
|
// --- Placeholder functions for other tools ---
|
|
async function select_location_from_list(params: { keyword?: string, index?: number }): Promise<any> {
|
|
const { keyword, index } = params;
|
|
|
|
if (!keyword && !index) {
|
|
return createResponse(false, "请提供地点关键词或索引。");
|
|
}
|
|
console.log(`尝试从列表中选择 - 关键词: "${keyword}", 索引: ${index}`);
|
|
|
|
const seenTitles = new Set<string>();
|
|
let currentItemCount = 0;
|
|
const maxScrolls = 10;
|
|
let lastSeenCount = -1;
|
|
|
|
for (let i = 0; i < maxScrolls; i++) {
|
|
const page = await UINode.getCurrentPage();
|
|
// In Baidu Maps, the results are often inside a RecyclerView
|
|
const recyclerView = page.findByClass("RecyclerView");
|
|
if (!recyclerView) {
|
|
console.log("未找到 RecyclerView, 无法选择项目。");
|
|
return createResponse(false, "未找到结果列表容器 (RecyclerView)。");
|
|
}
|
|
|
|
const goToButtons = recyclerView.findAllByText("到这去");
|
|
|
|
for (const button of goToButtons) {
|
|
const item = button.closest((node) =>
|
|
node.parent !== undefined && node.parent.equals(recyclerView)
|
|
);
|
|
if (!item) continue;
|
|
|
|
const textViews = item.findAllByClass('TextView');
|
|
const relevantTextViews = textViews.filter(tv => tv.text && tv.text.trim() !== '到这去' && tv.text.trim() !== '');
|
|
if (relevantTextViews.length === 0) continue;
|
|
|
|
relevantTextViews.sort((a, b) => {
|
|
try {
|
|
const [, y1a] = a.bounds!.match(/\[\d+,(\d+)\]/)!;
|
|
const [, y1b] = b.bounds!.match(/\[\d+,(\d+)\]/)!;
|
|
return parseInt(y1a) - parseInt(y1b);
|
|
} catch (e) { return 0; }
|
|
});
|
|
|
|
const title = relevantTextViews[0].text;
|
|
if (!title || seenTitles.has(title)) {
|
|
continue;
|
|
}
|
|
|
|
seenTitles.add(title);
|
|
currentItemCount++;
|
|
|
|
let shouldClick = false;
|
|
if (index) {
|
|
if (currentItemCount === index) {
|
|
shouldClick = true;
|
|
}
|
|
} else if (keyword) {
|
|
if (title.includes(keyword)) {
|
|
shouldClick = true;
|
|
}
|
|
}
|
|
|
|
if (shouldClick) {
|
|
await item.click();
|
|
await Tools.System.sleep(2000);
|
|
return createResponse(true, `已成功选择地点: "${title}"`);
|
|
}
|
|
}
|
|
|
|
if (seenTitles.size === lastSeenCount) {
|
|
console.log("滚动未产生新结果,停止。");
|
|
break;
|
|
}
|
|
lastSeenCount = seenTitles.size;
|
|
|
|
if (index && currentItemCount >= index) {
|
|
// If we're looking for an index and have passed it, no need to scroll more.
|
|
break;
|
|
}
|
|
|
|
await Tools.UI.swipe(540, 1800, 540, 600); // Swipe up
|
|
await Tools.System.sleep(2000);
|
|
}
|
|
|
|
return createResponse(false, "在列表中未找到或无法选择指定的地点。", { keyword, index });
|
|
}
|
|
|
|
async function go_to_location(params: { transport_mode?: string }): Promise<any> {
|
|
const transportMode = params.transport_mode || "驾车"; // 默认为驾车
|
|
console.log("尝试点击 '到这去' 按钮...");
|
|
const page = await UINode.getCurrentPage();
|
|
const goToButton = page.findByText("到这去");
|
|
if (goToButton) {
|
|
await goToButton.click();
|
|
await Tools.System.sleep(3000); // 等待路线规划页面加载
|
|
|
|
if (transportMode) {
|
|
console.log(`尝试选择交通方式: ${transportMode}`);
|
|
let pageAfterClick = await UINode.getCurrentPage();
|
|
|
|
// 尝试直接点击可见的交通方式按钮
|
|
try {
|
|
let modeButton = pageAfterClick.findByText(transportMode);
|
|
if (modeButton) {
|
|
await modeButton.click();
|
|
await Tools.System.sleep(1000);
|
|
return createResponse(true, `已点击“到这去”,并成功切换到“${transportMode}”模式。`);
|
|
}
|
|
} catch (e) {
|
|
console.log(`直接点击失败,按钮可能在屏幕外。尝试滚动查找。`);
|
|
}
|
|
|
|
// 如果未直接找到,尝试展开更多选项
|
|
console.log(`未直接找到 "${transportMode}",尝试展开更多选项...`);
|
|
const moreModesButton = pageAfterClick.findById('route_tab_group');
|
|
if (moreModesButton) {
|
|
await moreModesButton.click();
|
|
await Tools.System.sleep(1500); // 等待展开动画
|
|
pageAfterClick = await UINode.getCurrentPage(); // 刷新页面节点
|
|
let modeButton = pageAfterClick.findByText(transportMode);
|
|
if (modeButton) {
|
|
await modeButton.click();
|
|
await Tools.System.sleep(1500);
|
|
return createResponse(true, `已点击“到这去”,并成功切换到“${transportMode}”模式。`);
|
|
}
|
|
}
|
|
|
|
return createResponse(false, `已点击“到这去”,但无法找到交通方式“${transportMode}”。`);
|
|
}
|
|
|
|
return createResponse(true, "已点击“到这去”,进入路线规划页面。");
|
|
} else {
|
|
return createResponse(false, "在当前页面未找到“到这去”按钮。");
|
|
}
|
|
}
|
|
|
|
async function get_directions(params: { start_point?: string, end_point: string, transport_mode?: string }): Promise<any> {
|
|
return createResponse(false, "功能 'get_directions' 尚未实现。");
|
|
}
|
|
|
|
async function search_nearby(params: { keyword: string, center_point?: string }): Promise<any> {
|
|
return createResponse(false, "功能 'search_nearby' 尚未实现。");
|
|
}
|
|
|
|
async function navigate_to_home(params: {}): Promise<any> {
|
|
console.log("Navigating to home screen.");
|
|
if (await ensureMain()) {
|
|
// 在地图应用中,通常按返回键可以回到主图区
|
|
// 这里可以根据实际情况增加返回逻辑
|
|
await Tools.UI.pressKey("KEYCODE_BACK");
|
|
await Tools.System.sleep(500);
|
|
await Tools.UI.pressKey("KEYCODE_BACK");
|
|
return createResponse(true, "已尝试返回主界面。");
|
|
} else {
|
|
return createResponse(false, "无法导航到主界面,因为无法启动百度地图。");
|
|
}
|
|
}
|
|
|
|
async function activate_voice_assistant(params: {}): Promise<any> {
|
|
console.log("正在激活语音助手“小度”...");
|
|
if (!await ensureMain()) {
|
|
return createResponse(false, "无法启动或切换到百度地图主页,因此无法激活语音助手。");
|
|
}
|
|
await Tools.System.sleep(1000); // 等待UI稳定
|
|
|
|
const page = await UINode.getCurrentPage();
|
|
const voiceButton = page.findById('voice_search');
|
|
|
|
if (voiceButton) {
|
|
await voiceButton.click();
|
|
await Tools.System.sleep(2000); // 等待语音助手界面弹出
|
|
return createResponse(true, "已成功激活“小度”语音助手。");
|
|
} else {
|
|
return createResponse(false, "在主界面上未找到语音搜索按钮 (ID: voice_search)。");
|
|
}
|
|
}
|
|
|
|
async function wrapToolExecution(func: (params: any) => Promise<any>, params: any) {
|
|
try {
|
|
const result = await func(params);
|
|
complete({
|
|
...result,
|
|
});
|
|
} catch (error) {
|
|
console.error(`Tool ${func.name} failed unexpectedly`, error);
|
|
complete({
|
|
success: false,
|
|
message: `工具执行时发生意外错误: ${error.message}`,
|
|
});
|
|
}
|
|
}
|
|
|
|
async function main(params: {}) {
|
|
console.log("Running main for testing...");
|
|
// 这是一个用于测试的函数,它会搜索一个默认位置
|
|
// console.log("--- Test Case 1: Searching for '上海' ---");
|
|
// await search_location({ keyword: "上海" });
|
|
// await Tools.System.sleep(2000); // Wait for page to load
|
|
|
|
// // console.log("--- Test Case 1: Clicking 'go_to_location' ---");
|
|
// await go_to_location({ "transport_mode": "飞机" });
|
|
// await Tools.System.sleep(2000); // Wait for action
|
|
|
|
// // Navigate back to home for the next test
|
|
await navigate_to_home({});
|
|
await Tools.System.sleep(3000);
|
|
|
|
console.log("--- Test Case 2: Searching for '长安大学' to test multiple results ---");
|
|
await search_location({ keyword: "长安大学" });
|
|
await Tools.System.sleep(2000);
|
|
await select_location_from_list({ index: 1 });
|
|
await Tools.System.sleep(2000);
|
|
await go_to_location({ transport_mode: "飞机" });
|
|
await Tools.System.sleep(2000);
|
|
}
|
|
|
|
// 导出所有工具
|
|
return {
|
|
search_location: async (params) => await wrapToolExecution(search_location, params),
|
|
select_location_from_list: async (params) => await wrapToolExecution(select_location_from_list, params),
|
|
go_to_location: async (params) => await wrapToolExecution(go_to_location, params),
|
|
get_directions: async (params) => await wrapToolExecution(get_directions, params),
|
|
search_nearby: async (params) => await wrapToolExecution(search_nearby, params),
|
|
navigate_to_home: async (params) => await wrapToolExecution(navigate_to_home, params),
|
|
activate_voice_assistant: async (params) => await wrapToolExecution(activate_voice_assistant, params),
|
|
main: async (params) => await wrapToolExecution(main, params),
|
|
};
|
|
})();
|
|
|
|
// 逐个导出,以便在外部调用
|
|
exports.search_location = BaiduMapAssistant.search_location;
|
|
exports.select_location_from_list = BaiduMapAssistant.select_location_from_list;
|
|
exports.go_to_location = BaiduMapAssistant.go_to_location;
|
|
exports.get_directions = BaiduMapAssistant.get_directions;
|
|
exports.search_nearby = BaiduMapAssistant.search_nearby;
|
|
exports.navigate_to_home = BaiduMapAssistant.navigate_to_home;
|
|
exports.activate_voice_assistant = BaiduMapAssistant.activate_voice_assistant;
|
|
exports.main = BaiduMapAssistant.main; |