feat: publish workphone SDK deployment and API docs
This commit is contained in:
300
sdk/agent/skills/xianyu/skill.py
Normal file
300
sdk/agent/skills/xianyu/skill.py
Normal 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)
|
||||
Reference in New Issue
Block a user