Skip to content

Commit 5996d26

Browse files
committed
Add a new post
1 parent e935ea9 commit 5996d26

10 files changed

+262
-2
lines changed

content/zh/post/2025/一个自学习的Telegram广告拦截机器人.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
+++
2-
title = "一个自学习的Telegram广告拦截机器人"
2+
title = "基于贝叶斯算法的Telegram广告拦截机器人(一):从问题到产品"
33
date = 2025-08-28T23:45:00-07:00
4-
lastmod = 2025-09-01T21:42:23-07:00
4+
lastmod = 2025-09-14T10:39:32-07:00
55
tags = ["telegram", "design", "programming", "rails", "rust"]
66
categories = ["telegram"]
77
draft = false
@@ -12,6 +12,10 @@ highlighted = true
1212

1313
[English Version](https://ramsayleung.github.io/en/post/2025/a_telegram_spam_blocker_bot_based_on_bayesian/)
1414

15+
系列文章:
16+
17+
- [基于贝叶斯算法的Telegram广告拦截机器人(二):上线半月的故障、挑战与优化之路]({{< relref "基于贝叶斯算法的Telegram广告拦截机器人(二)" >}})
18+
1519

1620
## <span class="section-num">1</span> 序言 {#序言}
1721

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
+++
2+
title = "基于贝叶斯算法的Telegram广告拦截机器人(二):上线半月的故障、挑战与优化之路"
3+
author = ["Ramsay Leung"]
4+
date = 2025-09-13T14:28:00-07:00
5+
lastmod = 2025-09-14T10:38:03-07:00
6+
tags = ["telegram", "design", "programming", "rails", "rust"]
7+
categories = ["telegram"]
8+
draft = false
9+
toc = true
10+
showQuote = true
11+
+++
12+
13+
## <span class="section-num">1</span> 引言 {#引言}
14+
15+
半个月前,我发布了一个基于贝叶斯算法的Telegram广告拦截机器人 `@BayesSpamSniperBot` (<https://t.me/BayesSpamSniperBot>)
16+
17+
项目地址:<https://github.com/ramsayleung/bayes_spam_sniper>
18+
19+
系列文章:
20+
21+
- [基于贝叶斯算法的Telegram广告拦截机器人(一):从问题到产品]({{< relref "一个自学习的Telegram广告拦截机器人" >}})
22+
23+
尽管项目代码开源,但我始终以产品思维运营它。上线半个月以来,经历了故障、用户反馈与持续优化,现将这段经历分享出来。
24+
25+
26+
## <span class="section-num">2</span> 上线即故障 {#上线即故障}
27+
28+
没想到我的产品的第一个线上故障来得这么快,发布的时候直接不可用,把正常消息都给删了,用户在各种途径都向我反馈:
29+
30+
{{< figure src="/ox-hugo/delete_all_message_1.jpg" >}}
31+
32+
{{< figure src="/ox-hugo/delete_all_message_2.jpg" >}}
33+
34+
故障的原因是因为我当时一直在收集垃圾广告的数据,太专注于垃圾广告数据,而忽略了收集的正常数据,
35+
导致垃圾广告数据过多,消息都被认为是垃圾广告,被误删了。
36+
37+
通过补充大量正常消息数据,重新平衡训练集,模型逐渐恢复正常识别能力。
38+
39+
40+
## <span class="section-num">3</span> 挑战 {#挑战}
41+
42+
43+
### <span class="section-num">3.1</span> 邮件与即时消息的差异 {#邮件与即时消息的差异}
44+
45+
我在[《基于贝叶斯算法的Telegram广告拦截机器人(一):从问题到产品》]({{< relref "一个自学习的Telegram广告拦截机器人" >}})里面提到过:
46+
47+
> 常见的 Telegram 广告机器人是大多是基于关键字的,通过匹配关键字进行文本拦截,非常容易被发垃圾广告的人绕过。
48+
>
49+
> 这不禁让我想起了保罗.格雷厄姆在《黑客与画家》一书在2002年介绍的情况:
50+
>
51+
> 当时电子邮件兴起,也有非常多的垃圾邮件,常见的垃圾广告拦截方式是关键字匹配+邮件地址黑名单,但是既低效也容易被绕过。
52+
>
53+
> 保罗.格雷厄姆就创造性地使用贝叶斯算法(Bayes Theorem)实现了一个广告拦截器, 效果竟然出奇地好。
54+
55+
但产品上线之后,我发现聊天软件消息和Email虽然都是文字,还是有很大差别的:
56+
57+
Email 大多时候都是长文的,内容较长,并且大多情况,一封邮件上下文本身也是完整,就有较多的内容,较高的准确度来判断是否是广告。
58+
59+
而 Telegram, 微信这类的即时聊天软件,聊天消息大多都不长,可能把内容分成多条消息来发,就没有完整的上下文,比如:
60+
61+
> 换U
62+
63+
<!--quoteend-->
64+
65+
> 找我
66+
67+
单条消息很较难准确判断是否是广告,所以对即时消息做广告拦截本身就更难, 「短文本+无上下文」是NLP中的经典难题,也是本项目最大的技术挑战。
68+
69+
70+
### <span class="section-num">3.2</span> 漏删与误删 {#漏删与误删}
71+
72+
漏删广告与误删消息是相互矛盾的结果
73+
74+
因为如果想要提高拦截度,自然就需要把置信度降低,把疑似是广告的消息都拦截掉,但是因为拦截强度的上升,又会把正常消息误删。
75+
76+
如果想要不误删消息,那么难免需要增加拦截的阈值,只有超过一定的置信阈值才拦截,这样准确度增加了,难免就会存在漏删广告的可能性。
77+
78+
那么是否可以既准确拦截广告,又避免误删正常消息呢?
79+
80+
在即时消息上下文不齐整,消息短而密集的特性下,可以基本是不可能的。
81+
82+
而用户对消息被误删,以及垃圾广告被漏删的容忍度也是不同的,漏删广告,群成员可以举报或管理员删,但是误删就没法恢复.
83+
84+
所以我只能是提高置信度,把阈值设置成95%, 也就是说只有模型认为超过95%的概率这个是广告,才能把这条消息删掉,虽然会增加漏删广告的概率,但是起码能保证不误删正常消息。
85+
86+
87+
## <span class="section-num">4</span> 优化之路 {#优化之路}
88+
89+
90+
### <span class="section-num">4.1</span> 自动删除消息 {#自动删除消息}
91+
92+
产品上线之后,很快就有用户来试用了,然后有用户就提了一个非常好的优化建议。
93+
94+
{{< figure src="/ox-hugo/detect_spam_and_ban_user.jpg" >}}
95+
96+
这个警告的消息不会自动删除,如果有很多人在群里发广告,那么群里就会有一堆这样的消息,也算是对群消息的污染。
97+
98+
所以用户建议:
99+
100+
> 可以发这个提醒,但在几分钟后也把这个提醒消息删除掉
101+
102+
我觉得这是个非常好的优化体验,因为就把这个功能给加上了,提醒消息本身会在5分钟后自动删除。
103+
104+
{{< figure src="/ox-hugo/auto_delete_warning_message.jpg" >}}
105+
106+
倾听用户的声音是非常重要的,他们可能就会从他们的角度提出非常好的建议。
107+
108+
但是不要盲目听从用户的建议,比如也有用户建议:
109+
110+
> 我觉得还应该有以下功能.
111+
>
112+
> 1. 恢复消息, 恢复用户. (让管理员恢复误删的消息和用户)
113+
> 2. 主动投喂正常消息. (让管理员主动投喂一些消息. 比如, 群里面昨天 的消息, 随便选一些正常的, 投喂给机器人)
114+
115+
恢复消息这个功能没有太大必要,并且也不实用,因为恢复消息这个功能本身就很微妙,是直接恢复被删除的消息呢,还是重新发一条新消息?
116+
117+
如:
118+
119+
> - 2025-09-09 10:01:00 张三: 我今天吃了鸡翅
120+
> - 2025-09-09 10:02:00 李四:鸡翅有啥好的(被误删消息)
121+
> - 2025-09-09 10:03:00 王五:人家就喜欢吃,你管得着嘛
122+
123+
如果是直接恢复被删除的消息,当前时间是 `2025-09-09 11:00:00` ,把消息恢复之后,还有人会手动刷历史消息,查找旧消息么?
124+
125+
Telegram客户端不一定支持会跳转被恢复的旧消息,这意味着,你恢复误删的消息,也没人看得到。
126+
127+
假如是重新发一条新消息 `鸡翅有啥好的`, 因为缺失了上下文,群里的人反而会疑惑,你在说什么。
128+
129+
解决误删问题本质是提高拦截的准确率,而非考虑如何恢复被误删消息,准确率提高了,误删就会减少,
130+
自然就不需要考虑如何恢复消息,用户体验还会更好.
131+
132+
而主动投喂消息这个想法有点理所当然了。
133+
134+
没有任何群管理员有意愿帮忙训练这个机器人,对用户而言,他们只想要一个好用的广告拦截机器人,至于怎么开发,训练出来的,用户并不在乎。
135+
136+
所以用户不会有意愿和动力来优化这个机器人,不好用就再换一个好了,更何况,逐条消息收集的效率实在太慢太慢了,
137+
所以我后面想出了一个比手工收集数据提效至少100倍的主意。
138+
139+
140+
### <span class="section-num">4.2</span> 过滤重复消息 {#过滤重复消息}
141+
142+
发现人难免会有误区,总会以为别人会和自己一样,之前看到发垃圾广告的人的时候,总会觉得他们是正常的用户手工发。
143+
144+
但是最近几天发现了一些规律,有用户把同一条消息反复发,不同的群还是发同样的内容
145+
即使是复制粘贴也难免会多个或者少个空格,然后消息被删了还一直发同样的内容。
146+
147+
此外,还有一些群,内容的聊天内容都是广告,我还很奇怪,大家都在发广告,正常用户不都跑了嘛?
148+
149+
{{< figure src="/ox-hugo/spam_group.jpg" >}}
150+
151+
此时,我才意识到,发消息的都是机器人。
152+
153+
所以我加了个优化,计算消息内容的 hash 值,保存到数据库,并为这个字段建立索引。
154+
155+
后面检测消息的时候,先根据 hash 值查询,检查是否存在已有的消息,如果消息已经存在且已经被标记成广告或者正常消息,那么就无需再使用模型检测,可以直接返回之前的检测结果。
156+
157+
这样既提高了准确度,也优化了性能,也减少了人工干预的成本。
158+
159+
同一个用户如果在同一个群发了三条广告,那么就会自动被封禁掉,也就是相同的广告只要发三条,就会马上被自动封禁掉。
160+
161+
162+
### <span class="section-num">4.3</span> 自动收集数据 {#自动收集数据}
163+
164+
使用机器学习算法来实现一个类似的垃圾广告过滤器并不难,困难的持续收集高质量的训练数据,训练数据是非常宝贵的,毕竟数据才是核心资产。
165+
166+
而对于我这个产品来说,最难的是冷启动时的训练数据问题:
167+
168+
因为没有训练数据,模型就不准确,模型不好用就不会有人使用,自然也无法通过用户来收集垃圾广告数据,就无法良性循环,
169+
存在一个鸡生蛋,还是蛋生鸡的问题。
170+
171+
所以冷启动时,我是手动加了非常多的 Telegram大群,然后人工在里面收集垃圾广告.
172+
173+
但是这个效率实在是太低了,我收集了快一周才只有几百条数据,
174+
一个是我无法一直盯着各个群,另外是这种20w的大群,一般都会有几个管理员,会手工删除广告,一会没有看垃圾广告数据就会被删掉了。
175+
176+
这样手工收集数据实在在太痛苦了,我就在想有没有什么办法自动收集数据呢?
177+
178+
我本来想的是直接把我的机器人拉到这些大群里面,即使没有管理员权限无法删除消息,也可以收集数据嘛,后面才意识到 Telegram 有个规定,只有群管理员才有权限加机器人,因为我不是管理员,所以自动没有权限添加机器人。
179+
180+
但是 telegram 的客户端是开源的,他们提供了 [tdlib](https://github.com/tdlib/td)&nbsp;[^fn:1]这个跨平台的 C++ 库便于社区构建第三方的 Telegram 客户端,那么我自然可以使用这个库来登录我自己的账号,然后使用我的模型来过滤消息,然后把疑似广告的数据都收集起来,我再人工确认下。
181+
182+
(顺便说一下,tdlib 和 [telegram-bot-api](https://github.com/tdlib/telegram-bot-api)&nbsp;[^fn:2]这两个库竟然都是同一个[作者 Aliaksei Levin](https://github.com/levlam)&nbsp;[^fn:3]在维护,实在是太强了。)
183+
184+
我现在需要做的就是添加各种大群,然后程序就会自动监听并收集数据,我再人工批量确认下。
185+
186+
{{< figure src="/ox-hugo/spam_group_list.jpg" >}}
187+
188+
{{< figure src="/ox-hugo/telegram_data_collector.jpg" >}}
189+
190+
{{< figure src="/ox-hugo/maybe_spam_list.jpg" >}}
191+
192+
实现起来也不复杂, 200行代码就实现了这个监听消息,分析,并且收集的功能。
193+
194+
得益于这个自动化的数据收集程序,我1周不到就收集了近上万条的高质量训练数据了,效率实在高太多太多了。
195+
196+
懒惰真的是程序员的美德, 这个经历再次证明:自动化工具往往能成倍提升效率,这正是工程师价值的体现.
197+
198+
199+
## <span class="section-num">5</span> 推广 {#推广}
200+
201+
所谓酒香也怕巷子深,没有用户使用,代码写得再好也没有意义。从产品角度,运营推广至关重要。
202+
203+
作为个人开发者,我没有大量粉丝关注,也没有营销预算,因此采用了传统的推广方式:撰写博客并在相关社区分享。
204+
205+
我撰写了两篇双语博客文章,中文版本分享至:
206+
207+
- V2EX: [我写一个基于贝叶斯算法的 Telegram 广告拦截机器人](https://www.v2ex.com/t/1156542)
208+
- Emacs China: [分享一下我用Emacs写的Telegram广告拦截机器人](https://emacs-china.org/t/emacs-telegram/30043)
209+
- 微信公众号「宫孙说」:<https://mp.weixin.qq.com/s/Sgq9vDqpHykwge11bwZJrw>
210+
- 项目被收录到[阮一峰的科技爱好者周刊(第 364 期)](https://www.ruanyifeng.com/blog/2025/09/weekly-issue-364.html)
211+
212+
英文版本发布至:
213+
214+
- Reddit: [Built my first Rails project: A Telegram spam blocker bot](https://old.reddit.com/r/rails/comments/1n6p791/built_my_first_rails_project_a_telegram_spam/) - 获得一些讨论
215+
- HackerNews:<https://news.ycombinator.com/item?id=45105908>
216+
- Twitter: <https://x.com/foobar_ramsay/status/1967277792267247916>
217+
218+
虽然推广效果有限,但这些努力为项目带来了最初的用户关注。
219+
220+
221+
## <span class="section-num">6</span> 成果与数据 {#成果与数据}
222+
223+
上线半个月,截止到目前为止, 已经有超过80个群使用过这个机器人,用户数已经比我预期要多了:
224+
225+
| 指标 | 数值 |
226+
|------------|-----|
227+
| GitHub Stars | 106 |
228+
| 使用群组数 | 83 |
229+
| 训练数据量 | 10543 |
230+
231+
最开心的是看到我自己的程序在这些群成功拦截垃圾广告,就很有成就感,证明我做的东西真的能用户解决问题。
232+
![](/ox-hugo/detect_spam_success.jpg)
233+
234+
235+
## <span class="section-num">7</span> 结语 {#结语}
236+
237+
这半个月的运营让我深刻体会到:产品不是代码写完就结束,而是从用户反馈中不断迭代的开始。
238+
239+
产品是需要持续运营的,而写代码只是产品生命周期的其中一个环节,甚至不是最耗费时间的环节。
240+
241+
下一步,我计划进一步优化模型准确率,并探索多语言支持,也欢迎关注我的频道或提交Issue一起讨论。
242+
243+
- 开源地址:<https://github.com/ramsayleung/bayes_spam_sniper>
244+
- 立即体验:<https://t.me/BayesSpamSniperBot>
245+
- 我的频道(菠萝油与天光墟): <https://t.me/pipeapplebun>
246+
247+
<div class="qr-container" center>
248+
249+
<img src="/ox-hugo/qrcode_gh_e06d750e626f_1.jpg" alt="qrcode_gh_e06d750e626f_1.jpg" class="qr-container" width="160px" height="160px" center="t" />
250+
公号同步更新,欢迎关注👻
251+
252+
</div>
253+
254+
[^fn:1]: <https://github.com/tdlib/td>
255+
[^fn:2]: <https://github.com/tdlib/telegram-bot-api>
256+
[^fn:3]: <https://github.com/levlam>
32.6 KB
Loading
18.2 KB
Loading
15.2 KB
Loading
311 KB
Loading

static/ox-hugo/maybe_spam_list.jpg

550 KB
Loading

static/ox-hugo/spam_group.jpg

514 KB
Loading

static/ox-hugo/spam_group_list.jpg

223 KB
Loading
488 KB
Loading

0 commit comments

Comments
 (0)