Sly Bye

删除一个 Telegram 机器人后,https://t.me/your_deleted_bot 仍然返回 HTTP 200
并附带一个看起来完全正常的页面。我所知道的每一个链接检查器——CI 操作、
目录脚本、监控 cron 作业——都会永远报告它为健康状态。

如果你维护任何列出 Telegram 机器人的列表,你的列表中已经有部分
已经失效,而你的检查却告诉你一切正常。

重现问题

选择一个从未存在的用户名:

curl -s -o /dev/null -w "%{http_code}\n" https://t.me/nonexistent_test_bot_77712
# 200

Enter fullscreen mode Exit fullscreen mode

两百。没有重定向,没有 404,页面正文中也没有可被状态检查捕获的软 404 标记。

真相所在

状态码在这里毫无用处,但 Open Graph 标题却不同。我测量了四个
用户名——两个活跃机器人,两个不存在:

URL og:title
t.me/BookClassBot (live) BookClass
t.me/instanavy_bot (live) StoryViewer - anonymous instagram story viewer tool
t.me/nonexistent_test_bot_77712 Telegram: Contact @nonexistent_test_bot_77712
t.me/zzz_definitely_not_a_real_bot_9182 Telegram – a new era of messaging

活跃的机器人会在 og:title 中放置自己的显示名称。不存在的机器人会得到 Telegram 的两个占位符之一:Telegram: Contact @<username>,或者——如果用户名在语法上无效——则为通用的 Telegram – a new era of messaging

这就是全部信号。

curl -s https://t.me/some_bot | grep -o '<meta property="og:title" content="[^"]*"'

Enter fullscreen mode Exit fullscreen mode

检查方法

仅使用标准库,无需依赖:

import re
import urllib.request

UA = "Mozilla/5.0 (compatible; linkcheck/1.0)"

DEAD_EXACT = {"Telegram – a new era of messaging", "Telegram"}
DEAD_PREFIX = "Telegram: Contact @"


def telegram_bot_exists(url: str) -> bool:
    """True if the bot behind a t.me URL still exists.

    HTTP status is not usable here: Telegram serves 200 with a placeholder page
    for usernames that were deleted or never existed. The Open Graph title is
    what actually differs.
    """
    req = urllib.request.Request(url, headers={"User-Agent": UA})
    with urllib.request.urlopen(req, timeout=20) as resp:
        html = resp.read(200_000).decode("utf-8", "replace")

    m = re.search(r'<meta\s+property="og:title"\s+content="([^"]*)"', html)
    title = m.group(1).strip() if m else ""

    if not title:
        return False
    return title not in DEAD_EXACT and not title.startswith(DEAD_PREFIX)

Enter fullscreen mode Exit fullscreen mode

注意 Telegram – a new era of messaging 中的破折号。它是 U+2013,而不是连字符。
我为此浪费了几分钟。

它适用于 URL 的各种变体

Mini Apps 可以通过多种形式链接,该检查对所有形式都有效——标题仍然解析为应用的真实名称:

t.me/DefyTONBot/app                            -> "DefyTON"
t.me/arena_defense_bot/arena_defense           -> "ARENA DEFENSE"
t.me/lexicon_snap_bot?startapp=cat_awesome     -> "LexiCon"
t.me/playful_mind_bot/play?startapp=cat_awesome_tma -> "Playful Mind"

Enter fullscreen mode Exit fullscreen mode

因此,你可以将检查指向任何人提交的任何 URL,而无需先将其规范化为空用户名。

为什么要费这个事

因为目录会悄无声息地腐烂,而无声的腐烂正是毁掉它们的原因。

一个所有条目都返回 200 的机器人列表看起来像是得到了维护。每个条目都是
绿色的。与此同时,其中一部分指向一个 Telegram 页面,上面什么都没说,而唯一
发现问题的人是点击链接的读者——一次,然后就再也不相信这个列表了。

我在构建 Telegram Mini Apps 目录时遇到了这个问题。每个条目都通过了
传统的链接检查。只有在添加标题检查后,我才能说机器人确实存在。它现在每周在 CI 中运行,并标记任何悄无声息消失的条目。

值得说明的注意事项

  • 这取决于 Telegram 的标记,这不是 API 契约。如果他们更改了
    占位符字符串,检查就会失效。将字符串固定在显眼的位置,并
    偶尔针对已知不存在的用户名进行测试。
  • 一个存在但已损坏、被封禁或无响应的机器人仍然会通过检查。这只告诉你
    用户名可以解析——仅此而已。
  • 请礼貌地抓取。这是对每个条目的一次请求,按计划进行,而不是爬取。

通用情况

可复用的教训与 Telegram 无关。HTTP 200 意味着服务器做出了响应,而不是该事物存在。许多平台会提供一个装饰过的
“这里什么都没有”的页面,而不是 404——这对人类更好,但对指向它们的每一个
自动化检查都更糟。

每当你检查其他平台上的资源时,要问的问题是:这个主机对肯定不存在的东西会返回什么?先找出答案,然后编写检查。假设一个你从未验证过的 404,是监控器连续几个月报告绿色的原因。


这个问题的出处目录在
这里——检查程序位于
scripts/check.py,公共领域,可自由使用。我还构建了
StoriesFly,上面提到的 Instagram 风格测试
用户名就来自那里。