Initial commit: SMS Receiver Web Service

Features:
- Receive SMS from TranspondSms Android APP
- HMAC-SHA256 signature verification (optional)
- SQLite database storage
- Web UI with login authentication
- Multiple API tokens support
- Timezone conversion (Asia/Shanghai)
- Search, filter, and statistics
- Auto refresh and session management

Tech Stack:
- Flask 3.0
- SQLite database
- HTML5/CSS3 responsive design
This commit is contained in:
OpenClaw Agent
2026-02-06 23:23:49 +00:00
commit 4e5e93660d
16 changed files with 3754 additions and 0 deletions

118
sign_verify.py Normal file
View File

@@ -0,0 +1,118 @@
"""
签名验证工具
参考 TranspondSms 的签名规则:
timestamp + "\n" + secret -> HMAC-SHA256 -> Base64 -> URL Encode
"""
import hmac
import hashlib
import base64
import urllib.parse
import time
def generate_sign(secret: str, timestamp: int = None) -> str:
"""
生成签名
Args:
secret: 密钥
timestamp: 时间戳(毫秒),不传则使用当前时间
Returns:
签名字符串
"""
if timestamp is None:
timestamp = int(time.time() * 1000)
string_to_sign = f"{timestamp}\n{secret}"
hmac_code = hmac.new(
secret.encode('utf-8'),
string_to_sign.encode('utf-8'),
digestmod=hashlib.sha256
).digest()
sign = urllib.parse.quote(base64.b64encode(hmac_code).decode())
return sign
def verify_sign(secret: str, sign: str, timestamp: int, max_age: int = 3600000) -> tuple[bool, str]:
"""
验证签名
Args:
secret: 密钥
sign: 待验证的签名
timestamp: 时间戳(毫秒)
max_age: 签名最大有效时间毫秒默认1小时
Returns:
(是否有效, 错误信息)
"""
current_time = int(time.time() * 1000)
# 检查时间戳是否过期
if abs(current_time - timestamp) > max_age:
return False, f"签名过期,时间差: {abs(current_time - timestamp) / 1000:.1f}"
# 生成期望的签名
expected_sign = generate_sign(secret, timestamp)
# 比较签名
if sign != expected_sign:
return False, "签名不匹配"
return True, "签名有效"
def verify_from_app(from_number: str, content: str, timestamp: int,
sign: str, secret: str, max_age: int = 3600000) -> tuple[bool, str]:
"""
验证 TranspondSms APP 发来的请求
Args:
from_number: 发送方手机号
content: 短信内容
timestamp: 时间戳
sign: 签名
secret: 密钥
max_age: 最大有效时间(毫秒)
Returns:
(是否有效, 错误信息)
"""
# 检查必填字段
if not from_number or not content:
return False, "缺少必填字段"
# 如果没有签名,跳过验证(取决于配置)
if not sign:
return True, "无签名,跳过验证"
return verify_sign(secret, sign, timestamp, max_age)
# 测试代码
if __name__ == '__main__':
# 测试签名生成和验证
secret = "test_secret"
timestamp = int(time.time() * 1000)
# 生成签名
sign = generate_sign(secret, timestamp)
print(f"Timestamp: {timestamp}")
print(f"Sign: {sign}")
# 验证签名
is_valid, message = verify_sign(secret, sign, timestamp)
print(f"验证结果: {is_valid}, {message}")
# 测试过期签名
old_timestamp = timestamp - 7200000 # 2小时前
is_valid, message = verify_sign(secret, sign, old_timestamp)
print(f"过期验证: {is_valid}, {message}")
# 测试错误签名
wrong_sign = "wrong_signature"
is_valid, message = verify_sign(secret, wrong_sign, timestamp)
print(f"错误签名: {is_valid}, {message}")