接收短信并转发至 Telegram

虽然现在已经有不少工具能实现接收短信并转发至 Telegram, 但总感觉差点意思。毕竟别人写的东西再怎么万能, 也不如自己写的顺手。很多奇思妙想, 只有自己亲手造轮子才能实现。

网上常见的 Python 示例其实并不算完整

  • 只会监听新短信, 在脚本启动前收到的短信它不会去处理
  • 对于分段短信 (长短信被拆成几条的那种), 它不会自动拼接, 还会乱序打印

所以, 干脆自己写一个: 既能支持多分段短信, 也能读取历史短信, 并在第一时间转发到 Telegram

安装依赖

不要使用 python-gsmmodem, 它已经不再维护。使用新的 python-gsmmodem-new

pip install python-gsmmodem-new
pip install python-telegram-bot

Python 文件部分

收到短信后会自动转发至 Telegram

文件名: python-gsmmodem-advanced-read.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from datetime import datetime
from gsmmodem.modem import GsmModem, Sms
from gsmmodem.pdu import Concatenation
from telegram import Bot

# ========== 配置区 ==========
PORT = '/dev/ttyUSB0'
BAUDRATE = 115200
PIN = None  # SIM 卡 PIN (如果有的话)

# Telegram Bot 配置
TELEGRAM_BOT_TOKEN = '你的 BotToken'
TELEGRAM_CHAT_ID = '你的 ChatID'   # 可以是个人 ID 或群 ID

# SOCKS5 代理配置
request_kwargs = {
    'proxy_url': 'socks5://127.0.0.1:1080',  # 代理地址
    'urllib3_proxy_kwargs': {
        # 如果代理需要用户名密码, 填写这两个
        # 'username': 'proxyuser',
        # 'password': 'proxypass',
    }
}
# ============================

concat_sms = {}
bot = Bot(token=TELEGRAM_BOT_TOKEN, request_kwargs=request_kwargs)

# 转发消息到 Telegram
def send_to_telegram(text: str):
    try:
        bot.send_message(chat_id=TELEGRAM_CHAT_ID, text=text)
        print("Forwarded to Telegram")
    except Exception as e:
        print(f"Telegram send failed: {e}")

# 处理单条短信, 包括长短信拼接
def handleSms(sms: Sms):
    concat = None
    message = None

    # 检查是否是长短信的一部分
    if sms.udh:
        for i in sms.udh:
            if isinstance(i, Concatenation):
                concat = i
                break

    if concat:
        ref = concat.reference
        if ref not in concat_sms:
            concat_sms[ref] = {}
        concat_sms[ref][concat.number] = sms.text

        print(u'== Partial message received ==\n[{0}/{1}] ref={2}\n'.format(
            len(concat_sms[ref]), concat.parts, ref))

        # 如果所有分段都收齐了 → 拼接
        if len(concat_sms[ref]) == concat.parts:
            sortedParts = [concat_sms[ref][i] for i in sorted(concat_sms[ref])]
            message = "".join(sortedParts)
            del concat_sms[ref]
    else:
        message = sms.text

    if message:
        out = (u'== SMS message received ==\nFrom: {0}\nTime: {1}\nMessage:\n{2}\n'
               .format(sms.number, sms.time, message))
        print(out)

        # 转发到 Telegram
        tg_text = f"📩 新短信\n\n来自: {sms.number}\n时间: {sms.time}\n\n{message}"
        send_to_telegram(tg_text)

        # 可选: 保存到文件
        # date = datetime.today().strftime('%Y%m%d%H%M%S%f')
        # with open(f'/path/to/messages/{date}.txt', 'w') as f:
        #     f.write(out)

def main():
    print('Initializing modem...')
    modem = GsmModem(PORT, BAUDRATE, smsReceivedCallbackFunc=handleSms)
    modem.smsTextMode = False
    modem.connect(PIN)

    # 启动时读取并删除存量短信
    modem.processStoredSms(delete=True)

    print('Waiting for new SMS message...')

    try:
        # 无限等待, 但能响应 Ctrl+C
        modem.rxThread.join(2**31)
    except KeyboardInterrupt:
        print("\nProgram terminated by user (Ctrl+C)")
    finally:
        print("Closing modem...")
        modem.close()

if __name__ == '__main__':
    main()

启动监听

运行以下命令

$> python3.6 python-gsmmodem-advanced-read.py
Initializing modem...
Waiting for new SMS message...

保持此进程在后台运行即可, 例如使用 screen


原文

接收短信并转发至 Telegram
Read long SMS with python-gsmmodem (and send SMS with same running script)

最后更新于 2025-09-15