feat: publish workphone SDK deployment and API docs

This commit is contained in:
Manus AI
2026-07-14 18:10:52 +08:00
commit 021d633cc1
534 changed files with 122391 additions and 0 deletions

View File

@@ -0,0 +1,300 @@
"""
闲鱼控制技能 - Agent端实现
功能模块:
1. 消息管理 - 发送/获取私信
2. 联系人 - 获取最近聊天、关注用户
3. 商品/聊天 - 与买家/卖家沟通
@author 卡若
@version 3.0.0
"""
import time
import logging
from typing import Dict, Any, List
import sys
import os
_agent_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
if _agent_dir not in sys.path:
sys.path.insert(0, _agent_dir)
from skills.base import BaseSkill
from error_handler import retry, with_error_handling
logger = logging.getLogger(__name__)
class XianyuSkill(BaseSkill):
"""
闲鱼控制技能
支持的操作:
- send_message: 发送私信
- get_messages: 获取私信列表
- get_contacts: 获取最近联系人/聊天列表
- add_friend: 关注用户(闲鱼为「关注」)
- batch_send_message: 批量发送私信
"""
PACKAGE = "com.taobao.idlefish"
NAME = "闲鱼"
# ==========================================================================
# 一、消息管理
# ==========================================================================
@with_error_handling("发送闲鱼私信")
@retry(max_retries=2, delay=0.5)
def send_message(self, to_id: str, content: str, msg_type: str = "text") -> Dict[str, Any]:
"""
发送私信
Args:
to_id: 用户昵称或ID
content: 消息内容
msg_type: 消息类型 (text/image)
Returns:
执行结果
"""
try:
self.launch()
self.wait_for_app_ready()
# 进入消息页(闲鱼底部通常有「消息」)
self.click_text("消息") or self.click_desc("消息")
self.sleep(1)
# 搜索用户或从列表进入
if self.click_text("搜索") or self.click_desc("搜索"):
self.sleep(0.5)
self.input_text(to_id)
self.sleep(1.5)
if not self.click_text(to_id) and not self.click_contains(to_id):
return {"success": False, "error": f"未找到用户: {to_id}"}
self.sleep(1)
else:
# 从聊天列表点击
if not self.click_contains(to_id) and not self.click_text(to_id):
return {"success": False, "error": f"未找到会话: {to_id}"}
self.sleep(1)
# 确保在聊天界面
if not self.exists("发送") and not self.exists("输入"):
return {"success": False, "error": "未能进入聊天界面"}
# 输入并发送
self.input_text(content, clear=False)
self.sleep(0.3)
self.click_text("发送") or self.click_desc("发送")
logger.info(f"闲鱼私信发送成功: {to_id}")
return {
"success": True,
"message_id": f"xy_{int(time.time() * 1000)}"
}
except Exception as e:
logger.error(f"闲鱼发送失败: {e}")
return {"success": False, "error": str(e)}
def get_messages(self, limit: int = 20, conversation_id: str = None) -> Dict[str, Any]:
"""
获取私信列表
Args:
limit: 消息数量限制
conversation_id: 会话ID用户昵称指定则取该会话消息
Returns:
消息列表
"""
try:
self.launch()
self.wait_for_app_ready()
self.click_text("消息") or self.click_desc("消息")
self.sleep(1)
messages = []
if conversation_id:
if self.click_text("搜索") or self.click_desc("搜索"):
self.sleep(0.5)
self.input_text(conversation_id)
self.sleep(1.5)
self.click_text(conversation_id) or self.click_contains(conversation_id)
self.sleep(1)
ui_tree = self.d.dump_hierarchy()
import xml.etree.ElementTree as ET
try:
root = ET.fromstring(ui_tree)
for elem in root.iter():
text = elem.get("text", "")
if text and len(text) > 0:
if "发送" not in text and "输入" not in text:
messages.append({
"text": text,
"timestamp": int(time.time() * 1000)
})
except Exception:
pass
return {
"success": True,
"messages": messages[:limit],
"count": len(messages[:limit])
}
except Exception as e:
return {"success": False, "error": str(e), "messages": []}
def batch_send_message(
self,
to_ids: List[str],
content: str,
interval: float = 3.0
) -> Dict[str, Any]:
"""批量发送私信"""
results = []
success_count = 0
failed_count = 0
for to_id in to_ids:
try:
result = self.send_message(to_id, content)
if result.get("success"):
success_count += 1
else:
failed_count += 1
results.append({
"to_id": to_id,
"success": result.get("success", False),
"error": result.get("error"),
"message_id": result.get("message_id")
})
time.sleep(interval)
except Exception as e:
failed_count += 1
results.append({"to_id": to_id, "success": False, "error": str(e)})
return {
"success": True,
"success_count": success_count,
"failed_count": failed_count,
"results": results
}
# ==========================================================================
# 二、联系人 / 关注
# ==========================================================================
def get_contacts(self, limit: int = 100) -> Dict[str, Any]:
"""获取最近聊天/联系人列表(与微信技能接口保持一致)"""
try:
self.launch()
self.wait_for_app_ready()
self.click_text("消息") or self.click_desc("消息")
self.sleep(1)
contacts = []
ui_tree = self.d.dump_hierarchy()
import xml.etree.ElementTree as ET
try:
root = ET.fromstring(ui_tree)
for elem in root.iter():
text = elem.get("text", "")
if text and len(text) > 0:
if "消息" not in text and "搜索" not in text:
contacts.append({"name": text})
except Exception:
pass
return {
"success": True,
"contacts": contacts[:limit],
"count": len(contacts[:limit])
}
except Exception as e:
return {"success": False, "error": str(e), "contacts": []}
def add_friend(self, user_id: str, message: str = "") -> Dict[str, Any]:
"""关注用户(闲鱼为「关注」)- 与微信技能接口保持一致"""
return self.follow_user(user_id)
def accept_friend(self, user_id: str = None) -> Dict[str, Any]:
"""通过好友请求 - 闲鱼无此入口,占位"""
return {"success": False, "error": "闲鱼暂不支持通过好友请求"}
def set_remark(self, user_id: str, remark: str) -> Dict[str, Any]:
"""设置备注 - 闲鱼无此能力,占位"""
return {"success": False, "error": "闲鱼暂不支持设置备注"}
def delete_friend(self, user_id: str) -> Dict[str, Any]:
"""删除好友 - 闲鱼为取消关注"""
return self.unfollow_user(user_id)
def batch_add_friend(self, user_ids: List[str], message: str = "", interval: float = 5.0) -> Dict[str, Any]:
"""批量关注用户"""
results = []
success_count = 0
failed_count = 0
for uid in user_ids:
try:
r = self.add_friend(uid, message)
ok = r.get("success", False)
if ok:
success_count += 1
else:
failed_count += 1
results.append({"user_id": uid, "success": ok, "error": r.get("error")})
time.sleep(max(interval, 2.0))
except Exception as e:
failed_count += 1
results.append({"user_id": uid, "success": False, "error": str(e)})
return {"success_count": success_count, "failed_count": failed_count, "results": results}
def follow_user(self, user_id: str) -> Dict[str, Any]:
"""关注用户"""
try:
self.launch()
self.wait_for_app_ready()
self._search_user(user_id)
if self.click_text("关注"):
return {"success": True}
if self.exists("已关注"):
return {"success": True, "message": "已关注"}
return {"success": False, "error": "关注失败"}
except Exception as e:
return {"success": False, "error": str(e)}
def unfollow_user(self, user_id: str) -> Dict[str, Any]:
"""取消关注"""
try:
self.launch()
self.wait_for_app_ready()
self._search_user(user_id)
if self.click_text("已关注"):
self.sleep(0.5)
self.click_text("取消关注")
return {"success": True}
return {"success": False, "error": "取消关注失败"}
except Exception as e:
return {"success": False, "error": str(e)}
def _search_user(self, user_id: str):
"""搜索用户(内部方法)"""
self.click_desc("搜索") or self.click_text("搜索")
self.sleep(0.5)
self.input_text(user_id)
self.sleep(1)
self.click_text(user_id) or self.click_contains(user_id)
self.sleep(1)