-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
附录2:主动请求(远程控制)
没有特殊声明的接口,都是按照下面的规范进行约定:
接口地址:
http://手机IP:5000/<接口URI> #本机局域网
http://smsf.demo.com/<接口URI> #Frp内网穿透(绑定你自己的域名)
请求方式:
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字符集)
仅适用 v3.1.0
(含)以上版本
加密方式:
- 采用非对称加密RSA,在
主动控制·服务端
生成公私钥对 - 公钥用在客户端:客户端请求报文公钥加密,服务端私钥解密
- 私钥用在服务端:服务端应答报文私钥加密,客户端公钥解密
- 加密前的接口报文与明文传输一致,原报文Base64编码(避免中文乱码)后,进行RSA加密后
post
到服务端
仅适用 v3.1.0
(含)以上版本,比RSA加密更安全,更快
PS. Python 调用接口示例
【注意】采用SmsF
微信小程序作为客户端时,必须选用此加密方式
加密方式:
- 采用SM4对称加密,在
主动控制·服务端
生成SM4密钥
- SM4密钥用在客户端/服务端:请求/应答报文
SM4密钥
加密,接收端用SM4密钥
解密 - 加密前的接口报文与明文传输一致,原报文进行SM4加密后
post
到服务端
调用其他接口之前建议先调用此接口,判断服务端是否已经开启该功能
接口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+) |
接口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": ""
}
响应参数
略
接口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"
}
接口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"
}
接口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
接口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
接口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 | 是 | 号码 |
接口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 | 是 | 充电器 |
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"
}
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 | 是 | 上一次定位时间 |
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日
SmsForwarder V2.4.0 以上,可以通过 被动接收 或者 主动轮询 获取指令,从而操作本机
【注意】这只是一个先行尝试的功能(来自机油的PR),下一个版本(2.5.0)重点改造此功能
对暴露的api有什么想法欢迎提issue,在合法合规的前提下,酌情考虑会不会添加!
后续版本可能发生的改变(包括但不限于):
- 请求与应答报文重新设计(结构、状态码等统一规范)
- 服务端与客户端双向验签(或对称加密报文)【可选】
-
提供简单的 SmsHub Api 服务端demo【只保留HttpServer、增加内网穿透】
-
WiFi网络下可用,启动后局域网内其他机器可直接调用本机接口
-
与
一键克隆
共用本地HttpServer
,访问URL:http://本地ip:5000/send_api
-
接收一个list并执行操纵,设置处理结果后并在尾部追加一个心跳包后返回原list
接口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": ""
}
-
请先在通用设置中,填写服务端地址,再启动服务,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;