Skip to content

附录2:主动请求(远程控制)

pppscn edited this page Aug 6, 2023 · 25 revisions

适用 v3.0.0 (含)以上版本

1、接口规范约定

没有特殊声明的接口,都是按照下面的规范进行约定:

接口地址:

http://手机IP:5000/<接口URI>  #本机局域网
http://smsf.demo.com/<接口URI>  #Frp内网穿透(绑定你自己的域名)

1.1 报文明文传输(含校验签名)

请求方式:

POST

Content-Type:

application/json; charset=utf-8

请求示例

{
    "data": {},
    "timestamp": 1652590258638,
    "sign": ""
}

公共请求参数

参数 类型 是否必填 说明
data Any 请求参数,根据具体接口而定,参见 2、接口列表
timestamp Long 当前时间戳,单位是毫秒,与请求调用时间误差不能超过1小时
sign String 当服务端安全设置为校验签名时必传,生成的sign签名,规则见下方sign校验规则

响应示例

{
    "code": 200,
    "msg": "ok",
    "data": {},
    "timestamp": 1652590258638,
    "sign": ""
}

公共响应参数

参数 类型 是否必填 说明
code Int 200=成功,500=失败
msg String 成功或失败提示
data Any 响应参数,仅当成功时返回,根据具体接口而定,参见 2、接口列表
timestamp Long 时间戳,毫秒
sign String 当服务端安全设置为校验签名且code=200时必传,生成的sign签名,规则见下方sign校验规则

sign 校验规则(参考 阿里钉钉群机器人的sign生成):

timestamp+"\n"+密钥 当做签名字符串,使用 HmacSHA256 算法计算签名,然后进行 Base64 encode,最后再把签名参数再进行urlEncode,得到最终的签名(需要使用UTF-8字符集)

1.2 报文RSA加密传输

仅适用 v3.1.0 (含)以上版本

加密方式:

  • 采用非对称加密RSA,在主动控制·服务端生成公私钥对
  • 公钥用在客户端:客户端请求报文公钥加密,服务端私钥解密
  • 私钥用在服务端:服务端应答报文私钥加密,客户端公钥解密
  • 加密前的接口报文与明文传输一致,原报文Base64编码(避免中文乱码)后,进行RSA加密后post到服务端

1.3 报文SM4加密传输

仅适用 v3.1.0 (含)以上版本,比RSA加密更安全,更快

PS. Python 调用接口示例

【注意】采用SmsF微信小程序作为客户端时,必须选用此加密方式

加密方式:

  • 采用SM4对称加密,在主动控制·服务端生成SM4密钥
  • SM4密钥用在客户端/服务端:请求/应答报文SM4密钥加密,接收端用SM4密钥解密
  • 加密前的接口报文与明文传输一致,原报文进行SM4加密后post到服务端

2、接口列表

2.0 远程查配置

调用其他接口之前建议先调用此接口,判断服务端是否已经开启该功能

接口URI

/config/query

请求示例

{
  "data": {},
  "timestamp": 1652590258638,
  "sign": ""
}

响应示例

{
    "timestamp": 1656217722037,
    "code": 200,
    "msg": "success",
    "data": {
        "enable_api_battery_query": true,
        "enable_api_call_query": true,
        "enable_api_clone": true,
        "enable_api_contact_query": true,
        "enable_api_sms_query": true,
        "enable_api_sms_send": true,
        "enable_api_wol": true,
        "extra_device_mark": "Redmi Note 4X",
        "extra_sim1": "中国移动_+8618888888888",
        "extra_sim2": "中国电信_+8619999999999",
        "sim_info_list": {
            "0": {
                "carrier_name": "中国移动",
                "country_iso": "",
                "icc_id": "89860109605910235700",
                "number": "+8618888888888",
                "sim_slot_index": 0,
                "subscription_id": 1
            },
            "1": {
                "carrier_name": "中国电信",
                "country_iso": "",
                "icc_id": "89860109605910235711",
                "number": "+8619999999999",
                "sim_slot_index": 1,
                "subscription_id": 2
            }
        }
    }
}

响应参数

参数 类型 是否必填 说明
enable_api_battery_query Boolean 远程查电量
enable_api_call_query Boolean 远程查通话
enable_api_clone Boolean 一键换新机
enable_api_contact_query Boolean 远程查话簿
enable_api_sms_query Boolean 远程查短信
enable_api_sms_send Boolean 远程发短信
enable_api_wol Boolean 远程WOL
extra_device_mark String 设备备注(v3.0.5+)
extra_sim1 String SIM1备注(v3.0.5+)(v3.0.5+)
extra_sim2 String SIM2备注(v3.0.5+)
sim_info_list MutableMap<Int, SimInfo> SIM信息列表(实时卡槽信息)(v3.0.5+)

2.1 一键换新机

2.1.1 客户端从服务端拉取配置

接口URI

/clone/pull

请求示例

{
    "data": {
        "version_code": 300038
    },
    "timestamp": 1652590258638,
    "sign": ""
}

请求参数

参数 类型 是否必填 说明
version_code Int 客户端App版本号(服务端与客户端的版本号必须一致)

响应示例

{
    "code": 200,
    "msg": "ok",
    "data": {
        "version_name": "3.0.0",
        "notify_content": "通知栏个性文案",
        "call_type1": false,
        "call_type2": false,
        "call_type3": false,
        "cancel_app_notify": false,
        "duplicate_messages_limits": 0,
        "enable_app_notify": false,
        "enable_battery_cron": true,
        "enable_battery_receiver": false,
        "enable_exclude_from_recents": false,
        "enable_help_tip": false,
        "enable_not_user_present": false,
        "enable_phone": false,
        "enable_play_silence_music": false,
        "enable_sms": false,
        "enable_sms_template": false,
        "request_delay_time": 1,
        "request_retry_times": 0,
        "request_timeout": 10,
        "battery_level_once": false,
        "battery_level_min": 0,
        "battery_level_max": 100,
        "version_code": 400038,
        "battery_cron_interval": 60,
        "battery_cron_start_time": "00:00",
        "sms_template": "",
        "sender_list": [
            {
                "time": "Apr 14, 2022 3:37:36 PM",
                "json_setting": "{\"atAll\":false,\"atMobiles\":\"\",\"secret\":\"SEC1234567890\",\"token\":\"123456789012345678901234567890\"}",
                "name": "钉钉群机器人",
                "status": 1,
                "id": 1,
                "type": 0
            }
        ],
        "rule_list": [
            {
                "check": "is",
                "filed": "package_name",
                "value": "88888888",
                "regex_replace": "",
                "type": "app",
                "sim_slot": "ALL",
                "sms_template": "",
                "time": "Apr 14, 2022 3:37:36 PM",
                "id": 1,
                "sender_id": 1,
                "status": 1
            }
        ]
    },
    "timestamp": "1652590258638",
    "sign": ""
}

响应参数

2.1.2 客户端向服务端推送配置

接口URI

/clone/push

请求示例

{
    "data": {
        "version_name": "3.0.0",
        "notify_content": "通知栏个性文案",
        "call_type1": false,
        "call_type2": false,
        "call_type3": false,
        "cancel_app_notify": false,
        "duplicate_messages_limits": 0,
        "enable_app_notify": false,
        "enable_battery_cron": true,
        "enable_battery_receiver": false,
        "enable_exclude_from_recents": false,
        "enable_help_tip": false,
        "enable_not_user_present": false,
        "enable_phone": false,
        "enable_play_silence_music": false,
        "enable_sms": false,
        "enable_sms_template": false,
        "request_delay_time": 1,
        "request_retry_times": 0,
        "request_timeout": 10,
        "battery_level_once": false,
        "battery_level_min": 0,
        "battery_level_max": 100,
        "version_code": 400038,
        "battery_cron_interval": 60,
        "battery_cron_start_time": "00:00",
        "sms_template": "",
        "sender_list": [
            {
                "time": "Apr 14, 2022 3:37:36 PM",
                "json_setting": "{\"atAll\":false,\"atMobiles\":\"\",\"secret\":\"SEC1234567890\",\"token\":\"123456789012345678901234567890\"}",
                "name": "钉钉群机器人",
                "status": 1,
                "id": 1,
                "type": 0
            }
        ],
        "rule_list": [
            {
                "check": "is",
                "filed": "package_name",
                "value": "88888888",
                "regex_replace": "",
                "type": "app",
                "sim_slot": "ALL",
                "sms_template": "",
                "time": "Apr 14, 2022 3:37:36 PM",
                "id": 1,
                "sender_id": 1,
                "status": 1
            }
        ]
    },
    "timestamp": "1652590258638",
    "sign": ""
}

请求参数

略,同拉取配置接口返回的数据

响应示例

{
    "code": 200,
    "msg": "success",
    "data": "success",
    "timestamp": 1653982995002,
    "sign": "NGxleev7ZZ%2Bd2KYCPHGw9XuoDo6Y6u7y1Pe3AZmsw6k%3D"
}

2.2 远程短信

2.2.1 远程发短信

接口URI

/sms/send

请求示例

{
  "data": {
    "sim_slot": 1,
    "phone_numbers": "15888888888;19999999999",
    "msg_content": "短信内容"
  },
  "timestamp": 1652590258638,
  "sign": ""
}

请求参数

参数 类型 是否必填 说明
sim_slot Int 发送卡槽: 1=SIM1, 2=SIM2
phone_numbers String 接收手机号码,多个手机号用半角分号分隔
msg_content String 短信内容,70个字符内算一条,超过70个字符,每增加64字符累加1条,最多390字符(6条短信)

响应示例

{
    "code": 200,
    "msg": "success",
    "data": "success",
    "timestamp": 1653982995002,
    "sign": "NGxleev7ZZ%2Bd2KYCPHGw9XuoDo6Y6u7y1Pe3AZmsw6k%3D"
}

2.2.2 远程查短信

接口URI

/sms/query

请求示例

{
  "data": {
    "type": 1,
    "page_num": 1,
    "page_size": 10,
    "keyword": "关键字",
  },
  "timestamp": 1652590258638,
  "sign": ""
}

请求参数

参数 类型 是否必填 说明
type Int 短信类型: 1=接收, 2=发送
page_num Int 页码,默认=1
page_size Int 分页大小,默认=10
keyword String 关键字,模糊匹配短信内容

响应示例

{
    "code": 200,
    "msg": "success",
    "data": [
        {
            "content": "123456",
            "number": "15806064566",
            "name": "Unknown Number",
            "type": 2,
            "date": 1653903967357,
            "sim_id": -1,
            "sub_id": 0
        }
    ],
    "timestamp": 1653922777594,
    "sign": "PsizhZ16kBh7xc6W9env9U0qITqoy8lOcjpG8FKDNNs%3D"
}

响应参数

参数 类型 是否必填 说明
name String 联系人姓名
number String 联系人号码
content String 短信内容
date Long 短信时间
type Int 短信类型: 1=接收, 2=发送
sim_id Int 卡槽ID: 0=Sim1, 1=Sim2, -1=获取失败
sub_id Int 卡槽主键: 0=获取失败, 非零=Sim卡插入手机的序号(v3.2.0+)

【注意】卡槽ID 由短信记录的 subscription_id (相当于Sim卡插入手机的记录id) 反查当前手机Sim列表的卡槽的ID,如果手机Sim卡发生改变了(例如:原来插在卡槽1收了短信,拔掉了),可能找不到对应的记录,从而返回 -1


2.3 远程查通话

接口URI

/call/query

请求示例

{
  "data": {
    "type": 1,
    "page_num": 1,
    "page_size": 10,
    "phone_number": "15888888888",
  },
  "timestamp": 1652590258638,
  "sign": ""
}

请求参数

参数 类型 是否必填 说明
type Int 通话类型:1=呼入, 2=呼出, 3=未接,0=不筛选(默认)
page_num Int 页码,默认=1
page_size Int 分页大小,默认=10
phone_number String 手机号码,模糊匹配

响应示例

{
    "code": 200,
    "msg": "success",
    "data": [
        {
            "dateLong": 1653977301182,
            "number": "911111881",
            "sim_id": -1,
            "type": 2,
            "duration": 3
        }
    ],
    "timestamp": 1653977311680,
    "sign": "MOfD66%2BptfxHvyxpTXnMdApHy6qgfQcaB0EN9sks%2F0o%3D"
}

响应参数

参数 类型 是否必填 说明
name String 姓名
number String 号码
dateLong Long 通话日期
duration Int 通话时长,秒
type Int 通话类型:1=呼入, 2=呼出, 3=未接
sim_id Int 卡槽ID: 0=Sim1, 1=Sim2, -1=获取失败

【注意】卡槽ID 由通话记录的 subscription_id (相当于Sim卡插入手机的记录id) 反查当前手机Sim列表的卡槽的ID,如果手机Sim卡发生改变了(例如:原来插在卡槽1打了电话,拔掉了),可能找不到对应的记录,从而返回 -1


2.4 远程查话簿

接口URI

/contact/query

请求示例

{
  "data": {
    "phone_number": "15888888888",
    "name": "pppscn",
  },
  "timestamp": 1652590258638,
  "sign": ""
}

请求参数

参数 类型 是否必填 说明
phone_number String 手机号码,模糊匹配
name String 姓名,模糊匹配

响应示例

{
    "code": 200,
    "msg": "success",
    "data": [
        {
            "name": "pppscn",
            "phone_number": "15888888888"
        },
        {
            "name": "paopao",
            "phone_number": "19999999999"
        }
    ],
    "timestamp": 1653977311680,
    "sign": "MOfD66%2BptfxHvyxpTXnMdApHy6qgfQcaB0EN9sks%2F0o%3D"
}

响应参数

参数 类型 是否必填 说明
name String 姓名
phone_number String 号码

2.5 远程查电量

接口URI

/battery/query

请求示例

{
  "data": {},
  "timestamp": 1652590258638,
  "sign": ""
}

响应示例

{
    "code": 200,
    "msg": "success",
    "data": {
        "level": "100%",
        "scale": "100%",
        "status": "充电中",
        "health": "良好",
        "plugged": "AC"
    },
    "timestamp": 1653925480414,
    "sign": "GBmhQgeB5iplRolsMuqZd0eU%2FEBAS0PQfxFwe5TjhcU%3D"
}

响应参数

参数 类型 是否必填 说明
level String 剩余电量
scale String 充满电量
voltage String 当前电压
temperature String 当前温度
status String 电池状态
health String 健康度
plugged String 充电器

2.6 远程WOL

PS. v3.0.8+ 适用

接口URI

/wol/send

请求示例

{
  "data": {
    "mac": "24:5E:BE:0C:45:9A",
    "ip": "192.168.168.168"
  },
  "timestamp": 1652590258638,
  "sign": ""
}

请求参数

参数 类型 是否必填 说明
mac String 网卡MAC地址
ip String 内网IP地址
port Int 端口号:7 或 9,默认:9;仅传入ip节点时有效

响应示例

{
    "code": 200,
    "msg": "success",
    "data": "success",
    "timestamp": 1653982995002,
    "sign": "NGxleev7ZZ%2Bd2KYCPHGw9XuoDo6Y6u7y1Pe3AZmsw6k%3D"
}

2.7 远程查定位

PS. v3.2.0+ 适用

接口URI

/location/query

请求示例

{
  "data": {},
  "timestamp": 1652590258638,
  "sign": ""
}

响应示例

{
    "timestamp": 1676019869423,
    "code": 200,
    "msg": "success",
    "data": {
        "address": "福建省福州市XXXXXX",
        "latitude": 26.666666,
        "longitude": 119.999999,
        "provider": "fused",
        "time": "2023-02-10 17:04:25"
    }
}

响应参数

参数 类型 是否必填 说明
address String GPS坐标逆转后地址
latitude String 纬度
longitude String 经度
provider String 供应商
time String 上一次定位时间

2.8 远程加话簿

PS. v3.2.0+ 适用

接口URI

/contact/add

请求示例

{
    "timestamp": 1676020173225,
    "sign": "Sp6P13T0BSWBjlDbISW52MyXvu3v7g5ZgHwkb%2BmHeKc%3D",
    "data": {
        "phone_number": "15888888888;19999999999",
        "name": "真实姓名"
    }
}

请求参数

参数 类型 是否必填 说明
phone_number String 多个手机号用半角分号分隔,例:15888888888;19999999999
name String 通讯录显示名称

响应示例

{
    "timestamp": 1676020173526,
    "code": 200,
    "msg": "success",
    "data": "success"
}

PS. SmsForwarder 3.x API 文档到此结束,最后修改时间:2022年10月15日



适用 v2.4.4 (含)以下版本


SmsForwarder V2.4.0 以上,可以通过 被动接收 或者 主动轮询 获取指令,从而操作本机

【注意】这只是一个先行尝试的功能(来自机油的PR),下一个版本(2.5.0)重点改造此功能
对暴露的api有什么想法欢迎提issue,在合法合规的前提下,酌情考虑会不会添加!

后续版本可能发生的改变(包括但不限于):

  • 请求与应答报文重新设计(结构、状态码等统一规范)
  • 服务端与客户端双向验签(或对称加密报文)【可选】
  • 提供简单的 SmsHub Api 服务端demo【只保留HttpServer、增加内网穿透】

被动接收本地 HttpServer

  • WiFi网络下可用,启动后局域网内其他机器可直接调用本机接口

  • 一键克隆 共用本地 HttpServer,访问URL: http://本地ip:5000/send_api

  • 接收一个list并执行操纵,设置处理结果后并在尾部追加一个心跳包后返回原list

短信发送接口(v2.4.2+ 报文结构调整如下)

接口URL:

http://你的ip/send_api

请求方式:

POST

Content-Type:

application/json; charset=utf-8

请求参数:

{
    "data": [
        {
            "action": 0,
            "target": "136227276",
            "content": "Test Msg1",
            "channel": "1"
        },
        {
            "action": 0,
            "target": "13636277",
            "content": "Test Msg2",
            "channel": "2"
        }
    ]
}

返回报文:

{
    "code": 200,
    "data": [
        {
            "action": "2",
            "channel": "SIM1",
            "content": "Test Msg1",
            "target": "18888888888",
            "ts": "1644458153622",
            "type": "sms"
        },
        {
            "action": "2",
            "channel": "SIM1",
            "content": "Test Msg2",
            "target": "18888888888",
            "ts": "1644458153651",
            "type": "sms"
        }
    ],
    "heartbeat": {
        "action": "-1",
        "channel": "SIM1:18888888888;SIM2:18888888889",
        "deviceInfo": "{\"Version\":\"2.4.1\",\"heartbeat\":\"30\",\"simOperatorName\":\"中国联通\",\"imei\":xxxxxxxxxxxxxxx\"\",\"SDKVersion\":\"29\",\"mark\":\"Mi8\"}",
        "ts": "1644458153565"
    },
    "msg": ""
}

主动轮询远程 SmsHub Api

  • 请先在通用设置中,填写服务端地址,再启动服务,APP轮询执行接口返回的操作

  • 每隔30秒发送一个心跳包,包含当前设备的信息,children里为上一次心跳后收到的所有消息,服务端需返回一个 list

报文结构:

[{
    "action": "0", //发送短信操作(暂时只支持发送操作)
    "target": "88888",//收件人手机号
    "content": "xxx",//内容
    "channel": "1"//卡槽 1或2
},
{...
}]

附录:

//唯一id
private String msgId;
//心跳数据时发送的设备名
private String deviceInfo;
//卡槽信息
private String channel;
//消息内容
private String content;
//错误消息
private String errMsg;
//手机号(;分隔)或包名
private String target;
//状态或操作 0:发送短信, 1:接收到的消息, 2:操作处理成功, 3:操作处理失败, -1:心跳包 (包含deviceInfo字段,children里带有两次心跳间收到的消息)
private String action;
//消息类型 app:通知 phone:来电, sms:短信, battery:电池信息
private String type;
//时间戳
private String ts;
//两次交互之间接收到的消息
private List<SmsHubVo> children;