为静态站点配置 Telegram Instant View:从页面标记到部署等待
Telegram 的 Instant View(IV)可以让用户在 Telegram 客户端里直接阅读网页正文,不用跳转浏览器。对内容站来说,文章链接带上 INSTANT VIEW 按钮,阅读体验会好不少。
但要让 IV 稳定工作,特别是对部署在 GitHub Pages 上的静态站点来说,需要把页面标记、IV 规则、Bot 消息和部署时序几个环节串起来。这篇文章按操作顺序整理了关键步骤。
1. 页面端:加语义标记
IV 模板需要从页面 HTML 中提取标题、正文、作者、发布时间等信息。最直觉的做法是靠 CSS 类名来定位——但这非常脆弱。你用 Tailwind 生成的 text-xl、mt-16 之类的类名随时可能因为样式调整而变化,IV 规则会悄悄失效,而且没有任何报错。
更稳妥的做法是在页面模板中加一组专用的 data-iv-* 属性:
<article data-iv-page="post">
<header data-iv-header="true">
<h1 data-iv-title="true">{post.title}</h1>
{post.subtitle && (
<p data-iv-subtitle="true">{post.subtitle}</p>
)}
<div>
<time dateTime={post.date} data-iv-published={String(publishedUnix)}>
{publishedLabel}
</time>
<span data-iv-author="true">{post.author}</span>
</div>
</header>
<div data-iv-body="true"
dangerouslySetInnerHTML={{ __html: post.contentHtml }}
/>
{/* 不希望 IV 抓取的区域 */}
<nav data-iv-ignore="true">...</nav>
</article>
几个要点:
data-iv-title、data-iv-body、data-iv-author分别标记标题、正文和作者data-iv-published放在<time>元素上,值为 Unix 时间戳(IV 引擎可以直接解析)data-iv-ignore标记导航栏、侧边栏、评论区等不应出现在阅读视图中的元素
同时检查一下页面的 <link rel="canonical"> 是否包含了正确的部署子路径。如果你的站点部署在 /alpacanotes 下,canonical URL 也要带上这个前缀。这个问题浏览器不在乎,但 Telegram 在抓取和缓存时会参考 canonical。
2. IV 规则:编写与同域名兼容
到 Telegram Instant View Editor 编写规则。规则用 XPath 语法告诉 IV 引擎从页面中提取哪些内容。
如果你的域名下只有一套页面结构,规则很简单:
| |
同域名多模板的情况
如果同一个域名下还跑着其他站点(比如主站的文档页),HTML 结构可能完全不同。IV 规则是按域名绑定的,一个域名只有一份规则。这时需要用 XPath 的 |(或)运算符做双路匹配:
| |
规则会从左到右尝试匹配,第一个命中的生效。把 data-iv-* 放在前面,回退规则放在 | 后面。
写好之后在编辑器里用几个不同页面测试,确认都能正确提取,然后点 Mark as Published,会得到一个 rhash。把它存到 GitHub Secrets 的 TELEGRAM_IV_RHASH 里。
3. Bot 消息构造
有了 rhash,就可以构造 IV 链接了:
https://t.me/iv?url=<encoded_article_url>&rhash=<your_rhash>
通过 Bot API 发消息时,可以用 link_preview_options 参数指定预览 URL:
| |
注意事项
link_preview_options 的行为和用户手动粘贴链接不完全一样。几个实际经验:
- 消息文本中最好也包含原始文章链接(作为阅读兜底),不要只依赖 IV 链接
- 同一条消息里不要塞太多链接,否则 Telegram 可能会选错一条来生成预览
- 新发布的链接效果最好;旧链接可能因为 Telegram 缓存而无法及时更新 IV 状态
4. GitHub Pages 部署:两个容易踩的坑
4.1 .nojekyll 文件
如果你用 Next.js(或任何会生成 _next 目录的框架)部署到 GitHub Pages,必须在发布根目录放一个空的 .nojekyll 文件。
原因:GitHub Pages 默认启用 Jekyll 处理,而 Jekyll 会忽略所有下划线开头的目录。_next 里放着你全部的 CSS 和 JS 资源——被忽略之后,页面就变成光秃秃的纯文本,CSS/JS 全 404。
如果你用手写 shell 脚本部署,不要忘了这一步:
| |
这个问题的症状很容易被误判为前端代码错误,因为 HTML 本身是正常的,只是样式全丢了。
4.2 发布时序:先确保页面在线,再发通知
这是整个流程中最重要的一条。
代码推送到 gh-pages 分支之后,GitHub Pages 的 CDN 并不是立刻就能返回新内容。可能要几十秒,甚至一两分钟。如果 Bot 在部署步骤完成后立刻发通知,Telegram 去抓取页面时拿到的可能是旧版本、404 或者不完整的页面。IV 引擎匹配失败,卡片不会挂上 INSTANT VIEW 按钮。更糟的是 Telegram 会缓存这个失败的结果。
解决方案是在部署和通知之间加一个等待步骤,轮询目标 URL 直到确认新页面已经可以正常访问:
| |
核心思路:
- 查询参数
deploy_check=${Date.now()}让每次请求的 URL 都不同,避免 CDN 返回缓存 - 检查标准不只是 HTTP 200,还要确认页面内容包含
data-iv-body或文章标题——防止 CDN 返回旧版页面 - 最多等 3 分钟(18 次 × 10 秒),超时则报错中断部署流程
在 GitHub Actions 的 deploy workflow 中,把这个等待步骤放在部署和通知之间:
| |
sleep 20 是轮询通过之后的额外冷却时间。CDN 传播不是瞬时完成的,多等 20 秒可以让更多边缘节点同步完毕。
5. Telegram 客户端缓存
即使以上全部配置正确,你可能仍然会遇到一个现象:同一条 IV 链接第一次打开正常,再次打开偶尔白屏。
这是 Telegram 客户端自身的缓存/渲染行为,不在站点侧能控制的范围内。判断方法很简单:如果第一次能打开,说明规则和页面都没问题;如果是重复打开才出问题,那就是客户端侧的事。
务实的做法是:消息里始终保留原始文章链接作为兜底,验证 IV 效果时用新链接而不是反复测试同一条旧消息。
总结一下关键步骤
- 页面模板加
data-iv-*语义标记 - 在 IV 编辑器中编写基于这些标记的规则,发布拿到
rhash - Bot 消息用
link_preview_options挂 IV 链接,同时保留原始链接 - 部署目录放
.nojekyll - 部署后轮询确认页面在线,再发通知——这一条最容易漏,也最关键
如果你想看我实际排查这些问题时的弯路和心路历程,可以读读 《Instant View 踩坑记:折腾了一整天,答案是「等一下」》。