Files
sms_forwarding/README.md
T
2026-06-27 16:57:48 +08:00

305 lines
8.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Node.js 4G SMS Gateway
基于Node.js的4G短信网关,通过串口与4G模块通信,接收短信并转发到多种推送渠道。
## 功能特性
- ✅ 4G模块AT指令交互
- ✅ 自动接收短信(PDU模式)
- ✅ 长短信自动合并
- ✅ 多通道推送(邮件/钉钉/飞书/Telegram等)
- ✅ 短信发送功能
- ✅ RESTful API接口
- ✅ Web管理界面
## 环境要求
- Node.js >= 18.x
- 4G模块通过USB转串口连接到服务器
- 支持的4G模块:ML307R-DC、SIM7600等AT指令兼容模块
## 安装
```bash
npm install
```
## 配置
复制配置模板:
```bash
cp config.example.json config.json
```
`config.json` 必须存在,否则程序会直接退出。最小可运行配置如下:
```json
{
"serial": {
"path": "/dev/ttyUSB0",
"baudRate": 115200,
"autoDetect": true,
"probeTimeout": 1200
},
"api": {
"port": 3000,
"webToken": "change-this-token"
}
}
```
完整配置可参考 `config.example.json`。主要配置项如下:
| 配置项 | 是否必填 | 说明 |
|--------|----------|------|
| `serial.path` | 是 | 串口路径;Linux 常用 `/dev/ttyUSB0`,Windows 使用 `COM3` 等。`autoDetect=true` 时作为优先探测项,`autoDetect=false` 时作为实际串口。 |
| `serial.baudRate` | 是 | 串口波特率,通常为 `115200`。 |
| `serial.autoDetect` | 否 | 是否自动探测可响应 `AT` 的串口,默认按非 `false` 处理。 |
| `serial.probeTimeout` | 否 | 自动探测单个串口的超时时间,单位毫秒,默认 `1200`。 |
| `api.port` | 是 | API 和 Web 管理台监听端口。 |
| `api.webToken` | 是 | 唯一鉴权凭据;已取消 `admin/admin123` Basic Auth。 |
| `mobileData.cid` | 否 | 移动数据 PDP CID,默认 `1`;有效范围 `1-15`,不要使用 `8`。 |
| `simCards` | 否 | SIM 卡显示手机号,`slot=0` 是 SIM1,`slot=1` 是 SIM2。 |
| `smtp` | 否 | 邮件通知配置;填写 `smtp.server` 后启用。 |
| `inbox` | 否 | Web 收件箱持久化和双卡显示策略。 |
| `pushChannels` | 否 | 推送通道列表;通道开启后才要求目标字段。 |
`api.webToken` 可以通过以下方式传入:
- `Authorization: Bearer <token>`
- `X-Web-Token: <token>`
- `?token=<token>`
- Web 管理台 token 登录页
### 完整配置示例
```json
{
"serial": {
"path": "/dev/ttyUSB0",
"baudRate": 115200,
"autoDetect": true,
"probeTimeout": 1200
},
"mobileData": {
"cid": 1
},
"simCards": [
{ "slot": 0, "phoneNumber": "+8613800138000" },
{ "slot": 1, "phoneNumber": "+447700900000" }
],
"smtp": {
"server": "smtp.qq.com",
"port": 465,
"user": "your@qq.com",
"pass": "your_auth_code",
"sendTo": "recipient@example.com"
},
"inbox": {
"maxReceivedMessages": 200,
"receivedMessagesFile": "data/received-messages.json",
"partitionBySim": true,
"allowAllSimMessages": false,
"dualSimDefaultScope": "all"
},
"pushChannels": [
{
"enabled": true,
"type": "dingtalk",
"name": "钉钉通知",
"url": "https://oapi.dingtalk.com/robot/send?access_token=xxx",
"secret": "SECxxx"
}
],
"api": {
"port": 3000,
"webToken": "change-this-token"
}
}
```
### 可选项细节
- `smtp`:不需要邮件时可以删除整个对象,或清空 `server`。启用邮件时需要正确填写 `server`、`port`、`user`、`pass`、`sendTo`。
- `inbox.maxReceivedMessages`:默认 `200`,最大按 `5000` 处理。
- `inbox.receivedMessagesFile`:默认 `data/received-messages.json`,相对路径会基于项目根目录解析。
- `inbox.partitionBySim`:默认 `true`,按当前 SIM 隔离历史短信。
- `inbox.allowAllSimMessages`:默认 `false`,控制是否允许显式查询全部 SIM 历史。
- `inbox.dualSimDefaultScope`:可选 `all` 或 `current`;`current` 表示双卡时默认只看当前 SIM。
- `simCards`:只影响管理台显示,不影响短信收发。代码也兼容旧字段名 `simSlots`、`sims`、`sim`。
## 运行
### 开发模式
```bash
npm run dev
```
### 生产模式
```bash
npm start
```
### 使用PM2(推荐)
```bash
pm2 start ecosystem.config.js
pm2 save
pm2 startup
```
## API接口
### 查询状态
```bash
GET /api/status
```
### 发送短信
```bash
POST /api/sms/send
Content-Type: application/json
{
"phone": "13800138000",
"message": "测试短信"
}
```
### 查询收到的短信
```bash
GET /api/sms/received?limit=50
```
双卡双待模式下,默认返回全部 SIM 历史;显式传 `scope=current` 时才按当前卡槽过滤。短信记录会保留 `simSlot`、`simLabel` 与 `simSource`,用于判断收件卡槽。
也可以显式按卡槽查询:
```bash
GET /api/sms/received?limit=50&simSlot=0 # SIM1
GET /api/sms/received?limit=50&simSlot=1 # SIM2
```
直出模式下,新短信主要通过模组 `+CMT` URC 进入收件箱;如果 URC 未携带卡槽信息,会显示为未知 SIM。
### 查询信号强度
```bash
GET /api/modem/signal
```
### 查询模组信息
```bash
GET /api/modem/info
```
### 查询与切换SIM
```bash
GET /api/modem/sim
POST /api/modem/sim/switch
Content-Type: application/json
{
"slot": 1
}
```
当模组返回 `AT+DUALSIM?`、`AT+SWITCHSIM?` 与 `AT+BINDSIM?` 能力时,管理台会显示 SIM1/SIM2。短信接收使用 `CNMI` 直出模式,不为了接收短信切换 `SWITCHSIM`。如果模组上报的 URC 未携带卡槽信息,新短信会记录为未知 SIM。
管理台切卡会同时设置 `AT+SWITCHSIM` 与 `AT+BINDSIM`,随后恢复短信直出配置。发送短信前会提示当前使用的 SIM。
如果希望管理台显示手机号,可在 `config.json` 中配置:
```json
{
"simCards": [
{ "slot": 0, "phoneNumber": "+8613800138000" },
{ "slot": 1, "phoneNumber": "+447700900000" }
]
}
```
### 发送AT命令
```bash
POST /api/modem/at
Content-Type: application/json
{
"command": "AT+CSQ"
}
```
### 获取日志
```bash
GET /api/logs
```
所有API均需要有效 token。可以通过 `Authorization: Bearer <token>`、`X-Web-Token` 请求头或 `token` 查询参数传入。
## 项目结构
```
.
├── src/
│ ├── index.js # 入口文件
│ ├── modem.js # 4G模块通信
│ ├── sms.js # 短信处理
│ ├── push.js # 推送通道
│ ├── concat.js # 长短信合并
│ ├── api.js # REST API
│ └── logger.js # 日志系统
├── config.json # 配置文件
├── package.json
└── README.md
```
## 推送通道类型
| 类型 | type值 | 配置项 |
|------|--------|--------|
| 钉钉机器人 | `dingtalk` | url, secret(可选) |
| 飞书机器人 | `feishu` | url, secret(可选) |
| Telegram Bot | `telegram` | url(bot token), key1(chat_id) |
| PushPlus | `pushplus` | key1(token) |
| Server酱 | `serverchan` | key1(sendkey) |
| Bark | `bark` | url(bark服务器) |
| POST JSON | `post_json` | url |
| GET请求 | `get` | url |
| 自定义模板 | `custom` | url, customBody |
`pushChannels` 中 `enabled: false` 的通道可以保留空字段;`enabled: true` 时必须配置对应目标:
- `post_json`、`bark`、`get`、`dingtalk`、`custom`、`feishu`:必须有 `url`。
- `telegram`:`url` 填 Bot Token,`key1` 填 Chat ID。
- `pushplus`:`key1` 填 PushPlus Token。
- `serverchan`:`key1` 填 Server酱 SendKey。
- `custom`:`customBody` 如果填写,必须是合法 JSON 字符串,支持 `{sender}`、`{message}`、`{timestamp}` 占位符。
## 长短信处理
- 自动检测长短信(分段数 > 1)
- 使用参考号和发送者号码标识同一条长短信
- 收齐所有分段后自动合并
- 30秒超时保护(未收齐也转发)
## 故障排查
### 串口打不开
```bash
# Linux检查串口设备
ls -l /dev/ttyUSB*
# 添加用户到dialout组
sudo usermod -a -G dialout $USER
```
### 模块不响应
- 检查串口路径和波特率
- 检查USB连接
- 检查SIM卡是否插好
### 收不到短信
- 检查AT+CEREG?网络注册状态
- 检查AT+CNMI配置是否正确
- 检查SIM卡余额和服务状态
## 许可证
MIT