# MsgSender **Repository Path**: plusboy/msg-sender ## Basic Information - **Project Name**: MsgSender - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-11-01 - **Last Updated**: 2026-11-03 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # MsgSender — 越狱 iPhone 短信 → 飞书 Webhook 收到短信 / iMessage 后,由独立事件服务主动读取新消息并发送给飞书机器人。 **不做周期性短信库轮询,不依赖 MobileSMS 启动、ChatKit 私有方法或注入框架。** | 设备 | 系统 / 越狱 | 包版本 / 架构 | 文档 | | --- | --- | --- | --- | | iPhone 5s | iOS 12.5.7 / rootful | 1.5.1 / iphoneos-arm | [5s](devices/iphone5s-ios12/README.md) | | iPhone 7 Plus | iOS 15.8.5 / Dopamine rootless | 2.2.1 / iphoneos-arm64 | [7p](devices/iphone7p-ios15/README.md) | ## 1. 工作方式 两台设备共用 `core/src/MSEventService.[hm]`: 1. launchd 以 `mobile` 启动 `msgsender-events`,监听 SMS 目录、`sms.db` 和 `sms.db-wal` 的内核 vnode 事件。 2. 文件变化合并约 150ms 后查询已提交的新行,另做一次 350ms 的提交确认读取。 读库失败有五次有界重试;**空闲时没有定时查库**。不监听会被读库本身改变的 `-shm`。 3. ROWID 水位线和待发消息原子写入 `events.plist` 后逐条发送。失败保留,按 5–300 秒退避重试。 网络重试只处理待发队列,不扫描短信库。 4. WAL 删除、重建或 checkpoint 后重新绑定文件监听。重启补读停机期间的新行; 首次安装 / 短信库被替换且 ROWID 回退时跳过历史,保留已有待发队列。 升级卸载旧 `msgsenderd` 任务及 MobileSMS 注入文件,保留 Webhook 配置,迁移旧 `pending.json` 待发消息。 旧 `PollingEnabled`、`PollInterval`、`HooksEnabled` 不再控制运行逻辑。 > 这里的“hook”是文件写入事件,不是 Objective-C 方法 hook。短信到达后写库触发处理, > 不受旧私有方法名失效影响,但不能承诺设备完全休眠时立即执行,见下方验证边界。 ## 2. 连接、编译、安装 设备地址和 SSH 凭据只保存在对应设备的本地 `.env`(Git 已忽略),不编进二进制: ```bash cp devices/iphone7p-ios15/.env.example devices/iphone7p-ios15/.env # 编辑 DEVICE_IP / DEVICE_PORT / DEVICE_USER / DEVICE_PASS # 5s 对应 devices/iphone5s-ios12/.env ``` WSL **Ubuntu-18.04** 项目目录 `/opt/msg-sender`,Theos `/root/theos`: ```bash cd /opt/msg-sender # 由你提交推送后再 git pull --ff-only;助手不会自动提交或推送 PROXY=192.168.1.61:7890 bash tools/setup_ubuntu.sh # 首次准备工具链 bash devices/iphone7p-ios15/build_and_push_7p.sh --package-only bash devices/iphone7p-ios15/build_and_push_7p.sh # 5s: bash devices/iphone5s-ios12/build_and_install_5s.sh ``` 7p 使用 `/var/jb` rootless 布局,SSH `mobile` 用户时通过 sudo 安装, `DEVICE_SUDO_PASS` 默认使用 SSH 密码;设置 `DEVICE_INSTALL=0` 可只上传后用 Sileo 安装。 5s 使用 rootful 布局和 root SSH 安装。两边产物分别位于各设备 `packages/`。 ## 3. 手机 App 与配置 首页顶部显示**当前 App 二进制版本和编译时间(北京时间)**,不使用设备时钟或缓存状态推算。 首页三段:运行状态、配置、操作。每 2 秒刷新的是**界面状态**,不是短信库轮询。 - 运行状态:服务 pid、版本、文件监听完整性、最近文件事件、水位线、推送计数、待发条数、最近错误。 - 配置:Webhook、签名密钥、机器人关键词、正文开关、SMS / iMessage / 发出消息过滤、 号码白名单 / 黑名单、正文关键词、卡片样式、长度、单次请求重试及最小发送间隔。 - 操作:发送测试消息、查看 / 清空日志、分层网络自检、请求事件服务重试。 日志和自检报告页左上角保留“返回”;清空按钮在右侧。 - 5s 另外带 PreferenceLoader 设置面板;7p 用桌面 App,不依赖 PreferenceLoader。 配置位于 `/var/mobile/Library/Preferences/com.msgsender.plist`; 状态及持久队列位于 `/var/mobile/Library/Caches/MsgSender/`; 日志位于 `/var/mobile/Library/Logs/MsgSender.log`。 前台 App 的 HOME 可能是容器目录,共享文件使用 `/var/mobile/…` 绝对路径。 旧 App 容器配置在首次打开时迁移;今后配置直接原子写到共享文件,保存失败会提示,不伪报成功。 ### 设备来源与验证码 在 App 配置中填写“设备名称”(5s 的设置面板也可填写),保存后新消息标题显示 `📩 新消息 · 7P` 或 `📩 新消息 · 5S`。留空时仅显示 `📩 新消息`,不再追加 `SMS`。 卡片底部也使用同一设备名称,例如 `MsgSender · 7P`;留空时只显示 `MsgSender`。 卡片标题和正文分开存储,正文不重复标题;切换纯文本时由传输层加回一次标题。 旧队列中重复的标题行会在发送时兼容清理。 “单独展示验证码”默认开启:优先识别验证码 / verification code / OTP 等语境附近 完整的 **4 位或 6 位 ASCII 数字**,保留前导零;纯数字短信也可识别。 不会截取长号码或字母数字混合串,排除明显的年份、时长和金额;同等优先级有多个不同候选时不猜测。 这属于启发式识别,不保证每种短信模板都能准确提取。 验证码在正文截断前提取,关闭正文时也隐藏验证码。 ```text 📩 新消息 · 7P ← 卡片标题 👤 发送号码 🕒 2026-11-03 12:00:00 🔑 验证码:012345 💬 【测试服务】您的验证码为:012345,请勿泄露。 MsgSender · 7P ← 卡片底部 ``` 设备名按发送时配置显示,改名后待发卡片的标题和底部保持一致;已生成的旧待发正文不会被重新提取验证码。 新增测试位于 `core/tests/MSMessageSelfTest.m`,由同一 `--self-test` 入口执行,不发送真实 Webhook。 ## 4. 验证与排障 ```bash bash devices/iphone7p-ios15/verify_7p.sh bash devices/iphone7p-ios15/verify_7p.sh self-test bash devices/iphone7p-ios15/verify_7p.sh log # 5s 替换成 devices/iphone5s-ios12/verify_5s.sh ``` `self-test` 使用临时 WAL 数据库和假发送器,不读真实短信、不改用户配置、不调用真实 Webhook。 测试实现共用 `core/tests/MSEventSelfTest.m`。 正常状态为 `mode=sms.db 文件事件(无轮询)`、`dbReadable=true`、`watchCount>=2`。 WAL 存在时通常为 3;空闲时状态不更新是正常的,不再要求轮询 tick 或 hook 心跳。 电脑辅助工具详见 [tools/README.md](tools/README.md)。Python 工具默认读取 5s `.env`, 用 `DEVICE_PROFILE=7p` 或 `DEVICE_ENV_FILE` 切换设备;环境变量优先。 短信库快照诊断会读取私人短信,`--push` 会实际发送,勿公开结果。 | 问题 | 检查 | | --- | --- | | 没有推送 | 总开关、Webhook、号码 / 关键词过滤;事件服务是否存活、短信库是否可读 | | 服务没运行 | 使用对应 verify 脚本检查 launchd 和 `MsgSenderEvents.err`;7p 路径必须带 `/var/jb` | | App 与服务版本不同 | 重新安装,彻底退出并打开 App,确认顶部编译时间 | | 重启后暂时读库失败 | 首次解锁后重新读取;短信受 iOS 数据保护限制 | | 网络 -1003 / -1009 | 先做 App 分层自检;自签 bundle 的 CommCenter 数据策略在启动时尝试修复 | | Webhook 返回非零 code | 核对签名、关键词、IP 白名单;蜂窝出口 IP 会变化,优先签名验证 | | 偶发重复 | 响应丢失或成功后落盘失败可能触发重试;外部 Webhook 没有幂等确认 | ## 5. 锁屏与隐私边界 事件处理时尝试申请最多 25 秒防休眠窗口,空闲时释放,不长期唤醒手机。 这只能延长当前唤醒期,不能主动唤醒已完全休眠的 CPU。 **必须分别在真机锁屏熄屏 10–15 分钟后发一条短信,比较文件事件和机器人收信时间, 期间不要打开“信息”App,也不要持续 SSH 采样(会干扰休眠)。** 失败队列可恢复,但不能承诺严格 exactly-once 或锁屏下固定秒级延迟。 短信正文会发往第三方机器人:建议使用仅自己可见的群、配置过滤,或关闭正文。