-
-
Notifications
You must be signed in to change notification settings - Fork 3k
附录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 | 否 | 当服务端设置secret时必传,生成的sign签名,规则见下方sign校验规则 |
响应示例
{
"code": 200,
"msg": "ok",
"data": {},
"timestamp": 1652590258638,
"sign": ""
}公共响应参数
| 参数 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| code | Int | 是 | 200=成功,500=失败 |
| msg | String | 否 | 成功或失败提示 |
| data | Any | 否 | 响应参数,仅当成功时返回,根据具体接口而定,参见 2、接口列表
|
| timestamp | Long | 是 | 时间戳,毫秒 |
| sign | String | 否 | 当服务端设置secret且code=200时必传,生成的sign签名,规则见下方sign校验规则 |
sign 校验规则(参考 阿里钉钉群机器人的sign生成):
把 timestamp+"\n"+密钥 当做签名字符串,使用 HmacSHA256 算法计算签名,然后进行 Base64 encode,最后再把签名参数再进行urlEncode,得到最终的签名(需要使用UTF-8字符集)
调用其他接口之前建议先调用此接口,判断服务端是否已经开启该功能
接口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,
"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 | 是 | 远程发短信 |
| 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
}
],
"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=获取失败 |
【注意】
卡槽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": {
"ip": "192.168.168.168",
"mac": "24:5E:BE:0C:45:9A"
},
"timestamp": 1652590258638,
"sign": ""
}请求参数
| 参数 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| phone_numbers | String | 是 | 内网IP地址 |
| msg_content | String | 是 | 网卡MAC地址 |
响应示例
{
"code": 200,
"msg": "success",
"data": "success",
"timestamp": 1653982995002,
"sign": "NGxleev7ZZ%2Bd2KYCPHGw9XuoDo6Y6u7y1Pe3AZmsw6k%3D"
}PS. SmsForwarder 3.0 API 文档到此结束,最后修改时间:2022年07月27日
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;