|
| 1 | +--- |
| 2 | +title: "Hugo 博客集成 Mastodon" |
| 3 | +authors: ["eallion"] |
| 4 | +categories: ["代码"] |
| 5 | +series: ["Mastodon"] |
| 6 | +tags: |
| 7 | + - Mastodon |
| 8 | + - 长毛象 |
| 9 | + - 博客 |
| 10 | +slug: "hugo-blog-embed-mastodon" |
| 11 | +summary: "本文介绍了如何将 Hugo 博客集成到 Mastodon 中。作者首先解释了 API 和 RSS 两种方式可以用来集成 Mastodon,然后详细介绍了使用 API 和 JS 的方法。他使用了一个名为 mastodon-embed-timeline 的 JS 包进行集成,并提供了安装步骤。接下来,作者说明了如何创建一个新的模板文件并修改其中的 CSS 和 JS 引入部分以及配置 Mastodon 参数。最后,作者指导读者在 content 目录下创建一个页面,并插入渲染 Mastodon 容器所需的 HTML 代码。文章给出了具体示例和技巧,帮助读者实现 Hugo 博客与 Mastodon 的集成。" |
| 12 | +draft: false |
| 13 | +date: 2024-10-20T16:05:03+08:00 |
| 14 | +# images: ["/assets/images/og/blog-integration-mastodon.png"] # Delete this line |
| 15 | +--- |
| 16 | + |
| 17 | +### 说明 |
| 18 | + |
| 19 | +所有博客类型(Hugo、Hexo、Wordpress 等)都可以通过这个 JS 插件集成 Mastodon。本文以 Hugo 为例。 |
| 20 | + |
| 21 | +### 前言 |
| 22 | + |
| 23 | +我的博客子栏目「[嘀咕](https://www.eallion.com/mastodon/)」这个微博客(说说、自言自语),几经变迁,现在进化到了集成 Mastodon 时间线。可以把发布在联邦宇宙中的嘟文集成到博客中来。 |
| 24 | +应邀写一篇博客整理一下实现过程。 |
| 25 | + |
| 26 | +PS:`Mastodon` 是一个专有名词,像 `Iphone` `mAcOs` 一样,别写错了,很尬。 |
| 27 | + |
| 28 | +### 选型 |
| 29 | + |
| 30 | +- API or RSS |
| 31 | + |
| 32 | +Mastodon 提供 API(Json)和 RSS 两种信息流输出,可以选一种方式集成。 |
| 33 | + |
| 34 | +- SSG or JS |
| 35 | + |
| 36 | +Hugo、Hexo 等 SSG 类型的博客可以构建时渲染成静态内容,也可以用 JS 动态集成。 |
| 37 | +在早期为了极致的加载速度,我采用静态渲染的方式,发布一条嘟文时,构建一次 Hugo,但是现在构建一次 Hugo 要 3 分多钟,改为用 JS 的方式实时调用 API 了。 |
| 38 | + |
| 39 | +- 我的方式:API + JS |
| 40 | + |
| 41 | +我是用 [https://github.com/eallion/mastodon-embed-timeline](https://github.com/eallion/mastodon-embed-timeline) 这个 JS 包集成的。简单方便易维护。 |
| 42 | + |
| 43 | +预览: |
| 44 | + |
| 45 | +- [https://www.eallion.com/mastodon](https://www.eallion.com/mastodon) |
| 46 | +- [https://codepen.io/ipuntoj/pen/MWppNGL](https://codepen.io/ipuntoj/pen/MWppNGL) |
| 47 | + |
| 48 | +### 安装 |
| 49 | + |
| 50 | +有 3 种方式安装 [mastodon-embed-timeline](https://github.com/eallion/mastodon-embed-timeline) |
| 51 | + |
| 52 | +参考说明文档:[README#installation](https://github.com/eallion/mastodon-embed-timeline?tab=readme-ov-file#installation) |
| 53 | + |
| 54 | +我选择的是通过 `git submodule` 子模块的方式加载,相当于文档中下载的安装方式。 |
| 55 | + |
| 56 | +在博客根目录执行: |
| 57 | + |
| 58 | +```bash |
| 59 | +git submodule add https://github.com/eallion/mastodon-embed-timeline.git assets/lib/mastodon-embed-timeline |
| 60 | +``` |
| 61 | + |
| 62 | +把 mastodon-embed-timeline 下载到博客的 `assets/lib` 这个目录,这个目录可以随便选,只要记得这个路径就可以了。 |
| 63 | + |
| 64 | + |
| 65 | + |
| 66 | +注意事项: |
| 67 | + |
| 68 | +1. 在另外的电脑上 Clone 博客后,要执行 `git submodule update --init --recursive` 才能同步克隆子模块。 |
| 69 | +2. 子模板有更新时,执行 `git submodule update --remote --merge` 更新子模块。 |
| 70 | + |
| 71 | +### 新建模板 |
| 72 | + |
| 73 | +在博客的根目录的 `layouts/_default` 下(没有可以创建),新建一个 `mastodon.html` 文件。按 Hugo 的构建规则 [Docs](https://gohugo.io/templates/lookup-order/) 尽量不要去 themes 的 `layouts` 目录创建,除非你本人是当前主题的开发者。 |
| 74 | +更好的方式可以从主题的 `themes/xxx/layouts/_default` 复制一个模板文件过来,改名为 `mastodon.html`。 |
| 75 | +`mastodon.html` 可以是其他名字,如 `bb.html` `shuoshuo.html` `whisper.html` |
| 76 | + |
| 77 | + |
| 78 | + |
| 79 | +### 模板内容 |
| 80 | + |
| 81 | +> 参考: |
| 82 | +> |
| 83 | +> - [examples/profile-timeline.html](https://github.com/eallion/mastodon-embed-timeline/blob/master/examples/profile-timeline.html) |
| 84 | +> - [layouts/_default/mastodon.html](https://github.com/eallion/eallion.com/blob/main/layouts/_default/mastodon.html) |
| 85 | +
|
| 86 | +模板文件 `mastodon.html` 需要与主题的结构兼容(所以最好的方法是从主题中复制一个差不多的过来),主要修改三个地方: |
| 87 | + |
| 88 | +##### 1. 引入 CSS |
| 89 | + |
| 90 | +在模板文件的靠前部分插入下面的 CSS。 |
| 91 | +两个方式二选一: |
| 92 | + |
| 93 | +```html |
| 94 | +<!-- git submodule 方式 --> |
| 95 | +{{ $mastodonCss := resources.Get "lib/mastodon-embed-timeline/dist/mastodon-timeline.min.css" | minify | fingerprint "sha256" }} |
| 96 | +<link rel="stylesheet" href="{{ $mastodonCss.RelPermalink }}" integrity="{{ $mastodonCss.Data.Integrity }}" crossorigin="anonymous"> |
| 97 | + |
| 98 | +<!-- CDN 方式 --> |
| 99 | +<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@idotj/mastodon-embed-timeline@4.4.2/dist/mastodon-timeline.min.css" integrity="sha256-1UGgxsonaMCfOEnVOL89aMKSo3GEAmaRP0ISbsWa6lU=" crossorigin="anonymous"> |
| 100 | +``` |
| 101 | + |
| 102 | +如果有自定义的 CSS 样式需要,可以参考: |
| 103 | + |
| 104 | +- [assets/css/mastodon-timeline-custom.scss](https://github.com/eallion/eallion.com/blob/main/assets/css/mastodon-timeline-custom.scss) |
| 105 | + |
| 106 | +##### 2. 引入 JS |
| 107 | + |
| 108 | +在模板文件的结尾部分插入下面的 JS。 |
| 109 | +两个方式二选一,是否使用 ESM 版本的 JS 自行决定,不懂就无脑用 UMD 版本: |
| 110 | + |
| 111 | +```html |
| 112 | +<!-- git submodule 且 ESM 方式 --> |
| 113 | +{{ $js := resources.Get "lib/mastodon-embed-timeline/dist/mastodon-timeline.esm.js" }} |
| 114 | +<script type="module"> |
| 115 | + import * as MastodonTimeline from '{{ $js.RelPermalink }}'; |
| 116 | + const myTimeline = new MastodonTimeline.Init(); |
| 117 | +</script> |
| 118 | + |
| 119 | +<!-- CDN 方式 --> |
| 120 | +<script src="https://cdn.jsdelivr.net/npm/@idotj/mastodon-embed-timeline@4.4.2/dist/mastodon-timeline.umd.js" integrity="sha256-E6WPG6iq+qQIzvu3HPJJxoAeRdum5siq13x4ITjyxu8=" crossorigin="anonymous"></script> |
| 121 | +``` |
| 122 | + |
| 123 | +##### 3. 配置 Mastodon |
| 124 | + |
| 125 | +在引入 mastodon-timeline.umd.js 之后,修改下面这些配置(并非所有参数都需要配置) |
| 126 | + |
| 127 | +```html |
| 128 | +<script> |
| 129 | + const myTimeline = new MastodonTimeline.Init({ |
| 130 | + // 包含时间线的 <div> 的 ID |
| 131 | + mtContainerId: "mt-container", |
| 132 | +
|
| 133 | + // Mastodon 实例的 URL,包含 https:// |
| 134 | + instanceUrl: "https://mastodon.social", |
| 135 | +
|
| 136 | + // 选择要在时间线中显示的帖子类型:'local', 'profile', 'hashtag' |
| 137 | + // 默认:local |
| 138 | + timelineType: "profile", |
| 139 | +
|
| 140 | + // 你在 Mastodon 实例上的用户 ID 号 |
| 141 | + // 如果你没有选择 'profile' 作为时间线类型,请留空 |
| 142 | + userId: "107666", |
| 143 | +
|
| 144 | + // 你在 Mastodon 实例上的用户名(包括开头的 @ 符号) |
| 145 | + // 如果你没有选择 'profile' 作为时间线类型,请留空 |
| 146 | + profileName: "@eallion", |
| 147 | +
|
| 148 | + // 标签的名称(不包括 # 符号) |
| 149 | + // 如果你没有选择 'hashtag' 作为时间线类型,请留空 |
| 150 | + hashtagName: "", |
| 151 | +
|
| 152 | + // 加载旋转器的类名(也在 CSS 文件中使用) |
| 153 | + spinnerClass: "mt-loading-spinner", |
| 154 | +
|
| 155 | + // 首选颜色主题:'light', 'dark' 或 'auto' |
| 156 | + // 默认:auto |
| 157 | + defaultTheme: "auto", |
| 158 | +
|
| 159 | + // 向服务器请求的最大帖子数量 |
| 160 | + // 默认:20 |
| 161 | + maxNbPostFetch: "40", |
| 162 | +
|
| 163 | + // 在时间线中显示的最大帖子数量 |
| 164 | + // 默认:20 |
| 165 | + maxNbPostShow: "20", |
| 166 | +
|
| 167 | + // 根据选择的语言/国家指定日期格式 |
| 168 | + // 默认:英式英语(日 - 月 - 年顺序) |
| 169 | + dateLocale: "en-US", |
| 170 | +
|
| 171 | + // 使用选项自定义日期格式 |
| 172 | + // 默认:DD MMM YYYY |
| 173 | + dateOptions: { |
| 174 | + day: "2-digit", |
| 175 | + month: "short", |
| 176 | + year: "numeric", |
| 177 | + }, |
| 178 | +
|
| 179 | + // 隐藏未列出的帖子 |
| 180 | + // 默认:不隐藏 |
| 181 | + hideUnlisted: true, |
| 182 | +
|
| 183 | + // 隐藏转发的帖子 |
| 184 | + // 默认:不隐藏 |
| 185 | + hideReblog: false, |
| 186 | +
|
| 187 | + // 隐藏回复帖子 |
| 188 | + // 默认:不隐藏 |
| 189 | + hideReplies: true, |
| 190 | +
|
| 191 | + // 从个人资料时间线中隐藏置顶帖子 |
| 192 | + // 默认:不隐藏 |
| 193 | + hidePinnedPosts: true, |
| 194 | +
|
| 195 | + // 隐藏用户名下的用户账号 |
| 196 | + // 默认:不隐藏 |
| 197 | + hideUserAccount: false, |
| 198 | +
|
| 199 | + // 将文本内容限制为最大行数 |
| 200 | + // 使用 "0" 表示不显示文本 |
| 201 | + // 默认:""(无限制) |
| 202 | + txtMaxLines: "", |
| 203 | +
|
| 204 | + // 自定义用于显示/隐藏敏感/剧透文本的按钮文本 |
| 205 | + btnShowMore: "显示更多", |
| 206 | + btnShowLess: "显示更少", |
| 207 | +
|
| 208 | + // 将 Markdown 符号 ">" 转换为块引用 HTML 标签 |
| 209 | + // 默认:false(不应用) |
| 210 | + markdownBlockquote: false, |
| 211 | +
|
| 212 | + // 隐藏服务器上的自定义表情符号 |
| 213 | + // 默认:不隐藏 |
| 214 | + hideEmojos: false, |
| 215 | +
|
| 216 | + // 自定义用于显示敏感/剧透媒体内容的按钮文本 |
| 217 | + btnShowContent: "显示内容", |
| 218 | +
|
| 219 | + // 隐藏视频预览并加载视频播放器 |
| 220 | + // 默认:不隐藏 |
| 221 | + hideVideoPreview: true, |
| 222 | +
|
| 223 | + // 自定义用于加载和播放视频的按钮文本 |
| 224 | + btnPlayVideoTxt: "加载并播放视频", |
| 225 | +
|
| 226 | + // 如果帖子包含来自 URL 的链接、照片或视频,则隐藏预览卡片 |
| 227 | + // 默认:不隐藏 |
| 228 | + hidePreviewLink: true, |
| 229 | +
|
| 230 | + // 将预览文本描述限制为最大行数 |
| 231 | + // 使用 "0" 表示不显示文本 |
| 232 | + // 默认:""(无限制) |
| 233 | + previewMaxLines: "", |
| 234 | +
|
| 235 | + // 隐藏回复、转发和点赞的帖子计数器 |
| 236 | + // 默认:不隐藏 |
| 237 | + hideCounterBar: false, |
| 238 | +
|
| 239 | + // 当用户点击帖子中的图片时显示轮播/灯箱 |
| 240 | + // 默认:不关闭 |
| 241 | + disableCarousel: false, |
| 242 | +
|
| 243 | + // 自定义轮播/灯箱的按钮文本 |
| 244 | + carouselCloseTxt: "关闭轮播", |
| 245 | + carouselPrevTxt: "上一个媒体项", |
| 246 | + carouselNextTxt: "下一个媒体项", |
| 247 | +
|
| 248 | + // 自定义指向 Mastodon 页面的按钮文本,放置在时间线末尾 |
| 249 | + // 留空以隐藏 |
| 250 | + btnSeeMore: "在 e5n.cc 查看更多帖子", |
| 251 | +
|
| 252 | + // 自定义重新加载帖子列表的按钮文本,放置在时间线末尾 |
| 253 | + // 留空以隐藏 |
| 254 | + btnReload: "刷新", |
| 255 | +
|
| 256 | + // 在构建时间线之前继续搜索主 <div> 容器。在某些情况下很有用,需要额外的时间来渲染页面 |
| 257 | + // 默认:不应用 |
| 258 | + insistSearchContainer: true, |
| 259 | +
|
| 260 | + // 定义继续搜索主 <div> 容器的最长时间 |
| 261 | + // 默认:3 秒 |
| 262 | + insistSearchContainerTime: "3000", |
| 263 | +
|
| 264 | + }); |
| 265 | +
|
| 266 | +</script> |
| 267 | +``` |
| 268 | + |
| 269 | +### 新建页面 |
| 270 | + |
| 271 | +在博客的 `content` 目录新建一个页面,如 `mastodon.md` 用来显示 Mastodon 的独立页面。有的主题的独立页面结构可能是 `mastodon/_index.md`。 |
| 272 | +Front Matter 里需要指定 `layout` 的名称,要跟模板名称对应上。 |
| 273 | + |
| 274 | +在此 `mastodon.md`(或 `_index.md`)文件中插入以下内容,用于渲染 Mastodon 的 Dom 容器: |
| 275 | + |
| 276 | +```html |
| 277 | +<div id="mt-container" class="mt-container"> |
| 278 | + <div class="mt-body" role="feed"> |
| 279 | + <div class="mt-loading-spinner"></div> |
| 280 | + </div> |
| 281 | +</div> |
| 282 | +``` |
| 283 | + |
| 284 | + |
| 285 | + |
| 286 | +小技巧:Hugo 自带 `aliases` 参数,在 Front Matter 里写上这个参数,可以把这些需要的页面重定向到此页面,不需要另外新建那些页面。 |
| 287 | + |
| 288 | +### 其他 |
| 289 | + |
| 290 | +另外我的页面中,实现了 2 个小功能: |
| 291 | +针对国内访客,劫持图片链接,替换成国内 CDN 实现加速; |
| 292 | +拦截白名单外的域名,添加跳转提示。 |
| 293 | +这些修改都不破坏 `git submodule` 引入的子模块的完整性。 |
| 294 | +可在源码中查看:[layouts/_default/mastodon.html](https://github.com/eallion/eallion.com/blob/main/layouts/_default/mastodon.html) |
0 commit comments