741 lines
25 KiB
Python
741 lines
25 KiB
Python
"""
|
||
微信全量操作路由 — 补全统一路由缺失的端点
|
||
==========================================
|
||
|
||
对齐 agent 端 128 个 RPC 方法,通过 WechatSkillV2 统一分发。
|
||
所有操作通过 WiFi Frida 无线执行,无需 USB。
|
||
|
||
新增端点(统一路由已有的不重复):
|
||
- 视频号操作(6个)
|
||
- 小程序操作(3个)
|
||
- 收藏管理(3个)
|
||
- 公众号操作(4个)
|
||
- 账号安全(8个)
|
||
- 支付操作(6个)
|
||
- 浮窗管理(2个)
|
||
- 表情管理(2个)
|
||
- 搜索(1个)
|
||
- 设备信息(3个)
|
||
"""
|
||
from __future__ import annotations
|
||
import logging
|
||
from typing import Optional, List
|
||
|
||
from fastapi import APIRouter, HTTPException
|
||
from pydantic import BaseModel, Field
|
||
|
||
router = APIRouter(prefix="/wechat", tags=["微信全量操作"])
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
# ============================================================
|
||
# § 1 请求模型
|
||
# ============================================================
|
||
|
||
class WechatBaseRequest(BaseModel):
|
||
device_id: str
|
||
timeout: int = 30
|
||
|
||
class WxidRequest(WechatBaseRequest):
|
||
wxid: str
|
||
|
||
class SearchRequest(WechatBaseRequest):
|
||
keyword: str
|
||
limit: int = 50
|
||
|
||
class ChannelVideoRequest(WechatBaseRequest):
|
||
video_id: str
|
||
|
||
class ChannelCommentRequest(WechatBaseRequest):
|
||
video_id: str
|
||
comment: str
|
||
|
||
class ChannelFollowRequest(WechatBaseRequest):
|
||
channel_id: str
|
||
|
||
class ShareChannelRequest(WechatBaseRequest):
|
||
video_id: str
|
||
to_id: str
|
||
|
||
class MiniProgramRequest(WechatBaseRequest):
|
||
app_id: str
|
||
path: str = ""
|
||
|
||
class ShareMiniProgramRequest(WechatBaseRequest):
|
||
app_id: str
|
||
to_id: str
|
||
title: str = ""
|
||
|
||
class FavoriteRequest(WechatBaseRequest):
|
||
msg_svr_id: str = ""
|
||
local_id: str = ""
|
||
type: str = "message"
|
||
|
||
class OfficialAccountRequest(WechatBaseRequest):
|
||
account_id: str
|
||
|
||
class OfficialAccountArticlesRequest(WechatBaseRequest):
|
||
account_id: str
|
||
limit: int = 10
|
||
|
||
class PasswordRequest(WechatBaseRequest):
|
||
old_password: str
|
||
new_password: str
|
||
|
||
class PhoneRequest(WechatBaseRequest):
|
||
phone: str
|
||
|
||
class DeviceRemoveRequest(WechatBaseRequest):
|
||
target_device_id: str
|
||
|
||
class ToggleRequest(WechatBaseRequest):
|
||
enable: bool = True
|
||
|
||
class RedPacketRequest(WechatBaseRequest):
|
||
to_id: str
|
||
amount: str
|
||
message: str = "恭喜发财"
|
||
|
||
class TransferRequest(WechatBaseRequest):
|
||
to_id: str
|
||
amount: str
|
||
description: str = ""
|
||
|
||
class ReceiveRequest(WechatBaseRequest):
|
||
msg_svr_id: str
|
||
|
||
class FloatRequest(WechatBaseRequest):
|
||
wxid: str
|
||
|
||
class EmojiSendRequest(WechatBaseRequest):
|
||
to_id: str
|
||
emoji_md5: str
|
||
|
||
class EmojiAddRequest(WechatBaseRequest):
|
||
image_path: str
|
||
|
||
class ProfileSetRequest(WechatBaseRequest):
|
||
value: str
|
||
|
||
class PrivacyRequest(WechatBaseRequest):
|
||
setting: str
|
||
value: bool = True
|
||
|
||
class NotificationRequest(WechatBaseRequest):
|
||
type: str = "all"
|
||
enable: bool = True
|
||
|
||
class ChatBgRequest(WechatBaseRequest):
|
||
wxid: str
|
||
image_path: str = ""
|
||
|
||
class DndRequest(WechatBaseRequest):
|
||
wxid: str
|
||
enable: bool = True
|
||
|
||
class PinChatRequest(WechatBaseRequest):
|
||
wxid: str
|
||
pin: bool = True
|
||
|
||
class QrScanRequest(WechatBaseRequest):
|
||
image_path: str = ""
|
||
|
||
class LabelContactRequest(WechatBaseRequest):
|
||
wxid: str
|
||
label_ids: List[str]
|
||
|
||
|
||
# ============================================================
|
||
# § 2 辅助函数
|
||
# ============================================================
|
||
|
||
async def _get_skill(device_id: str):
|
||
"""获取 WechatSkillV2 实例"""
|
||
from skills.wechat.skill_v2 import WechatSkillV2
|
||
return WechatSkillV2(device_id=device_id)
|
||
|
||
|
||
# ============================================================
|
||
# § 3 视频号操作(H27)
|
||
# ============================================================
|
||
|
||
@router.get("/channels/browse", response_model=dict)
|
||
async def browse_channels(device_id: str, limit: int = 10):
|
||
"""浏览视频号"""
|
||
skill = await _get_skill(device_id)
|
||
return await skill.browse_channels(limit)
|
||
|
||
@router.post("/channels/like", response_model=dict)
|
||
async def like_channel_video(req: ChannelVideoRequest):
|
||
"""视频号点赞"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.like_channel_video(req.video_id)
|
||
|
||
@router.post("/channels/comment", response_model=dict)
|
||
async def comment_channel_video(req: ChannelCommentRequest):
|
||
"""视频号评论"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.comment_channel_video(req.video_id, req.comment)
|
||
|
||
@router.post("/channels/follow", response_model=dict)
|
||
async def follow_channel(req: ChannelFollowRequest):
|
||
"""关注视频号"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.follow_channel(req.channel_id)
|
||
|
||
@router.post("/channels/unfollow", response_model=dict)
|
||
async def unfollow_channel(req: ChannelFollowRequest):
|
||
"""取消关注视频号"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.unfollow_channel(req.channel_id)
|
||
|
||
@router.post("/channels/share", response_model=dict)
|
||
async def share_channel_video(req: ShareChannelRequest):
|
||
"""分享视频号视频"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.share_channel_video(req.video_id, req.to_id)
|
||
|
||
|
||
# ============================================================
|
||
# § 4 小程序操作(H32)
|
||
# ============================================================
|
||
|
||
@router.post("/mini-program/open", response_model=dict)
|
||
async def open_mini_program(req: MiniProgramRequest):
|
||
"""打开小程序"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.open_mini_program(req.app_id, req.path)
|
||
|
||
@router.get("/mini-program/recent", response_model=dict)
|
||
async def get_recent_mini_programs(device_id: str):
|
||
"""获取最近使用的小程序"""
|
||
skill = await _get_skill(device_id)
|
||
return await skill.get_recent_mini_programs()
|
||
|
||
@router.post("/mini-program/share", response_model=dict)
|
||
async def share_mini_program(req: ShareMiniProgramRequest):
|
||
"""分享小程序"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.share_mini_program(req.app_id, req.to_id, req.title)
|
||
|
||
|
||
# ============================================================
|
||
# § 5 收藏管理(H29)
|
||
# ============================================================
|
||
|
||
@router.get("/favorites", response_model=dict)
|
||
async def get_favorites(device_id: str, limit: int = 50):
|
||
"""获取收藏列表"""
|
||
skill = await _get_skill(device_id)
|
||
return await skill.get_favorites(limit)
|
||
|
||
@router.post("/favorites/add", response_model=dict)
|
||
async def add_favorite(req: FavoriteRequest):
|
||
"""添加收藏"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.add_favorite(req.msg_svr_id, req.type)
|
||
|
||
@router.delete("/favorites/{local_id}", response_model=dict)
|
||
async def delete_favorite(local_id: str, device_id: str = ""):
|
||
"""删除收藏"""
|
||
skill = await _get_skill(device_id)
|
||
return await skill.delete_favorite(local_id)
|
||
|
||
|
||
# ============================================================
|
||
# § 6 公众号操作(H36)
|
||
# ============================================================
|
||
|
||
@router.get("/official-accounts", response_model=dict)
|
||
async def get_official_accounts(device_id: str, limit: int = 100):
|
||
"""获取关注的公众号列表"""
|
||
skill = await _get_skill(device_id)
|
||
return await skill.get_official_accounts(limit)
|
||
|
||
@router.post("/official-accounts/follow", response_model=dict)
|
||
async def follow_official_account(req: OfficialAccountRequest):
|
||
"""关注公众号"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.follow_official_account(req.account_id)
|
||
|
||
@router.post("/official-accounts/unfollow", response_model=dict)
|
||
async def unfollow_official_account(req: OfficialAccountRequest):
|
||
"""取消关注公众号"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.unfollow_official_account(req.account_id)
|
||
|
||
@router.get("/official-accounts/{account_id}/articles", response_model=dict)
|
||
async def get_official_account_articles(account_id: str, device_id: str = "", limit: int = 10):
|
||
"""获取公众号文章"""
|
||
skill = await _get_skill(device_id)
|
||
return await skill.get_official_account_articles(account_id, limit)
|
||
|
||
|
||
# ============================================================
|
||
# § 7 账号安全(H24)
|
||
# ============================================================
|
||
|
||
@router.post("/security/unblock", response_model=dict)
|
||
async def unblock_self(req: WechatBaseRequest):
|
||
"""自助解封"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.unblock_self()
|
||
|
||
@router.post("/security/change-password", response_model=dict)
|
||
async def change_password(req: PasswordRequest):
|
||
"""修改密码"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.change_password(req.old_password, req.new_password)
|
||
|
||
@router.post("/security/bind-phone", response_model=dict)
|
||
async def bind_phone(req: PhoneRequest):
|
||
"""绑定手机号"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.bind_phone(req.phone)
|
||
|
||
@router.post("/security/unbind-phone", response_model=dict)
|
||
async def unbind_phone(req: WechatBaseRequest):
|
||
"""解绑手机号"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.unbind_phone()
|
||
|
||
@router.get("/security/login-devices", response_model=dict)
|
||
async def get_login_devices(device_id: str):
|
||
"""获取登录设备列表"""
|
||
skill = await _get_skill(device_id)
|
||
return await skill.get_login_devices()
|
||
|
||
@router.delete("/security/login-device", response_model=dict)
|
||
async def remove_login_device(req: DeviceRemoveRequest):
|
||
"""移除登录设备"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.remove_login_device(req.target_device_id)
|
||
|
||
@router.post("/security/fingerprint", response_model=dict)
|
||
async def enable_fingerprint(req: ToggleRequest):
|
||
"""启用/禁用指纹"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.enable_fingerprint(req.enable)
|
||
|
||
@router.post("/security/account-protection", response_model=dict)
|
||
async def set_account_protection(req: ToggleRequest):
|
||
"""设置账号保护"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.set_account_protection(req.enable)
|
||
|
||
|
||
# ============================================================
|
||
# § 8 支付操作(H25)
|
||
# ============================================================
|
||
|
||
@router.post("/pay/red-packet", response_model=dict)
|
||
async def send_red_packet(req: RedPacketRequest):
|
||
"""发红包"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.send_red_packet(req.to_id, req.amount, req.message)
|
||
|
||
@router.post("/pay/red-packet/receive", response_model=dict)
|
||
async def receive_red_packet(req: ReceiveRequest):
|
||
"""收红包"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.receive_red_packet(req.msg_svr_id)
|
||
|
||
@router.post("/pay/transfer", response_model=dict)
|
||
async def send_transfer(req: TransferRequest):
|
||
"""转账"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.send_transfer(req.to_id, req.amount, req.description)
|
||
|
||
@router.post("/pay/transfer/receive", response_model=dict)
|
||
async def receive_transfer(req: ReceiveRequest):
|
||
"""收转账"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.receive_transfer(req.msg_svr_id)
|
||
|
||
@router.get("/pay/balance", response_model=dict)
|
||
async def get_wallet_balance(device_id: str):
|
||
"""获取钱包余额"""
|
||
skill = await _get_skill(device_id)
|
||
return await skill.get_wallet_balance()
|
||
|
||
@router.get("/pay/transactions", response_model=dict)
|
||
async def get_transaction_history(device_id: str, limit: int = 20):
|
||
"""获取交易记录"""
|
||
skill = await _get_skill(device_id)
|
||
return await skill.get_transaction_history(limit)
|
||
|
||
|
||
# ============================================================
|
||
# § 9 浮窗管理(H38)
|
||
# ============================================================
|
||
|
||
@router.post("/float/add", response_model=dict)
|
||
async def add_to_float(req: FloatRequest):
|
||
"""添加到浮窗"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.add_to_float(req.wxid)
|
||
|
||
@router.post("/float/remove", response_model=dict)
|
||
async def remove_from_float(req: FloatRequest):
|
||
"""从浮窗移除"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.remove_from_float(req.wxid)
|
||
|
||
|
||
# ============================================================
|
||
# § 10 表情管理(H37)
|
||
# ============================================================
|
||
|
||
@router.post("/emoji/send-custom", response_model=dict)
|
||
async def send_emoji_custom(req: EmojiSendRequest):
|
||
"""发送自定义表情"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.send_emoji(req.to_id, req.emoji_md5)
|
||
|
||
@router.post("/emoji/add-custom", response_model=dict)
|
||
async def add_custom_emoji(req: EmojiAddRequest):
|
||
"""添加自定义表情"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.add_custom_emoji(req.image_path)
|
||
|
||
|
||
# ============================================================
|
||
# § 11 账号管理(H23)
|
||
# ============================================================
|
||
|
||
@router.get("/profile", response_model=dict)
|
||
async def get_profile(device_id: str):
|
||
"""获取个人资料"""
|
||
skill = await _get_skill(device_id)
|
||
return await skill.get_profile()
|
||
|
||
@router.get("/account/status", response_model=dict)
|
||
async def check_account_status(device_id: str):
|
||
"""检查账号状态"""
|
||
skill = await _get_skill(device_id)
|
||
return await skill.check_account_status()
|
||
|
||
@router.post("/profile/nickname", response_model=dict)
|
||
async def set_nickname(req: ProfileSetRequest):
|
||
"""设置昵称"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.set_nickname(req.value)
|
||
|
||
@router.post("/profile/signature", response_model=dict)
|
||
async def set_signature(req: ProfileSetRequest):
|
||
"""设置签名"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.set_signature(req.value)
|
||
|
||
@router.post("/profile/avatar", response_model=dict)
|
||
async def set_avatar(req: ProfileSetRequest):
|
||
"""设置头像"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.set_avatar(req.value)
|
||
|
||
@router.post("/profile/sex", response_model=dict)
|
||
async def set_sex(req: ProfileSetRequest):
|
||
"""设置性别"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.set_sex(req.value)
|
||
|
||
@router.post("/profile/region", response_model=dict)
|
||
async def set_region(req: ProfileSetRequest):
|
||
"""设置地区"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.set_region(req.value)
|
||
|
||
@router.post("/profile/status", response_model=dict)
|
||
async def set_what_up(req: ProfileSetRequest):
|
||
"""设置状态"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.set_what_up(req.value)
|
||
|
||
|
||
# ============================================================
|
||
# § 12 搜索(H31)
|
||
# ============================================================
|
||
|
||
@router.get("/search", response_model=dict)
|
||
async def global_search(device_id: str, keyword: str, limit: int = 30):
|
||
"""全局搜索"""
|
||
skill = await _get_skill(device_id)
|
||
return await skill.global_search(keyword, limit)
|
||
|
||
|
||
# ============================================================
|
||
# § 13 设置(H30)
|
||
# ============================================================
|
||
|
||
@router.post("/settings/privacy", response_model=dict)
|
||
async def set_privacy(req: PrivacyRequest):
|
||
"""设置隐私"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.set_privacy(req.setting, req.value)
|
||
|
||
@router.post("/settings/notification", response_model=dict)
|
||
async def set_notification(req: NotificationRequest):
|
||
"""设置通知"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.set_notification(req.type, req.enable)
|
||
|
||
@router.post("/settings/clear-history", response_model=dict)
|
||
async def clear_chat_history(req: WxidRequest):
|
||
"""清除聊天记录"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.clear_chat_history(req.wxid)
|
||
|
||
@router.post("/settings/chat-background", response_model=dict)
|
||
async def set_chat_background(req: ChatBgRequest):
|
||
"""设置聊天背景"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.set_chat_background(req.wxid, req.image_path)
|
||
|
||
@router.post("/settings/dnd", response_model=dict)
|
||
async def set_do_not_disturb(req: DndRequest):
|
||
"""设置免打扰"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.set_do_not_disturb(req.wxid, req.enable)
|
||
|
||
@router.post("/settings/pin-chat", response_model=dict)
|
||
async def pin_chat(req: PinChatRequest):
|
||
"""置顶聊天"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.pin_chat(req.wxid, req.pin)
|
||
|
||
|
||
# ============================================================
|
||
# § 14 二维码(H26)
|
||
# ============================================================
|
||
|
||
@router.post("/qr/scan", response_model=dict)
|
||
async def scan_qr_code(req: QrScanRequest):
|
||
"""扫描二维码"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.scan_qr_code(req.image_path)
|
||
|
||
@router.get("/qr/my", response_model=dict)
|
||
async def generate_my_qr_code(device_id: str):
|
||
"""生成我的二维码"""
|
||
skill = await _get_skill(device_id)
|
||
return await skill.generate_my_qr_code()
|
||
|
||
@router.get("/qr/group/{group_id}", response_model=dict)
|
||
async def generate_group_qr_code(group_id: str, device_id: str = ""):
|
||
"""生成群二维码"""
|
||
skill = await _get_skill(device_id)
|
||
return await skill.generate_group_qr_code(group_id)
|
||
|
||
|
||
# ============================================================
|
||
# § 15 设备信息(H39)
|
||
# ============================================================
|
||
|
||
@router.get("/device-info", response_model=dict)
|
||
async def get_device_info(device_id: str):
|
||
"""获取设备信息"""
|
||
skill = await _get_skill(device_id)
|
||
return await skill.get_device_info()
|
||
|
||
@router.get("/storage-info", response_model=dict)
|
||
async def get_storage_info(device_id: str):
|
||
"""获取存储信息"""
|
||
skill = await _get_skill(device_id)
|
||
return await skill.get_storage_info()
|
||
|
||
@router.get("/network-info", response_model=dict)
|
||
async def get_network_info(device_id: str):
|
||
"""获取网络信息"""
|
||
skill = await _get_skill(device_id)
|
||
return await skill.get_network_info()
|
||
|
||
|
||
# ============================================================
|
||
# § 16 标签补全(H28)
|
||
# ============================================================
|
||
|
||
@router.post("/tag/set-contact", response_model=dict)
|
||
async def set_contact_label(req: LabelContactRequest):
|
||
"""设置联系人标签"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.set_contact_label(req.wxid, req.label_ids)
|
||
|
||
@router.get("/tag/contacts/{label_id}", response_model=dict)
|
||
async def get_contacts_by_label(label_id: str, device_id: str = ""):
|
||
"""按标签获取联系人"""
|
||
skill = await _get_skill(device_id)
|
||
return await skill.get_contacts_by_label(label_id)
|
||
|
||
|
||
# ============================================================
|
||
# § 17 Hook 状态
|
||
# ============================================================
|
||
|
||
@router.get("/hook/status", response_model=dict)
|
||
async def get_hook_status(device_id: str):
|
||
"""获取 Hook 状态"""
|
||
skill = await _get_skill(device_id)
|
||
return await skill.get_hook_status()
|
||
|
||
@router.get("/hook/process-info", response_model=dict)
|
||
async def get_process_info(device_id: str):
|
||
"""获取微信进程信息"""
|
||
skill = await _get_skill(device_id)
|
||
return await skill.get_process_info()
|
||
|
||
@router.get("/hook/wechat-version", response_model=dict)
|
||
async def get_wechat_version(device_id: str):
|
||
"""获取微信版本"""
|
||
skill = await _get_skill(device_id)
|
||
return await skill.get_wechat_version()
|
||
|
||
|
||
# ============================================================
|
||
# § 17 矩阵 H40 补全(128 canonical 扩展)
|
||
# ============================================================
|
||
|
||
@router.get("/ping", response_model=dict)
|
||
async def ping(device_id: str):
|
||
skill = await _get_skill(device_id)
|
||
return await skill.ping()
|
||
|
||
@router.get("/safety-center", response_model=dict)
|
||
async def get_safety_center(device_id: str):
|
||
skill = await _get_skill(device_id)
|
||
return await skill.get_safety_center()
|
||
|
||
@router.get("/restrictions", response_model=dict)
|
||
async def check_restrictions(device_id: str):
|
||
skill = await _get_skill(device_id)
|
||
return await skill.check_restrictions()
|
||
|
||
@router.get("/top-stories", response_model=dict)
|
||
async def get_top_stories(device_id: str):
|
||
skill = await _get_skill(device_id)
|
||
return await skill.get_top_stories()
|
||
|
||
@router.get("/steps", response_model=dict)
|
||
async def get_wechat_steps(device_id: str):
|
||
skill = await _get_skill(device_id)
|
||
return await skill.get_wechat_steps()
|
||
|
||
@router.get("/stickers", response_model=dict)
|
||
async def get_sticker_list(device_id: str):
|
||
skill = await _get_skill(device_id)
|
||
return await skill.get_sticker_list()
|
||
|
||
@router.post("/pay/show-code", response_model=dict)
|
||
async def show_payment_code(device_id: str):
|
||
skill = await _get_skill(device_id)
|
||
return await skill.show_payment_code()
|
||
|
||
@router.post("/steps/like", response_model=dict)
|
||
async def like_wechat_steps(device_id: str, user_id: str = "filehelper"):
|
||
skill = await _get_skill(device_id)
|
||
return await skill.like_wechat_steps(user_id)
|
||
|
||
@router.post("/cache/clear", response_model=dict)
|
||
async def clear_cache(device_id: str):
|
||
skill = await _get_skill(device_id)
|
||
return await skill.clear_cache()
|
||
|
||
@router.get("/update/check", response_model=dict)
|
||
async def check_for_update(device_id: str):
|
||
skill = await _get_skill(device_id)
|
||
return await skill.check_for_update()
|
||
|
||
class CallRequest(WechatBaseRequest):
|
||
to_id: str
|
||
|
||
@router.post("/call/voice", response_model=dict)
|
||
async def voice_call(req: CallRequest):
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.voice_call(req.to_id)
|
||
|
||
@router.post("/call/video", response_model=dict)
|
||
async def video_call(req: CallRequest):
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.video_call(req.to_id)
|
||
|
||
@router.post("/location/share-realtime", response_model=dict)
|
||
async def share_real_time_location(req: CallRequest):
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.share_real_time_location(req.to_id)
|
||
|
||
class DownloadFileRequest(WechatBaseRequest):
|
||
url: str
|
||
save_path: str = "/sdcard/Download/wp_download.bin"
|
||
|
||
@router.post("/file/download", response_model=dict)
|
||
async def download_file(req: DownloadFileRequest):
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.download_file(req.url, req.save_path)
|
||
|
||
class MassSendRequest(WechatBaseRequest):
|
||
user_ids: List[str]
|
||
content: str
|
||
|
||
@router.post("/message/mass-send", response_model=dict)
|
||
async def mass_send(req: MassSendRequest):
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.mass_send(req.user_ids, req.content)
|
||
|
||
@router.post("/message/batch-send", response_model=dict)
|
||
async def batch_send(req: MassSendRequest):
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.batch_send(req.user_ids, req.content)
|
||
|
||
class ReplyCommentRequest(WechatBaseRequest):
|
||
sns_id: str
|
||
comment: str
|
||
|
||
@router.post("/moments/reply-comment", response_model=dict)
|
||
async def reply_comment(req: ReplyCommentRequest):
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.reply_comment(req.sns_id, req.comment)
|
||
|
||
class ReceivePaymentRequest(WechatBaseRequest):
|
||
msg_svr_id: str
|
||
|
||
@router.post("/pay/receive", response_model=dict)
|
||
async def receive_payment(req: ReceivePaymentRequest):
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.receive_payment(req.msg_svr_id)
|
||
|
||
|
||
# ============================================================
|
||
# § 18 统一执行入口
|
||
# ============================================================
|
||
|
||
class UnifiedExecuteRequest(BaseModel):
|
||
device_id: str
|
||
action: str
|
||
params: dict = Field(default_factory=dict)
|
||
timeout: int = 30
|
||
|
||
@router.post("/execute", response_model=dict)
|
||
async def unified_execute(req: UnifiedExecuteRequest):
|
||
"""
|
||
统一执行入口 — 支持所有 128 个微信 canonical action(174 catalog 含 alias)
|
||
|
||
通过 action 名直接调用对应的 Frida RPC 方法。
|
||
通道自动选择:Hook > WebSocket Agent > UI 自动化
|
||
"""
|
||
skill = await _get_skill(req.device_id)
|
||
return await skill.execute(req.action, req.params)
|
||
|
||
@router.get("/actions", response_model=dict)
|
||
async def list_all_actions():
|
||
"""列出所有支持的微信操作(128 canonical + 174 catalog)"""
|
||
from skills.wechat.skill_v2 import WECHAT_ACTIONS, MODULES, WechatSkillV2
|
||
catalog = WechatSkillV2.get_catalog_action_list()
|
||
return {
|
||
"success": True,
|
||
"total_actions": len(WECHAT_ACTIONS),
|
||
"catalog_actions": len(catalog),
|
||
"total_modules": len(MODULES),
|
||
"modules": {k: v["name"] for k, v in MODULES.items()},
|
||
"actions": sorted(WECHAT_ACTIONS.keys()),
|
||
"catalog": catalog,
|
||
}
|