接上回书,我用旧的小米 9 搭了一个我的世界服务器,用的是手机流量。但是有一个问题,我每次看还剩多少话费还得解锁手机,万一我忘了有这回事,错过了运营商发的扣费短信怎么办?我得想个办法随时看到手机短信。于是有了这套方案。

QQ 机器人

我刚好有一个 QQ 机器人,用的 nonebot 框架,python 写的。QQ 能干的它都能干(除了红包相关的),所以我决定加一个功能,轮询手机里的短信,把新的短信发送到我的 QQ 私聊里。

开始搞机

首先得知道手机短信到底存在哪里。我在网上搜到了短信存放在一个 SQLite 格式的 db 文件里,路径是 /data/data/com.android.providers.telephony/databases/mmssms.db。想在 Linux Deploy 里看到这个文件还需要启动 Linux 的时候配置挂载安卓上的资源,我把安卓上的 /data/data 目录挂载到 Linux 上的 /data/data 目录。

这时就可以读这个文件了。Linux 系统默认会装 sqlite3,如果没有的话可以用 apt 或者 yum 装一下。

对了,先研究一下表结构。把文件下载到 windows,然后用 Data Grip 看看里面到底长啥样。

表结构

有用的表就 2 个,分别是 sms 和 canonical_addresses。

sms

sms 表是每一条短信的各种信息,我们重点关注下面几个字段。

create table sms
(
    -- 主键
    _id                INTEGER
        primary key,
    -- 发信人地址
    address            TEXT,
    -- 是否已读,0 表示未读,1 表示已读
    read               INTEGER default 0,
    -- 消息的内容
    body               TEXT,
);

canonical_addresses

这个表存储了发信人地址和发信人的名字。比如说 10001 是中国电信。

create table canonical_addresses
(
    -- 主键
    _id     INTEGER
        primary key autoincrement,
    -- 发信人地址
    address TEXT,
    -- 发信人名字
    name    TEXT
);

查询 sql

查询的时候只要把这两张表连接起来,就能一起查到发信人名字和信息的内容了。

select 
    sms._id, 
    sms.body, 
    sms.address, 
    case 
        when ca.name is null 
        then '未知号码' 
        else ca.name 
    end as address_name
from 
    sms left join canonical_addresses as ca on sms.address=ca.address
where sms.type=1 and sms.read=0 

把 read 字段为 0 (也就是未读)的短信查出来,遍历结果集,把结果集里的每一条短信每隔 5 秒发送私聊消息给我的 QQ,发送完之后把 read 字段置为 1,表示我已经读过了。

update sms set read=1 where _id={data.get('_id')}

这里用了 python 的 fstring 拼接字符串,还是挺方便的,也没有 sql 注入啥的问题。

结尾

重启机器人后顺利收到了消息,但我并不想在这一篇文里谈机器人的细节,所以就这样吧。

有兴趣的话可以自己看看 nonebot 文档gocqhttp 文档