电报群关键词监听机器人:Pyrogram普号隐身监控与避坑指南
简介一套电报群关键词监听机器人源码再现了“多号潜伏、关键词触发、私聊跟进”的自动化流程通过辅助账户监控群内聊天命中预设词后提示操作者由其伪装身份主动联系目标主账号则全程隐身。资源面向PHP开发者、安全研究者及对即时通讯机器人机制感兴趣的读者适合拆解事件驱动架构也可作为识别此类网络推广手段的参考案例。压缩包共57个文件大小仅94KB以36个PHP源文件为核心覆盖事件观察者、日志记录、控制器及底层API扩展等模块另有9个备份文件、关键词配置、Docker部署配置和教程文档便于本地搭建、对比备份与二次开发。源码目录按事件、日志、控制器等职责划分涉及异步服务迁移与异常处理可帮助理解常驻进程下消息分发与机器人管理思路其中日志记录和事件响应模块具备一定复用价值。目前已有129人学习。资源来自网络分享仅限学习交流使用请勿用于商业用途。1. 电报群关键词监听依旧是个技术活先说清楚边界做过社群运营或者盯过竞品群的人都有这种感受一天二十四小时盯着几个群眼都快瞎了真正有价值的信息却总是漏掉。电报群关键词监听机器人要解决的就是这个问题——用一个普号普通账号非Bot隐身挂在群里群成员看不到它在线后台却实时跑着关键词匹配一旦命中立刻把消息内容、发送人、群名、时间戳一起推给值班人员由真人决定下一步是回复、记录还是静默。这个资源的核心不是「爬消息」而是「监听加人工响应」这套半自动闭环。适合谁用第一类是电商、外贸、币圈项目方需要盯竞品低价、渠道方报价、负面舆情第二类是社群运营需要把几十个群里的售后问题、求购信息按关键词捞出来。它不是全自动客服命中后的动作大多是推送提醒真正的商业判断还是由人来下。全文用的技术栈是 Pyrogram Telethon 这类 MTProto 客户端库监听原理走的是账号自身的更新事件流不依赖抓包和网页接口稳定性比你想的要好坑也比你想的多。2. 群监听的关键不是爬接口而是保住登录态和更新流2.1 为什么选普号而不是官方 Bot很多人会先想到用 BotFather 创建一个官方 Bot拉进群里监听。这个方案的优点是被封风险低但有两个硬伤第一Bot 无法看见群里的历史消息只能从被拉入那一刻开始收消息群内已有的聊天记录全部丢失第二Bot 默认收不到普通成员之间的私聊和部分群动作除非群主开启「允许 Bot 读取消息」而且很多群对外来 Bot 是直接拒绝的。普号普通用户账号则没有这两层限制它就是以群成员的身份存在能看到所有常规消息。资源里用的是普号登录模式通过客户端库拿到的 session 文件保持登录态实现「隐身监听」这里所谓的隐身指的是不发言、不改昵称、不回复任何消息在群成员列表里它就是一个安静的普通号极难被单独注意到。代价是普号比 Bot 更容易触发风控所以后面第 6 章会专门讲如何降低动作频率。2.2 Pyrogram 的更新回调是怎么工作的项目主体基于 PyrogramMTProto 的 Python 实现。监听的核心逻辑不在轮询接口而在 Pyrogram 的 updates 机制——客户端和 Telegram 服务器保持一个长连接服务器把新消息实时推给客户端客户端触发on_message回调。from pyrogram import Client, filters app Client( monitor_session, api_idYOUR_API_ID, api_hashYOUR_API_HASH ) app.on_message(filters.group ~filters.me) async def watch_group(client, message): text message.text or message.caption or if not text: return keywords [报价, 现货, 低价, badword] for kw in keywords: if kw in text.lower(): print(f[命中] 群:{message.chat.title} 用户:{message.from_user.first_name} 内容:{text}) break app.run()这段代码拆开看是四件事filters.group限定只处理群聊~filters.me排除自己发的消息文本取的是message.text和message.captioncaption 是为了兼容图片下面的描述文字匹配用in而不是正则因为in在低频监听场景下足够快也避免正则写错导致整个回调抛异常。如果你接的是资源里的完整源码会发现它还多做了一件事把命中消息写入一个 queue而不是直接同步推送。原因是on_message回调如果被网络请求阻塞比如推送 HTTP 请求超时会拖慢 Pyrogram 处理后续更新严重时会被服务器断开连接写成生产者消费者模型会更稳。2.3 session 文件与 api_id 的关系这里有个常见的混淆点。很多人从网上随便抄一段 Pyrogram 代码就去跑发现Client(monitor_session)每次都提示需要重新登录原因是 Pyrogram 的 session 文件绑定 api_id你换了一个 api_id原 session 就没法复用了。资源配套的使用说明里会把你的 api_id、api_hash 写进 config 文件首次运行扫码登录后生成.session文件后续重启只加载 session不再需要扫码。还有一个细节session 文件里存的是 Telegram 服务器的 auth key如果你把这个文件拷贝到另一台服务器必须保证那个服务器的 IP 和首次登录时差异不大否则 Telegram 风控会判定账号异地登录轻则要求二次验证重则临时封禁。这个坑在 Telegram 的场景下比微信严重得多后面避坑章节会展开。3. 部署一个能跑整年的监听进程systemd 和重启策略是重点3.1 Python 虚拟环境与依赖版本拿到项目源码后第一步是装依赖。项目要求 Python 3.9 以上Pyrogram 2.x 系列。注意不要用 Pyrogram 1.x 的旧教程两个版本在 client 初始化和on_message装饰器上有差异直接套旧代码会翻车。cd tg-monitor python3 -m venv venv source venv/bin/activate pip install -r requirements.txtrequirements.txt里大约有这些依赖pyrogram、tgcrypto加密加速、aiohttp用于推送 webhook、apscheduler用于定时清理无效 session 或重置心跳。其中tgcrypto是必须装的它是 MTProto 加解密底层 lib不装也能跑但速度慢很多群多的时候掉更新流的概率会明显上升。3.2 首次登录和验证码的自动化处理首次运行会要求登录Pyrogram 的交互式登录在终端里按提示走就行。真正值得关注的是后续的 2FA 密码和验证码。如果你的账号开了两步验证Pyrogram 会在登录时抛出一个TwoStepVerificationRequired异常。from pyrorogram.errors import TwoStepVerificationRequired try: app.start() except TwoStepVerificationRequired: password input(请输入两步验证密码: ) app.sign_in(passwordpassword)这里有个非常容易踩的坑很多人在app.start()失败后直接重跑脚本导致 session 文件写入一半登录态损坏。正确做法是启动前检查.session文件是否存在且大小大于 2KB否则先删除再登录避免半成品 session 干扰后续动作。3.3 systemd 守护进程与崩了自动拉起进程跑在服务器上最忌讳的是前台nohup挂着。崩了就没人管ssh 断了终端关了进程也可能跟着没。推荐用 systemd 当作守护写一个 service 文件sudo vim /etc/systemd/system/tg-monitor.service[Unit] DescriptionTG Keyword Monitor Afternetwork-online.target [Service] WorkingDirectory/opt/tg-monitor ExecStart/opt/tg-monitor/venv/bin/python /opt/tg-monitor/main.py Restartalways RestartSec10 Userroot [Install] WantedBymulti-user.target写完保存再执行sudo systemctl daemon-reload sudo systemctl enable tg-monitor sudo systemctl start tg-monitor sudo journalctl -u tg-monitor -fRestartalways意味着任何非正常退出都会在 10 秒后拉起来包括更新断连、内存溢出、甚至是代码里未捕获的异常。这里要提醒的是崩了重启不等于健康运行监控还是要看日志口径而不是进程存活。3.4 日志策略按天切割而非无限追加监听程序的日志必须按天切割否则跑三个月就是几十 GB 的纯文本。资源里的日志模块用的两个方法任选一种是 Python 自带的logging.handlers.TimedRotatingFileHandler另一种是直接输出 JSON 后交给 logrotate 切分。后一种更省心因为 logrotate 是系统级的不依赖 Python 进程何时重启。# /etc/logrotate.d/tg-monitor /opt/tg-monitor/logs/*.log { daily rotate 14 compress missingok copytruncate }copytruncate很重要——它先复制日志再做截断不重启 Python 进程避免文件句柄丢失。不写这一项的话logrotate 每天把原来的文件 mv 走Python 还对着旧 inode 写日志直接消失。4. 命中之后的响应链路本地记录到人工响应全打通4.1 命中消息的标准化字段监听本身的意义不大真正价值在于命中后把信息结构化成一条「记录」。资源里定义的消息字段大致是这样字段类型来源group_idintmessage.chat.idgroup_namestringmessage.chat.titlesender_idintmessage.from_user.idcontentstringmessage.text / message.captionmsg_timedatetimemessage.datematched_keywordstring满足 ruletable 里的哪一组词这些字段被拼成一个 JSON写入本地 SQLite 文件保存。SQLite 比 MySQL 更适合这个场景因为单机写入量不大一个大文件便于备份换服务器直接拷贝文件就迁移了。4.2 人工响应如何接线企业微信和 Server酱电报本身是域外工具而值班人员的响应大多发生在微信生态内。这里的做法是一条消息命中后除了写 SQLite还会走一次 HTTP 请求推到第三方通知渠道。最常见的是用 Server酱sct.ftqq.com 的 SendKey推送到微信或者走企业微信机器人 Webhook 推到内部群。import aiohttp WEBHOOK_URL https://qyapi.weixin.qq.com/cgi-bin/webhook/send?keyxxx async def push_to_wechat(payload: dict): data { msgtype: text, text: { content: f[监控命中] {payload[matched_keyword]}\n群{payload[group_name]}\n人{payload[sender_id]}\n内容{payload[content][:200]} } } async with aiohttp.ClientSession() as session: async with session.post(WEBHOOK_URL, jsondata) as resp: status resp.status print(fpush status: {status})这个推送为什么一定要异步因为 Pyrogram 回调是事件循环驱动的同步requests.post会阻塞整个事件循环在群消息密集的时段一旦推送接口响应超过 5 秒后续所有群的消息都排队延迟会以分钟级累积。用aiohttp做异步发送虽然不能彻底消除阻塞但至少不会让一个慢接口拖垮整个监听进程。4.3 去广告与去闲聊的过滤规则真正跑过监听的人会告诉你最怕的不是漏报而是误报刷屏。一个群一天几百条消息如果关键词配得太宽命中的数量比人工能看的上限还多那就等于没监听。资源里的过滤规则有两层第一层是黑名单词去广告把「代购」「加V」「滴滴」「扫码」一类要求私聊的词单独过滤这类消息命中广告关键词后只记录不入响应通道。第二层是群内发言频率限制同一个 sender 在 30 秒内命中多条消息只推送第一条其余折叠成「N 条类似消息」。这一条在竞品群里尤其重要因为很多群疯子会连发几十条刷屏不折叠的话值班手机能震到没电。4.4 多群管理与动态开关群需要监听的群往往不是拉一个 Bot 进去等它自己跑就完了。每个群的监听开关建议单独做配置而不是全部写死在代码里。理由很简单有些群只是临时观察观察完就应该关掉减少账号在群里的存在感。资源里的群配置长这样{ group_list: [ {id: -100123456789, enabled: true, keywords: [报价, 低价, 事件]}, {id: -100987654321, enabled: false, keywords: []} ] }监听代码每次从group_list里遍历判断当前消息的群 id 是否 enabled再把该群下独有的 keywords 拿来匹配。群级别关键词和全局关键词分开的好处是你在一个群里盯「低价」在另一个群里盯「维权」互不干扰不需要把所有词都堆在同一个列表里。5. 避坑session 失效、封号信号、时区与乱码5.1 异地登录触发二次验证session 直接变废现象在本地电脑成功登录后把整个项目目录打包传到云服务器运行后提示需要输入验证码或者直接报AUTH_KEY_UNREGISTERED。原因Telegram 的风控模型里有「认证密钥与设备/IP 绑定」的关联判断session 在一个陌生 IP 上首次使用时服务器会要求重新验证。这不是 bug是安全机制。解决任何情况下都不要先本地登录再整包上传正确做法是先上传代码然后在服务器上首次登录生成会话。如果已经踩了坑删掉旧的.session文件重新扫一次码。之后就用这台服务器固定 IP 跑别再到处搬家。5.2 消息里有图片和链接message.text拿到的是 None现象部分命中消息用「内容」字段打出来的全是 None但明明群里发了好长一段话。原因Telegram 消息可以同时带文字和媒体带图片的纯文字描述在message.caption里而message.text只在无媒体时有效。部分消息本身就是图片或视频caption 为 None 是完全正常的。解决取文本时message.text or message.caption or 兜底这是资源里已经写好的逻辑。另一个隐藏问题大群里的消息如果带回复引用原始文本藏在message.reply_to_message里需要递归取一次。5.3 普号在群里的「上线状态」暴露了监听动作现象监听账号登录后群成员能看到它在最近上线列表里出现。原因普号只要在线就会广播 last seen 状态Telegram 没有像微信一样的「真正隐身」选项所谓的隐身只是伪隐身。解决资源里的做法是给 Pyrogram 客户端设置app.storage里不携带自身在线状态广播同时在消息处理回调内不做任何主动动作既不已读回执也不发通知。实操上还有一个更稳妥的方案把账号的隐私设置改为「所有人不可见我的上线时间」。这两者叠加后群成员基本上注意不到这个号的存在。5.4 关键词含中文时出现大小写和全半角匹配失败现象配置里写了“报价”但群内发出的「報 價」或者「报 价」没被命中。原因Telegram 消息文本里包含大量全半角混排和繁体/简体混写直接in匹配对这些情况无能为力。解决资源里用了一层简单的归一化函数——先把全角字符转半角再统一转小写再对中文繁体做一次简易映射。不要指望 NLP 级别的繁简转换监听场景下够用即可。5.5 命中推送过于频繁被第三方 webhook 拉黑现象推送接口正常但跑了几天后突然推送全部失败HTTP 返回 403。原因微信类 webhook 对每分钟调用次数有限制命中密集的时候 60 秒内推了 20 条超过量直接被限流拉黑一段时间。解决在推送层做 10 秒内去重聚合同类关键词、同群、同发送人的消息合并成一条同时把推送的 HTTP 请求包进带重试的队列里避免同时并发打爆 webhook 限制。6. 把监听账号调成「背着手巡逻」的状态停机检验与词表管理监听账号跑起来和跑得好是两码事。我的习惯是每周挑一个低峰时段做一次「停机检验」把推送通道断开手动在目标群里发几条测试消息用四个不同的小号轮流发避免同一账号连续发言触发频率限制验证命中和推送链路是否通畅。做完恢复推送后再看日志里是否有积压的消息延迟。词表管理是最容易被低估的优化点。监听跑久了会发现「低价」这种词在每个群里都会命中但真正值得人工介入的其实占比很低长期误报会让值班人员麻木最后看到推送也懒得翻了。我一般每个季度做一次词表瘦身拉出过去三个月的命中记录按命中次数降序排前 20% 的词如果推送后人工处理率不足 5%直接降级为仅记录不推送。这比在代码里改阈值要直观得多。另外还有一个小技巧很多人不会主动想到给监听账号在源群里设置消息免打扰状态Telegram 客户端层面把这个账号设置成静音防止手机端弹通知暴露登录状态。虽然 Pyrogram 登录后不会主动在手机上弹消息但账号如果是同一个手机号码注册的手机上的 Telegram App 如果同时在后台挂着群消息照样会出现在通知栏这就暴露了。资源里给的方案是登出手机 App或者把手机端对应会话全部静音。从那以后我每次部署这类监听项目都会强制走一遍「登录态检查、词表瘦身后台比对、推送限流压测」这三个动作缺一个不上线。监听这套东西不怕功能复杂就怕账号先没了或者人先疲了希望你也能在正式用之前把这几关都过一遍。希望帮到你。本文还有配套的精品资源点击获取