2025.01.03 更新
- 增强: 完成云函数项目重构,代码逻辑更清晰、高效。由 Cursor 提供技术支持
9.27 更新
优化 bbtalk.js 方法,解决 pjax 兼容问题
6.28 更新:Safari 无法播放 mp4 问题已破案
下文提到哔哔闪念发送视频后,存储在我的cdn.guole.fun
上,结果发现 Safari 无法播放。一顿操作猛如虎,各种排查最终确认问题如下,且已根本解决。
原因:
罪魁祸首:workbox
,我早前通过正则给 cdn.guole.fun
域名加了 workbox 缓存,结果发现如果不特殊配置,workbox 缓存的视频无法正常响应 Safari 的 range 请求。甚至 Chrome 也偶现无法播放,不光 Safari。我原来是这样用的,简单粗暴:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| workbox.routing.registerRoute( /^https:\/\/cdn\.guole\.fun/, new workbox.strategies.CacheFirst({ cacheName: "cdn-static-libs", plugins: [ new workbox.expiration.ExpirationPlugin({ maxEntries: 1000, maxAgeSeconds: 60 * 60 * 24 * 30 }), new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }) ] }) );
|
解决方案:
只需改为如下即可。压根不用 workbox 缓存 JSON 、mp4 资源了(workbox缓存音频后,也会影响其正常响应)。Chrome 官方给了说明,该日再研究折腾:提供缓存的音频和视频:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| workbox.routing.registerRoute( ({url}) => { return url.href.match(/^https:\/\/cdn\.guole\.fun/) && !url.pathname.includes('/json/bb-json/') && !url.pathname.includes('/media/bb-media/'); }, new workbox.strategies.CacheFirst({ cacheName: "cdn-static-libs", plugins: [ new workbox.expiration.ExpirationPlugin({ maxEntries: 1000, maxAgeSeconds: 60 * 60 * 24 * 30 }), new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }) ] }) );
|
6.26 再更新:新增说说转储 JSON 功能
如题,现在云函数可以在你新增 / 删除 / 修改 / 插入 / 追加说说内容后,自动拉取变动数据生成 JSON 文件,每个 JSON 文件的条数 = 环境变量设置的PageSize
值。也就是说,前端读 JSON 即可,不用直接请求 LeanCloud 了,提升哔哔闪念的加载速度!(JSON 自动上传腾讯云 COS ,配合 CDN 加持,老上头了)
新的资源 URL 是下面这样,json/bb-json
这部分是你自己在环境变量中设置的上传路径 Tcb_JsonPath
值:
1
| https://cdn.guole.fun/json/bb-json/bbtalk_page${page}.json
|
生成的 JSON 暂时明明格式为:”bbtalk_page=” + 页数 + “.json” 。前端读取时,只用递增这个页面即可。首页轮播哔哔,固定读取 bbtalk_page1.json
就完事儿了!配合上述功能,哔哔闪念独立页面的前端 JS 方法,调整 fetch
请求地址为如下类型即可(v1.0.1 以上版本):
1
| fetch(`https://cdn.guole.fun/json/bb-json/bbtalk_page${page}.json`)
|
首页轮播哔哔,其中的 jsonUrl
变量值调整为如下这种写死即可:
1
| jsonUrl = 'https://cdn.guole.fun/json/bb-json/bbtalk_page1.json';
|
更新:解决 Safari 无法播放视频问题
- 原因:Safari 处理
video
标签时,会先发一个获取视频 1 字节信息的请求,拿视频封面之类的。只有用户点击播放时,才会再发一个请求,获取后面的完整内容。这里需要 cos 正常响应range
请求。但是我原本使用了本站的cdn.guole.fun
来获取媒体资源,但这个 CDN 加速域名配置的加速类型是“ CDN 网页小文件”,似乎会覆盖导致存储桶本身自动给 .mp4 文件设置的Content-Type: video/mp4
头部信息不生效,Chrome 中正常,但 iOS Safari 中就不行(iOS 里的 Chrome / 微信内嵌浏览器都使用 Safari 内核……) - 解决:测试直接放存储桶的资源链接,是可以正常播放的。但是我这边重新设置
cdn.guole.fun
的加速类型为“音视频点播”时报错,设置不成功。我就曲线解决了,重新创建了个media.guole.fun
加速域名(与cdn.guole.fun
都关联同一个存储桶……),专门用来读取多媒体资源,cdn.guole.fun
先保留用作它用,后续考虑要不要删除重新创建为“音视频点播”(2023.6.26 已删除重建解决)。 - 创建好相关域名后,拆分成:顶级域名、二级域名、子域,在下面环境变量中配置既可。
综上所述,如果你准备新建一个存储桶用来放说说多媒体资源,一定记得选加速类型为“音视频点播”。不建议直接暴露使用存储桶的原始域名(没有 CDN 加速)。同时,存储桶和加速域名设置好防盗链,免得被盗刷……
原文
分享下本站说说的发展历程。
早前,使用黑石哔哔、木木的 bber 、小康的 ispeak 等,后来腾讯云开发收费了,就陆续没法用了。
目前本站哔哔存储在 LeanCloud
(其他 MongoDB 啥的应该也没问题),/bb/
和首页轮播是我写了个 Vercel
云函数接口,加了个缓存功能,同时避免 LeanCloud
的访问秘钥在前端被泄露。欢迎扫描关注,先行体验效果再决定要不要折腾。

这个端午竟然没出去玩!!就把之前用木木的 bber 复刻了出来,同时新增了发视频、发位置、发链接卡片等功能。微信其实还支持个语音消息,由于是 amr
格式音频,浏览器没法播放,服务端复用 FFmpeg
等插件转换格式,又一直环境不对……心态崩了,干脆摆烂……谁搞定了,欢迎 PR 或下面留言通知我啊!
注:本文包含的微信发说说功能,部署在腾讯云 Serverless 里,其他 Vercel 、Railway 应该也没问题,我没折腾。之前域名备案,一定要有服务器之类的 IP ,就买了三年 Serverless 套餐,个人使用不超量的话也很便宜,所以 也不能闲着便宜了腾讯不是……
效果演示
[{"url":"https://cdn.guole.fun/img/posts/bb-wechat1.jpg","alt":"帮助","title":""},{"url":"https://cdn.guole.fun/img/posts/bb-wechat2.jpg","alt":"发链接卡片","title":""},{"url":"https://cdn.guole.fun/img/posts/bb-wechat3.jpg","alt":"发视频/文本","title":""},{"url":"https://cdn.guole.fun/img/posts/bb-wechat4.jpg","alt":"搜索","title":""},{"url":"https://cdn.guole.fun/img/posts/bb-wechat5.jpg","alt":"发图片","title":""},{"url":"https://cdn.guole.fun/img/posts/bb-wechat6.jpg","alt":"发定位","title":""}]
支持特性
- 包含
- 部署在云函数上,无需服务器
- 使用微信随时随地发布闪念瞬间(memos 很香,但是得有机器……)
- 在原来 @木木木 的
bber-weixin
基础上升级而来,新增支持:发位置、发链接卡片、发视频功能; - 发图片:直接拍照片或发本地图片给公众号既可(存在腾讯云 cos ,原有的去不图床逻辑没删,但我也没验证);
- 发视频:录视频、或本地视频直接发给公众号既可(存在腾讯云 cos,支持 .mp4 / .flv / .ts ,其他格式受腾讯 CDN 支持范围影响,不支持 Safari 播放);
- 发位置:需要在 /bb/ 页面引入资源,详见使用指南;(其他页面嵌入地图,见另一个插件:hexo-tag-map)
- 发链接卡片:直接分享卡片到公众号;
发语音:本来想借用 FFmpeg 在服务端转换语音音频 amr 为 mp3,前端再调 aplayer 等播放,但是夭折了……云函数里一直搞不定 FFmpeg 的环境……
部署到 LeanCloud
这部分很简单,到 LeanCloud 注册个 国际版
账号,然后创建一个应用,比如就叫 bb
,然后新建 2 个集合 content
、UserBindingStatus
。
可以在 LeanCloud 导入导出功能里,把你原来的数据导进去(建议部署后,发布 1 条消息,数据库自动创建字段后,再导出到本地,按照字段映射手动修改后,再导入以前的数据进去。做好备份,大不了多试几次)。content
集合差不多有这几个字段:
字段名 | 类型 | 说明 |
---|
objectId | String | 唯一标识符,LeanCloud 自动创建,无视它; |
ACL | ACL | 读写权限,LeanCloud 自动创建,无视它 |
MsgType | String | 微信消息类型,前端用来处理一些逻辑 |
content | String | 说说内容 |
from | String | 说说来源,自定义 |
other | String | 一些特殊消息,比如位置消息,保存脚本,前端插入后渲染出地图 |
createdAt | Date | 创建日期时间,LeanCloud 自动创建,无视它;若导入数据,得修改这个字段,ISO 格式的时间戳,比如 2023-06-24T07:20:55.650Z |
updatedAt | Date | 更新日期时间,LeanCloud 自动创建,无视它 |
UserBindingStatus
集合不用管,字段云函数会自动创建。(其实 content
集合应该也不用手动创建字段……只是我没试过)
获取 LeanCloud 凭证
- 上述 content 和 UserBindingStatus 集合需要在同一个应用里,这样 appid 才一样;
- 在 LeanCloud 应用凭证 界面(设置——应用凭证),找到
AppID
/ AppKey
/ MasterKey
信息,记录下来下文使用。

获取腾讯云 API 访问密钥
- 注册并认证腾讯云开发者;
- 在腾讯云 访问管理 界面新建 1 个密钥。(建议创建一个子用户,可以更小颗粒度限制权限范围,更安全);
- 如果使用子用户(访问方式选择“编程访问”),在关联策略中,搜索
cos
,选择QcloudCOSDataFullControl 对象存储(COS)数据读、写、删除、列出的访问权限
这一项既可;(为啥要删除权限,因为你发指令删除说说时,如果包含了视频图片等资源,是同步删了存储桶文件的)

4. 记录下 SecretId
/ SecretKey
;

获取微信公众号凭证
- 注册一个微信公众号,个人只能注册订阅号 微信公众平台;
- 在公众平台 —— 设置与开发 —— 基本设置界面,记录下
AppID
/ AppSecret
; - 在服务器配置中,选择兼容模式,自定义
Token
,随机生成 EncodingAESKey
都记录下来; - 不要关闭这个页面,等下回来配置服务器地址;

创建云函数
- 访问腾讯云云函数平台 腾讯云 Serverless;
- 创建一个云函数,选择
从头开始
,函数类型:事件函数
;函数名称:自定义;地域:中国香港
(要访问 LeanCloud,香港可能好一点);运行环境:Nodejs 16.13
;

3. 其他配置中,启用日志投递
。默认格式;启用固定公网出口 IP
;触发器选择 自定义创建
,触发方式选择 API 网关触发
,勾选集成响应
,其他用默认的不用改。
4. 创建一个 src
目录,函数代码拷贝我 Github 仓库:BBtalk-Serverless,总共是 6 个文件,都放在 src
目录下保存。不要在线安装依赖,部署太慢了,往下看;
5. 本地创建个空目录,git clone
或手动下载解压缩都可以,下载我的代码包Github 仓库:BBtalk-Serverless,然后 cd src
后,npm install
安装依赖;
6. 进入 src
下的 node_modules
目录,全选压缩为 .zip 压缩包;(一定要进入node_modules
目录全选压缩)
7. 在腾讯云云函数页面,选择层
,新建层
,名称随意,上传刚才压缩的 zip 包;
8. 进入函数服务 —— 刚才创建的云函数 —— 层管理
,绑定刚才创建的 层
;

9. 回到 函数配置
,右上角编辑
,创建以下环境变量(Tcb_Bucket / Tcb_Region / Tcb_ImagePath / Tcb_MediaPath / Tcb_JsonPath 请自行手动创建):
key | value |
---|
Binding_Key | 绑定密钥,自定义一条字符串 |
LeanCloud_ID | 上面记录的 LeanCloud AppID |
LeanCloud_KEY | 上面记录的 LeanCloud AppKey |
LeanCloud_MasterKey | 上面记录的 LeanCloud MasterKey |
Tcb_SecretId | 上面记录的腾讯云 API 访问秘钥 SecretId |
Tcb_SecretKey | 上面记录的腾讯云 API 访问秘钥 SecretKey |
WeChat_Token | 上面记录的微信公众平台自定义的 Token |
WeChat_appId | 上面记录的微信公众平台自定义的 AppID |
WeChat_appSecret | 上面记录的微信公众平台自定义的 AppSecret |
WeChat_encodingAesKey | 上面记录的微信公众平台自定义的 EncodingAESKey |
Upload_Media_Method | 图片 / 视频的上传方式,可选值:cos 或qubu ,要使用发视频功能必须配置为cos ;qubu 我没测试… |
Tcb_Bucket | 腾讯云存储桶名称(如“file-1258755354”)。若 Upload_Media_Method 为 cos ,此项必填 |
Tcb_Region | 腾讯云存储桶地域(如“ap-guangzhou”)。若 Upload_Media_Method 为 cos ,此项必填 |
Tcb_ImagePath | 腾讯云存储桶图片路径(如“/img/bb-img/”,即图片上传你要放在什么路径)。若 Upload_Media_Method 为 cos ,此项必填 |
Tcb_MediaPath | 腾讯云存储桶媒体路径(如“/media/bb-media/”,即媒体上传你要放在什么路径)。若 Upload_Media_Method 为 cos ,此项必填 |
Tcb_JsonPath | 腾讯云存储桶 JSON 转储路径(如“/json/bb-json/”,即媒体上传你要放在什么路径)。若 Upload_Media_Method 为 cos ,此项必填 |
TopDomain | 顶级域名(如 media.guole.fun 中的 “fun”)。若 Upload_Media_Method 为 cos ,此项必填 |
SecondLevelDomain | 二级域名(如 media.guole.fun 中的 “guole”)。若 Upload_Media_Method 为 cos ,此项必填 |
SubDomain | 子域(如 media.guole.fun 中的 “media”)。若 Upload_Media_Method 为 cos ,此项必填 |
PageSize | 从 LeanCloud 获取数据转 Json 的每页条数。若 Upload_Media_Method 为 cos ,此项必填 |
- 提交保存环境变量后,回到函数配置界面,点击
部署
; - 在云函数
触发管理
中,记录下访问路径
;
完成微信公众号配置
在刚才停留的公众号配置界面,服务器地址填写上面记录的云函数访问路径
,然后提交,应该就成功了。
IP 白名单:一定要配置这个,不然发图、视频时,取不到微信 Access token 。ip 就是上面云函数的固定出口 IP 地址。
这个地方如果微信校验不通过,重点检查云函数环境变量 Token 与微信这边填的是否相同,验证云函数执行方法是否为index.main_handler
,浏览器访问云函数访问路径
,显示403
错误应该就没问题;
Hexo 创建说说页面
在 Blog 根目录,执行hexo new page bb
新建一个页面,bb
是说说页面路径,可以自定义;接着:
在 /bb/
目录下的 index.md
文件中,添加以下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| --- title: 哔哔闪念 date: 2021-06-07 17:29:43 type: "bb" aplayer: false top_img: false #aside: false ---
{% note success %} 本页使用「哔哔闪念 2.0」生成,记录 @夜的第八章 的胡思乱想<a href="/posts/17745" class="banner-button"><i class="iconfont icon-you"></i><span class="banner-button-text">部署指引</span></a> {% endnote %}
<!-- 存放闪念的容器 --> <!-- <div class="js-pjax"> --> <div id="bbtalk"><p class="info"></p></div> <div id="loading" style="text-align: center"><img src='/img/loading1.gif' alt="loading…" class="no-lightbox nolazyload" style="width: 11rem;"></img></div> <button id="load-more-btn" class="next" style="background:linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab); background-size: 1000% 1000%;box-shadow: 0 0px 6px 1px rgb(7,17,27,15%)">再翻翻</button> <div class="bbtalk-bottom-tip" id="bbtalk-bottom-tip" style="display: none;color:#CACACA;text-align: center;font-size: 14px;">~ 我是有底线的 ~</div> <!-- <div class="gaodeMap-22.521799-113.99757" id="gaodeMap-22.521799-113.99757"></div> --> <link rel="stylesheet" href="//unpkg.com/hexo-tag-map/lib/leaflet@1.7.1.css"> <link rel="stylesheet" href="/css/bbtalk.css" onload="this.media='all'"> <script data-pjax="" src="//unpkg.com/hexo-tag-map/lib/leaflet@1.7.1.js"></script> <script data-pjax="" src="//unpkg.com/hexo-tag-map/lib/leaflet.ChineseTmsProviders@1.0.4.js"></script> <script src="/js/timeago@4.0.2.min.js"></script> <script src="/js/BBtalk.js"></script> <script> bbtalk.init({ bbtalk_name: '夜的第八章', // 昵称 bbtalk_avatar: '/img/gl.jpg', // 头像 bbtalk_avatar_svg: '', //头像后的图标,可留空使用默认的 aplayer_js: '/js/APlayer.min.js', //自定义引入 aplayer // leaflet_css: '', // 无必要可注释 // leaflet_js: '', // 无必要可注释 // ChineseTmsProviders_js: '', // 无必要可注释 }); </script>
|
再去 /source/css/
目录下创建一个 bbtalk.css
文件(也可以命名其他的或者放在其他地方,上面 md 文件里记得同步修改),内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
| [data-theme=light] { --bbtalk-from-color: #fff; --bbtalk-from-chrome: #aa96f5; --bbtalk-from-wechat: #49c65b; --bbtalk-from-postman: #7194a4; --bbtalk-from-edge: #2196f3; --bbtalk-from-siri: #8290d6; --bbtalk-from-bg: #9e9e9e; --bb-TimeTitle-bg: rgba(48, 49, 51, 0.85); --bb-TimeTitle: rgba(255, 255, 255, 0.9); --bbtalk-url-bg: #fff; } [data-theme=dark] { --bbtalk-from-color: rgba(255,255,255,0.7); --bbtalk-from-chrome: #8573c8; --bbtalk-from-wechat: #31933f; --bbtalk-from-postman: #607d8b; --bbtalk-from-edge: #1b84d8; --bbtalk-from-siri: #6b7bce; --bbtalk-from-bg: #847f7f; --bb-TimeTitle-bg: #042537; --bb-TimeTitle: rgba(255, 255, 255, 0.7); --bbtalk-url-bg: #222528; }
#article-container img:not(#loading img) {margin: unset;} #bbtalk div.timenode { background: unset; box-shadow: unset; border: 1px solid var(--guole-card-border); margin-top: 1.8rem; } #bbtalk div.timenode:hover { background: unset; box-shadow: unset; border: 1px solid rgba(28,105,237,0.5); } #loading img { text-align: center; display: inline-block; } #loading .img-alt.is-center { text-align: center }
@font-face { font-family: "iconfont"; src: url("//at.alicdn.com/t/font_1755564_z4mhxbw13mq.eot?t=1586882866168"); src: url("//at.alicdn.com/t/font_1755564_z4mhxbw13mq.eot?t=1586882866168#iefix") format("embedded-opentype"), url("data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAALcAAsAAAAAB2gAAAKPAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCCfgqCUII8ATYCJAMMCwgABCAFhG0HQBuEBsiuMRkynL4YFCHtLKs4b+k7/80U8DwHIB6+xtr7u3vniDZI6tmnkyDS8NC5iFgJlE7pTPcO8e5b+803aYQy8YtH1JPKyZzY3ttb1EonioVGSBAiRJp4JV9cFppCxLpe2z8sUs37O4QEDFRM0lFeAw4XN6bQPG06zTG8DRwHFOBeGEVWIjE+mP8wduVFPCbQNK4ktN8/OgFJhbEqEFemLkEyFVcUVqoL1ZKDRbyIqafb9AA8+9+PX41IklQyY+PBSZ8KnR+9iBx0OxZcp8PxvGDHyFgACnFY6j8QCfMLIppKPmoCrGtW8OG/y0eufLX/8AiJqDKa22AGWpMzUth6p6aARySQQRUUL4AnSBmuyvIwPdbz21OX5ybO5duOJifaLl6cvkrd1HR7kWEy6yikff29PvgO17V/Tf/9f/oN3PDqz22a719IYlZJHQetpLY08MV6fwFDrF0B6F4XyB3/FyEEnZnU2XGtVwHvpxtA1d6pftKB4AcoWo4U1QQbiiy35Xow27whbwK2NNFlhAc99zeOtM4S6ikFQ1IzhqxuglAoC6ho2UBV3Raa5g0cbxnCRURpYM4pQOh3h6TXG7J+j4RCeULFqFdU9UcETYfRdGHLVJjpHk6SIBXlTTQV5mmWS6SD1jHSVx2JJ1U+a4p4YKawvrSSLQ2QR3yMLcGa3hBCQ40zF/vBc+Q4DH3OLFJExRDCb64un5S9qaIwF9L2cEQiEBWSbUImBePR3PFCOvf5MUS3yiHhNR0VySmEC5jTo7qKWgvCgN5r1XEtrwTW6BoEQYM0HONC/cBLHDPPIL98noUohAqjR7uvWVV1HlVbfWV+o/t8J6DJOJ4jRY6idmVKnmVS1q8tK8zfBAAA") format("woff2"), url("//at.alicdn.com/t/font_1755564_z4mhxbw13mq.woff?t=1586882866168") format("woff"), url("//at.alicdn.com/t/font_1755564_z4mhxbw13mq.ttf?t=1586882866168") format("truetype"), url("//at.alicdn.com/t/font_1755564_z4mhxbw13mq.svg?t=1586882866168#iconfont") format("svg"); } #bbtalk img { display: block; max-height: 16rem; }
#bbtalk-content a:not(.leaflet-control-container a) { display: inline-block; text-align: left; } .iconfont { font-family: "iconfont" !important; font-size: 16px; font-style: normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
.icon-lianjie:before { content: "\e6a3"; }
.icon-lianjie-copy:before { content: "\e6a4"; }
#article-container .banner-button { height: 40px; width: 118px; border-radius: 20px; justify-content: center; background: #67b010; color: #fff;; align-items: center; z-index: 1; transition: .3s; cursor: pointer; backdrop-filter: saturate(180%) blur(20px); -webkit-backdrop-filter: blur(20px); transform: translateZ(0); border-bottom: unset !important; right: 1.4rem; position: absolute; display: inline-flex; bottom: 0.68rem; }
#article-container .banner-button:hover { background: #007909; color: #fff; }
#article-container .banner-button i { margin-right: 6px; font-size: 22px }
@media screen and (max-width: 1200px) { #article-container .banner-button { height: 32px; width: 98px; right: 0.8rem; font-size: 14px; }
#article-container .banner-button i { margin-right: 4px; font-size: 16px }
.map-box .leaflet-container { height: 300px; } }
@media screen and (max-width: 768px) { #bbtalk div.timenode .body { margin: 0; padding: 5px; border-radius: 8px; display: block; } #bbtalk div.timenode .card-name { display: flex; align-items: center; margin: 10px 5px 5px 5px; } #bbtalk div.timenode { padding: 8px 14px; margin-top: 1.4rem; } } div.timenode { position: relative; padding: 10px 20px; border-radius: 10px; background: rgba(255, 255, 255, 0.1); box-shadow: 0 3px 8px 6px rgba(7, 17, 27, 0.06); overflow: hidden; margin-top: 32px; user-select: none; } div.timenode:hover { box-shadow: 0 2px 10px 5px rgba(7, 17, 27, 0.16); transition: all .15s ease-in-out; } div.timenode:last-child:after { height: calc(100% - 26px - 16px); border-bottom-left-radius: 2px; border-bottom-right-radius: 2px; } div.timenode .time { position: relative; font-size: 12px; line-height: 32px; height: 32px; margin-left: 10px; cursor: pointer; } div.timenode .time p { font-weight: bold; margin: 0 0 0 24px; } div.timenode .body:empty { display: none; } div.timenode .body { margin: 0 0 16px; } div.timenode .body > *:first-child { margin: 0.25em 0 0.8rem 0; } div.timenode .body > *:last-child { margin-bottom: 0.25em; } div.timenode .body .highlight { border: 1px solid #e4e4e4; } div.timenode time::after { content: attr(data-msg); box-sizing: border-box; width: 0; height: 0; position: absolute; left: 0; overflow: hidden; } div.timenode time:hover:after { background: var(--bb-TimeTitle-bg); color: var(--bb-TimeTitle); left: calc(50% - 70px); top: 36px; padding: 8px 10px; border-radius: 6px; width: 180px; height: auto; opacity: 0.9; line-height: 1.2; text-align: center; font-size: 14px; font-weight: 500; transition: opacity 1s; z-index: 10; }
div.timenode time::before { content: ""; box-sizing: border-box; position: absolute; opacity: 0; } div.timenode time:hover:before { top: 24px; left: calc(50% - 4px); text-align: center; width: 8px; height: 6px; border: 6px dashed var(--bb-TimeTitle-bg); border-color: transparent transparent var(--bb-TimeTitle-bg) transparent; opacity: 0.9;
} div.timenode .body { margin: 0 15px; padding: 0 16px 16px 16px; border-radius: 8px;
display: block; }
#bbtalk-media .aplayer.aplayer-withlrc.aplayer-withlist { position: relative; margin: 0; padding: 0; border: none; background-color: transparent; font-size: 14px; line-height: 1.5; color: #333; }
#bbtalk-media .aplayer.aplayer-withlrc.aplayer-withlist .aplayer-body { display: flex; flex-direction: row; align-items: center; justify-content: flex-start; width: 100%; height: 40px; margin: 0; padding: 0; border-radius: 20px; background-color: #f5f5f5; cursor: pointer; }
#bbtalk-media .aplayer.aplayer-withlrc.aplayer-withlist .aplayer-body:hover { background-color: #e6e6e6; }
#bbtalk-media .aplayer.aplayer-withlrc.aplayer-withlist .aplayer-info { display: flex; flex-direction: row; align-items: center; justify-content: flex-start; width: 100%; height: 100%; margin: 0; padding: 0 12px; border-radius: 20px; background-color: #f5f5f5; }
#bbtalk-media .aplayer.aplayer-withlrc.aplayer-withlist .aplayer-info:hover { background-color: #e6e6e6; }
#bbtalk-media .aplayer.aplayer-withlrc.aplayer-withlist .aplayer-icon { display: flex; flex-direction: row; align-items: center; justify-content: center; width: 40px; height: 40px; margin: 0; padding: 0; border-radius: 20px; background-color: #4cd964; color: #fff; font-size: 18px; }
#bbtalk-media .aplayer.aplayer-withlrc.aplayer-withlist .aplayer-icon:hover { background-color: #44c662; }
#bbtalk-media .aplayer.aplayer-withlrc.aplayer-withlist .aplayer-icon i { margin: 0; padding: 0; }
#bbtalk-media .aplayer.aplayer-withlrc.aplayer-withlist .aplayer-name { display: flex; flex-direction: row; align-items: center; justify-content: flex-start; width: calc(100% - 40px); height: 100%; margin: 0; padding: 0 12px; border-radius: 20px; background-color: #f5f5f5; font-size: 14px; font-weight: bold; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#bbtalk-media .aplayer.aplayer-withlrc.aplayer-withlist .aplayer-author { display: flex; flex-direction: row; align-items: center; justify-content: flex-start; width: calc(100% - 40px); height: 100%; margin: 0; padding: 0 12px; border-radius: 20px; background-color: #f5f5f5; font-size: 12px; color: #999; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bbtalk-url { position: relative; display: inline-block; width: 100%; max-width: 600px; margin: 10px 0; padding: 20px; border-radius: 10px; background-color: var(--bbtalk-url-bg); box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.2); text-decoration: auto !important; }
.bbtalk-url-info { position: absolute; top: 20px; left: 20px; width: 80px; height: 80px; background-size: cover; border-radius: 10px; }
.bbtalk-url-info i { font-size: 4rem; color: #5d91a366; }
.bbtalk-url-title { font-size: 18px; font-weight: bold; margin: 0 0 10px 110px; line-height: 1.4rem; }
.bbtalk-url-desc { font-size: 14px; color: #999; margin: 0 0 10px 110px; line-height: 1.2rem; }
@media screen and (max-width: 768px) { .bbtalk-url-info { opacity: 0.6; bottom: 20px; right: 20px; top: unset; left: unset; } .bbtalk-url-title, .bbtalk-url-desc { margin: 0 0 10px 0px; } }
#bbtalk-content .leaflet-fade-anim .leaflet-map-pane .leaflet-popup { bottom: -30px !important; left: -86px !important; }
#bbtalk-content .map-box { margin: 0.8rem 0 1.2rem 0; }
#bbtalk-content video { margin-top: 0.8rem; max-height: 16rem; }
div.timenode .card-name { display: flex; align-items: center; margin: 15px 5px 15px 15px; } div.timenode .card-name .is-badge { height: 20px; width: 20px; margin-left: 8px; } div.timenode .card-name .avatar { width: 32px; height: 32px; border-radius: 50%; margin-right: 10px; } div.timenode .card-name .avatar-img { width: 100%; height: unset; border-radius: 50%; } div.timenode .bbtalk-from { color: var(--bbtalk-from-color); font-weight: 600; border-radius: 4px; padding: 0 8px 2px 2px; width:fit-content; width:-webkit-fit-content; width:-moz-fit-content; font-size: 12px; } div.timenode .loading { text-align: center; } @keyframes Gradient { 0% { background-position: 0 50%; }
50% { background-position: 100% 50%; }
to { background-position: 0 50%; } } button.next { cursor: pointer; color: #fff; border: 0; margin: 2rem auto 3rem auto; border-radius: 0.3125rem; display: none; padding: 0 1rem; height: 40px; font-weight: 500; text-align: center; transition: all 0.5s ease-out; background: -webkit-linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab); background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab); background-size: 1000% 1000%; animation: Gradient 10s linear infinite; outline: 0; box-shadow: 0 0px 6px 1px rgb(7 17 27 / 15%); } button.next:hover { box-shadow: 0 0px 6px 2px rgb(7 17 27 / 15%); } #bbtalk .load-ctn a{padding:8px 18px;background:#eaeded;border-radius:6px;color:#333} #bbtalk .load-ctn {padding-top:30px;cursor:pointer;} blockquote { background: var(--guole-secondbg); border: 1px solid var(--guole-card-border); box-shadow: none; margin: 0; margin-bottom: 0.5rem; font-size: 0.6rem; color: var(--guole-secondtext); border-radius: 8px; padding: 0.5rem 0.8rem; } blockquote p { margin: 0 !important; } #bbtalk .bbtalk-bottom { display: flex; justify-content: space-between; width: 100%; -webkit-user-select: none; } #bbtalk a.bbtalk-reply { line-height: initial; align-items: center; display: flex; cursor: pointer; position: relative; } #bbtalk a.bbtalk-reply:hover { text-decoration: unset; } #bbtalk a.bbtalk-reply i { font-size: 1.3rem; font-weight: bold; color: var(--bbtalk-reply) } #bbtalk a.bbtalk-reply:hover i { color: var(--guole-main) } #bbtalk a.bbtalk-reply::before { content: ""; box-sizing: border-box; position: absolute; opacity: 0; } #bbtalk a.bbtalk-reply:hover:before { bottom: 24px; left: 4px; text-align: center; width: 8px; height: 6px; border: 6px dashed var(--bb-TimeTitle-bg); border-color: transparent transparent var(--bb-TimeTitle-bg) transparent; opacity: 0.9; transform: rotate(180deg); } #bbtalk a.bbtalk-reply::after { content: "引用回复"; box-sizing: border-box; width: 0; height: 0; position: absolute; left: 0; overflow: hidden; height: calc(100% - 26px - 16px); border-bottom-left-radius: 2px; border-bottom-right-radius: 2px; } #bbtalk a.bbtalk-reply:hover:after { background: var(--bb-TimeTitle-bg); color: var(--bb-TimeTitle); left: -42px; bottom: 36px; padding: 8px 10px; border-radius: 6px; width: 100px; height: auto; opacity: 0.9; line-height: 1.2; text-align: center; font-size: 14px; font-weight: 500; transition: opacity 1s; z-index: 10; position: absolute; }
|
引入的 timeago.js 内容如下,放到 source/js/
目录下(其他位置也可,记得修改 md 中地址):
1
| !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e=e||self).timeago={})}(this,function(e){"use strict";var r=["second","minute","hour","day","week","month","year"];var a=["秒","分钟","小时","天","周","个月","年"];function t(e,t){n[e]=t}function i(e){return n[e]||n.en_US}var n={},f=[60,60,24,7,365/7/12,12];function o(e){return e instanceof Date?e:!isNaN(e)||/^\d+$/.test(e)?new Date(parseInt(e)):(e=(e||"").trim().replace(/\.\d+/,"").replace(/-/,"/").replace(/-/,"/").replace(/(\d)T(\d)/,"$1 $2").replace(/Z/," UTC").replace(/([+-]\d\d):?(\d\d)/," $1$2"),new Date(e))}function d(e,t){for(var n=e<0?1:0,r=e=Math.abs(e),a=0;e>=f[a]&&a<f.length;a++)e/=f[a];return(0===(a*=2)?9:1)<(e=Math.floor(e))&&(a+=1),t(e,a,r)[n].replace("%s",e.toString())}function l(e,t){return((t?o(t):new Date)-o(e))/1e3}var s="timeago-id";function h(e){return parseInt(e.getAttribute(s))}var p={},v=function(e){clearTimeout(e),delete p[e]};function m(e,t,n,r){v(h(e));var a=r.relativeDate,i=r.minInterval,o=l(t,a);e.innerText=d(o,n);var u,c=setTimeout(function(){m(e,t,n,r)},Math.min(1e3*Math.max(function(e){for(var t=1,n=0,r=Math.abs(e);e>=f[n]&&n<f.length;n++)e/=f[n],t*=f[n];return r=(r%=t)?t-r:t,Math.ceil(r)}(o),i||1),2147483647));p[c]=0,u=c,e.setAttribute(s,u)}t("en_US",function(e,t){if(0===t)return["just now","right now"];var n=r[Math.floor(t/2)];return 1<e&&(n+="s"),[e+" "+n+" ago","in "+e+" "+n]}),t("zh_CN",function(e,t){if(0===t)return["刚刚","片刻后"];var n=a[~~(t/2)];return[e+" "+n+"前",e+" "+n+"后"]}),e.cancel=function(e){e?v(h(e)):Object.keys(p).forEach(v)},e.format=function(e,t,n){return d(l(e,n&&n.relativeDate),i(t))},e.register=t,e.render=function(e,t,n){var r=e.length?e:[e];return r.forEach(function(e){m(e,e.getAttribute("datetime"),i(t),n||{})}),r},Object.defineProperty(e,"__esModule",{value:!0})});
|
最后,md 文件引用的 BBtalk.js
代码如下,放到 source/js/
目录下(其他位置也可,记得修改 md 中地址):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
| var bbtalk = { limit: 12, bbtalk_name: '夜的第八章', bbtalk_avatar: '/img/gl.jpg', bbtalk_avatar_svg: '<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" class="is-badge"><path d="m512 268c0 17.9-4.3 34.5-12.9 49.7s-20.1 27.1-34.6 35.4c.4 2.7.6 6.9.6 12.6 0 27.1-9.1 50.1-27.1 69.1-18.1 19.1-39.9 28.6-65.4 28.6-11.4 0-22.3-2.1-32.6-6.3-8 16.4-19.5 29.6-34.6 39.7-15 10.2-31.5 15.2-49.4 15.2-18.3 0-34.9-4.9-49.7-14.9-14.9-9.9-26.3-23.2-34.3-40-10.3 4.2-21.1 6.3-32.6 6.3-25.5 0-47.4-9.5-65.7-28.6-18.3-19-27.4-42.1-27.4-69.1 0-3 .4-7.2 1.1-12.6-14.5-8.4-26-20.2-34.6-35.4-8.5-15.2-12.8-31.8-12.8-49.7 0-19 4.8-36.5 14.3-52.3s22.3-27.5 38.3-35.1c-4.2-11.4-6.3-22.9-6.3-34.3 0-27 9.1-50.1 27.4-69.1s40.2-28.6 65.7-28.6c11.4 0 22.3 2.1 32.6 6.3 8-16.4 19.5-29.6 34.6-39.7 15-10.1 31.5-15.2 49.4-15.2s34.4 5.1 49.4 15.1c15 10.1 26.6 23.3 34.6 39.7 10.3-4.2 21.1-6.3 32.6-6.3 25.5 0 47.3 9.5 65.4 28.6s27.1 42.1 27.1 69.1c0 12.6-1.9 24-5.7 34.3 16 7.6 28.8 19.3 38.3 35.1 9.5 15.9 14.3 33.4 14.3 52.4zm-266.9 77.1 105.7-158.3c2.7-4.2 3.5-8.8 2.6-13.7-1-4.9-3.5-8.8-7.7-11.4-4.2-2.7-8.8-3.6-13.7-2.9-5 .8-9 3.2-12 7.4l-93.1 140-42.9-42.8c-3.8-3.8-8.2-5.6-13.1-5.4-5 .2-9.3 2-13.1 5.4-3.4 3.4-5.1 7.7-5.1 12.9 0 5.1 1.7 9.4 5.1 12.9l58.9 58.9 2.9 2.3c3.4 2.3 6.9 3.4 10.3 3.4 6.7-.1 11.8-2.9 15.2-8.7z" fill="#1da1f2"></path></svg>', aplayer_js: '//unpkg.com/aplayer@1.10.1/dist/APlayer.min.js', init: function (options) { this.bbtalk_name = options.bbtalk_name || this.bbtalk_name; this.bbtalk_avatar = options.bbtalk_avatar || this.bbtalk_avatar; this.bbtalk_avatar_svg = options.bbtalk_avatar_svg || this.bbtalk_avatar_svg; this.aplayer_js = options.aplayer_js || this.aplayer_js; this.leaflet_css = options.leaflet_css || this.leaflet_css; this.leaflet_js = options.leaflet_js || this.leaflet_js; this.ChineseTmsProviders_js = options.ChineseTmsProviders_js || this.ChineseTmsProviders_js; } };
let bbtalkPage = 1; let bbtalkIsLoading = false;
function BBtalk_init() { const loadMoreBtn = document.getElementById('load-more-btn'); const loading = document.getElementById('loading'); const bbTalkBottomTip = document.getElementById('bbtalk-bottom-tip');
if (!loading || !loadMoreBtn || bbtalkIsLoading) return;
bbtalkIsLoading = true; loading.style.display = 'block'; loadMoreBtn.style.display = 'none';
const timestamp = Math.floor(Date.now() / 1000 / 60); const jsonUrl = getJsonUrl(bbtalkPage, timestamp);
fetchData(jsonUrl) .then(data => { renderData(data); const info = document.querySelector('p.info'); if (info) info.innerHTML = `共计发送 ${data.count} 条闪念`; bbtalkPage++; bbtalkIsLoading = false; loading.style.display = 'none'; loadMoreBtn.style.display = 'block'; loadMoreBtn.addEventListener("click", BBtalk_init); bbTalkBottomTip.style.display = 'none'; if (data.results.length < bbtalk.limit) { loadMoreBtn.style.display = 'none'; loading.style.display = 'none'; bbTalkBottomTip.style.display = 'block'; } }) .catch(error => { console.error(error); bbtalkIsLoading = false; loading.style.display = 'none'; loadMoreBtn.style.display = 'block'; }); }
function getJsonUrl(page, timestamp) { return location.host === 'blog.guole.fun' ? `https://cdn.guole.fun/json/bb-json/bbtalk_page${page}.json?t=${timestamp}` : `/test/bbtalk_page${page}.json?t=${timestamp}`; }
async function fetchData(url) { const response = await fetch(url); if (!response.ok) { throw new Error(`获取数据失败: ${response.status} - ${response.statusText}`); } return response.json(); }
function renderData(data) { data.results.forEach(item => { const timenode = createTimenode(item); const bbtalk = document.getElementById('bbtalk'); if (bbtalk) bbtalk.appendChild(timenode);
if (item.MsgType === 'location') { const assets = document.createElement('script'); assets.innerHTML = item.other; document.body.appendChild(assets); } else if (item.MsgType === 'music') { renderMusicAPlayer(item); } }); }
function createTimenode(item) { const timenode = document.createElement('div'); let time = formatTime(item.createdAt); let time_ago = timeago.format(time, 'zh_CN'); let content = urlToLink(item.content, true); let quote = urlToLink(item.content, item.MsgType, false); let fromColor; switch (true) { case item.from.includes('Chrome'): fromColor = 'var(--bbtalk-from-chrome)'; break; case item.from.includes('WeChat'): fromColor = 'var(--bbtalk-from-wechat)'; break; case item.from.includes('Postman'): fromColor = 'var(--bbtalk-from-postman)'; break; case item.from.includes('Edge'): fromColor = 'var(--bbtalk-from-edge)'; break; case item.from.includes('Siri'): fromColor = 'var(--bbtalk-from-siri)'; break; default: fromColor = 'var(--bbtalk-from-bg)'; break; } timenode.innerHTML = ` <div class="card-name"> <div class="avatar"> <img src=${bbtalk.bbtalk_avatar} class="avatar-img no-lightbox nolazyload"> </div> <div class="name">${bbtalk.bbtalk_name}</div> ${bbtalk.bbtalk_avatar_svg} <div class="time" style="margin-left:18px;"> <p> <time datetime="${time}" data-msg="${time}">${time_ago}</time> </p> </div> </div> <div class="body"> <div class="bbtalk-content"> ${content} <div class="bbtalk-media" id="${item.objectId}"></div> </div> <div class="bbtalk-bottom"> <div class="bbtalk-from" style="background-color: ${fromColor};color: var(--bbtalk-from-color)"> <span>${item.from}</span> </div> <a id="bbtalk-reply" class="bbtalk-reply" onclick="quoteComment(\`${quote}\`)"> <i class="iconfont icon-xiaoxi4"></i> </a> </div> </div> `; timenode.classList.add('timenode', 'wow', 'animate__zoomIn'); return timenode; }
function renderMusicAPlayer(item) { let lrc = `${item.other["lrc"]}`; let lrcType = 3; if (!lrc) { lrcType = 1; lrc = "[00:00.00] 暂无歌词"; } const aplayerCode = ` const ap_${item.objectId} = new APlayer({ container: document.getElementById('${item.objectId}'), lrcType: ${lrcType}, audio: [{ name: '${item.other["name"]}', artist: '${item.other["artist"]}', url: '${item.other["url"]}', cover: '${item.other["cover"]}', lrc: '${lrc}', }] }); ` const assets = document.createElement('script'); assets.innerHTML = aplayerCode; document.getElementById(`${item.objectId}`).appendChild(assets); }
function formatTime(time) { const timestamp = Date.parse(time); if (isNaN(timestamp)) return ''; const date = new Date(timestamp); const year = date.getFullYear(); const month = padStart(date.getMonth() + 1); const day = padStart(date.getDate()); const hour = padStart(date.getHours()); const minute = padStart(date.getMinutes()); const second = padStart(date.getSeconds()); return `${year}-${month}-${day} ${hour}:${minute}:${second}`; }
function padStart(number) { return number.toString().padStart(2, '0'); }
function urlToLink(str, msgType = 'text', convertToHtml = true) { const re = /(?<!<img\s+[^>]*\bsrc=["'])(?<!<video\s+[^>]*\bsrc=["'])(?<!\.(png|jpe?g|webp|gif|amr|speex|mp4|ts|flv)(\?.*)?$)\bhttps?:\/\/\S+\b/g; const re_forpic = /\bhttps?:[^:<>"]*\/([^:<>"]*)(\.(jpeg)|(png)|(jpg)|(webp)|(gif))/ig; const music_type = /(&songmid=)|(songid=)|(songDetail)|(\/m\/song\?)|(cdn\.guole\.fun\/mp3\/)/g; const reVideo = /\bhttps?:[^:<>"]*\/([^:<>"]*)(\.(mp4)|(ts)|(flv))/ig; const reMap = /(<div[^>]*>(.*?)<\/div>|<\/div>)/is; const lineBreak = /<br>/g; const html = /<[^>]+>/g; const tencet = /y\.qq\.com/g; const netease = /y\.music\.163\.com/g; const urlRegex = /((http?s):\/\/[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|])/g; let type, server, song_id;
if (convertToHtml) { str = str.replace(re_forpic, url => `<a href="${url}" target="_blank" data-fancybox="group" class="fancybox"><img src="${url}" data-lazy-src="${url}"></a>`) .replace(reVideo, url => `<video src="${url}" controls></video>`) if (tencet.test(str) || netease.test(str)) { let url = str.match(urlRegex)[0]; url = new URL(str); const urlPath = url.pathname; const urlParams = new URLSearchParams(url.search); type = urlPath.includes("playlist") || urlPath.includes("details") ? "playlist" : "song"; server = url.host === "i.y.qq.com" ? "tencent" : "netease"; song_id = urlParams.get("songid") || urlParams.get("songmid") || urlParams.get("id"); str = `<meting-js server="${server}" type="${type}" id="${song_id}"></meting-js>`; } if (window.qqWechatEmotionParser) { str = qqWechatEmotionParser(str); } } else { if (music_type.test(str)) { str = str.replace(re, " [音乐]"); if (msgType === "music") str = str + " [音乐]" ; } else { str = str.replace(re_forpic, " [图片]") .replace(reVideo, " [视频]") .replace(reMap, " [位置]") .replace(re, " [外部链接]") .replace(lineBreak, "") .replace(html, "") } } return str; }
function quoteComment(txt) { let input = document.getElementsByClassName('el-textarea__inner')[0]; let evt = new Event('input', { bubbles: true, cancelable: true }); let inputValue = replaceAll(txt, '\n', '\n> ') input.value = '> ' + inputValue + '\n\n'; input.dispatchEvent(evt); let domTop = document.querySelector("#post-comment").offsetTop; window.scrollTo(0, domTop - 80); input.focus(); input.setSelectionRange(-1, -1); if (document.getElementById("comment-tips")) { document.getElementById("comment-tips").style.display = "flex"; } }
document.addEventListener("pjax:complete", (function () { bbtalkPage = 1; BBtalk_init(); }));
BBtalk_init();
|
首页轮播说说
自定义或放到其他能全局引入的 js 文件里都可以,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
|
var pathInfo = window.location.pathname; function bbtalkBanner() { let talkList = document.getElementById('bbtalk-list'); let bbtalkLoading = document.getElementById('bbtalk-loading'); let bbtalkRight = document.getElementById('bbtalk-right'); if (pathInfo === '/') { if (talkList) { const timestamp = Math.floor(Date.now() / 1000 / 60); let jsonUrl; if (location.host === 'blog.guole.fun') { jsonUrl = `https://cdn.guole.fun/json/bb-json/bbtalk_page1.json?t=${timestamp}`; } else { jsonUrl = `/test/bbtalk_page1.json?t=${timestamp}` } let xhr = new XMLHttpRequest(); xhr.open('GET', jsonUrl, true); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { let res = JSON.parse(xhr.responseText); let bberHtml = ''; res.results.forEach(function (item, i) { const d = new Date(item.createdAt); const date = d.getFullYear() + '/' + (d.getMonth() + 1) + '/' + d.getDate() + ' ' + d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds(); const dataTime = timeago.format(date, 'zh_CN'); const newdataTime = '<span class="datatime">' + dataTime + '</span>'; bberHtml += '<li class="bbtalk-list item-' + (i + 1) + '"><a href="/bb" style="color: var(--font-color);/*font-weight: normal;*/">' + newdataTime + ': ' + urlToLink(item.content,item.MsgType) + '</a></li>'; }); bbtalkLoading.style.display = 'none'; if (bbtalkLoading) bbtalkLoading.remove(); bbtalkRight.style.display = 'unset'; talkList.insertAdjacentHTML('beforeend', bberHtml); } }; xhr.send();
function urlToLink(str,MsgType) { const imgTag = /(<br>)|([[\s\S]*])|(\()|(\))/g; const reForImg = /\<[img|IMG].*?src=[\'|\"](https\:\/\/.*?(?:[\.jpg|\.jpeg|\.png|\.gif|\.bmp]))[\'|\"].*?[\/]?>/g; const music = /(y.qq.com)|(music.163.com)/g; const musicUrl = /[a-zA-z]+:\/\/[^\s]*/g;
if (music.test(str)) { str = str.replace(musicUrl, function () { return '<svg viewBox="0 0 1024 1024" width="16" height="16"><path d="M960 0H1024v736c0 88.3712-100.283733 160-224 160S576 824.3712 576 736s100.283733-160 224-160c62.685867 0 119.3472 18.397867 160 48.042667V256l-512 113.783467v494.216533c0 88.3712-100.283733 160-224 160S0 952.3712 0 864s100.283733-160 224-160c62.685867 0 119.3472 18.397867 160 48.042667V128L960 0z"></path></svg>'; }); } else { if (MsgType === "music") { str = str + '<svg viewBox="0 0 1024 1024" width="16" height="16"><path d="M960 0H1024v736c0 88.3712-100.283733 160-224 160S576 824.3712 576 736s100.283733-160 224-160c62.685867 0 119.3472 18.397867 160 48.042667V256l-512 113.783467v494.216533c0 88.3712-100.283733 160-224 160S0 952.3712 0 864s100.283733-160 224-160c62.685867 0 119.3472 18.397867 160 48.042667V128L960 0z"></path></svg>'; return str; } }
str = str.replace(reForImg, '$1');
const reForMd = /^!\[(.*)\]\((.*)\)/g; str = str.replace(reForMd, '$2'); str = str.replace(imgTag, '');
const re = /\bhttps?:\/\/(?!\S+(?:jpe?g|png|bmp|gif|webp|gif|mp4))\S+/is; const reForPic = /\bhttps?:\/\/.*?(\.gif|\.jpe?g|\.png|\.bmp|\.webp)/is; const reImg = /<img.*?src="(.*?)".*?>/is; const reA = /<a.*?href="(.*?)".*?>.*?<\/a>/is; const reVideo = /\bhttps?:[^:<>"]*\/([^:<>"]*)(\.(mp4)|(ts)|(flv))/ig; const reMap = /(<div[^>]*>(.*?)<\/div>|<\/div>)/is; let count = 0; str = str.replace(reImg, function () { return '<svg viewBox="0 0 1024 1024" width="21" height="21"><path d="M821.6 120.93333333H195.4c-74.1 0-134.2 60.1-134.2 134.2v492c0 74.1 60.1 134.2 134.2 134.2h626.2c74.1 0 134.2-60.1 134.2-134.2v-492c0-74.1-60.1-134.2-134.2-134.2zM251.3 255.13333333c30.9 0 55.9 25 55.9 55.9s-25 55.9-55.9 55.9-55.9-25-55.9-55.9 25-55.9 55.9-55.9z m614.6 559.1H153.3c-37.3 0-58.2-43.1-35.1-72.4L302.1 508.33333333c17.9-22.7 52.4-22.7 70.3 0l76.5 97.2 148.6-260c17.2-30.1 60.5-30.1 77.7 0L904.8 747.33333333c17 29.8-4.5 66.9-38.9 66.9z"></path></svg>'; });
str = str.replace(reForPic, function () { return '<svg viewBox="0 0 1024 1024" width="21" height="21"><path d="M821.6 120.93333333H195.4c-74.1 0-134.2 60.1-134.2 134.2v492c0 74.1 60.1 134.2 134.2 134.2h626.2c74.1 0 134.2-60.1 134.2-134.2v-492c0-74.1-60.1-134.2-134.2-134.2zM251.3 255.13333333c30.9 0 55.9 25 55.9 55.9s-25 55.9-55.9 55.9-55.9-25-55.9-55.9 25-55.9 55.9-55.9z m614.6 559.1H153.3c-37.3 0-58.2-43.1-35.1-72.4L302.1 508.33333333c17.9-22.7 52.4-22.7 70.3 0l76.5 97.2 148.6-260c17.2-30.1 60.5-30.1 77.7 0L904.8 747.33333333c17 29.8-4.5 66.9-38.9 66.9z"></path></svg>'; });
str = str.replace(reVideo, function () { return '<svg viewBox="0 0 1025 1024" width="21" height="21"><path d="M892.8 128H131.2c-19.2 0-35.2 6.4-48 22.4-12.8 12.8-19.2 32-19.2 54.4V816c0 44.8 28.8 80 67.2 80h761.6c19.2 0 35.2-6.4 48-22.4s19.2-35.2 19.2-54.4V204.8c0-41.6-32-76.8-67.2-76.8zM224 832h-51.2c-25.6 0-44.8-22.4-44.8-51.2V640h96v192z m0-256h-96V448h96v128z m0-192h-96V243.2c0-28.8 19.2-51.2 44.8-51.2H224v192z m512 32v416H288V192h448v224z m160 364.8c0 28.8-19.2 51.2-44.8 51.2H800V640h96v140.8z m0-204.8h-96V448h96v128z m0-192h-96V192h54.4c22.4 0 41.6 22.4 41.6 51.2V384z" fill="#333333" p-id="4200"></path><path d="M444.8 627.2l144-73.6 25.6-12.8c32-16 32-41.6 0-57.6l-169.6-86.4c-16-6.4-28.8 0-28.8 16v192c0 22.4 12.8 28.8 28.8 22.4z" fill="#333333" p-id="4201"></path></svg>'; });
str = str.replace(reA, function () { return '<svg viewBox="0 0 1025 1024" width="21" height="21"><path d="M333.06186 733.061768c-58.347896 52.210106-97.040127 49.051159-136.467091 9.492188l-45.156456-48.462758c-39.427988-39.541575-39.427988-103.667058 0-143.226029l193.260585-193.848986c39.426965-39.558971 103.355973-39.558971 142.78396 0l35.679617 35.794228c30.457686 30.555923 37.398772 75.762521 20.801768 112.997564l86.286202 66.040089c59.149145-59.33027 59.149145-155.517983 0-214.830857L523.162476 249.600755c-59.133795-59.33027-155.010423-59.33027-214.160591 0L44.350342 515.071965c-59.133795 59.313897-59.133795 155.50161 0 214.830857l107.08797 107.415428c59.133795 59.313897 155.026796 59.313897 214.176964 0l102.161774-105.647155-72.980151-70.034053L333.06186 733.061768zM987.196021 285.394982 880.1234 177.979554c-59.149145-59.33027-155.026796-59.33027-214.176964 0 0 0 4.223185-1.064238-57.988716 61.343113l71.113641 68.167542 31.604812-34.877345c39.427988-39.541575 103.356996-39.541575 142.78396 0l35.69599 35.8106c39.427988 39.541575 39.427988 103.667058 0 143.226029L714.818517 632.847345c-39.427988 39.541575-103.340623 39.541575-142.768611 0l-29.395494-48.462758c-61.883419-46.25344-42.865273-57.317427-37.611619-88.544639L426.548044 418.130076c-59.150168 59.33027-59.150168 155.517983 0 214.830857l107.072621 107.432825c59.149145 59.312874 155.026796 59.312874 214.176964 0l239.398392-240.166895C1071.582967 402.924769 987.196021 285.394982 987.196021 285.394982z"></path></svg>'; });
str = str.replace(re, function () { return '<svg viewBox="0 0 1025 1024" width="21" height="21"><path d="M333.06186 733.061768c-58.347896 52.210106-97.040127 49.051159-136.467091 9.492188l-45.156456-48.462758c-39.427988-39.541575-39.427988-103.667058 0-143.226029l193.260585-193.848986c39.426965-39.558971 103.355973-39.558971 142.78396 0l35.679617 35.794228c30.457686 30.555923 37.398772 75.762521 20.801768 112.997564l86.286202 66.040089c59.149145-59.33027 59.149145-155.517983 0-214.830857L523.162476 249.600755c-59.133795-59.33027-155.010423-59.33027-214.160591 0L44.350342 515.071965c-59.133795 59.313897-59.133795 155.50161 0 214.830857l107.08797 107.415428c59.133795 59.313897 155.026796 59.313897 214.176964 0l102.161774-105.647155-72.980151-70.034053L333.06186 733.061768zM987.196021 285.394982 880.1234 177.979554c-59.149145-59.33027-155.026796-59.33027-214.176964 0 0 0 4.223185-1.064238-57.988716 61.343113l71.113641 68.167542 31.604812-34.877345c39.427988-39.541575 103.356996-39.541575 142.78396 0l35.69599 35.8106c39.427988 39.541575 39.427988 103.667058 0 143.226029L714.818517 632.847345c-39.427988 39.541575-103.340623 39.541575-142.768611 0l-29.395494-48.462758c-61.883419-46.25344-42.865273-57.317427-37.611619-88.544639L426.548044 418.130076c-59.150168 59.33027-59.150168 155.517983 0 214.830857l107.072621 107.432825c59.149145 59.312874 155.026796 59.312874 214.176964 0l239.398392-240.166895C1071.582967 402.924769 987.196021 285.394982 987.196021 285.394982z"></path></svg>'; });
while (reMap.test(str)) { if (count === 0) { str = str.replace(reMap, function () { return '<svg viewBox="0 0 1025 1024" width="21" height="21"><path d="M512 85.333333a233.386667 233.386667 0 0 0-234.666667 232.746667c0 85.333333 69.973333 185.6 207.573334 308.053333a40.96 40.96 0 0 0 54.186666 0C676.693333 503.68 746.666667 402.56 746.666667 318.08A233.386667 233.386667 0 0 0 512 85.333333z m0 448c-100.053333-91.306667-149.333333-164.266667-149.333333-215.253333a149.333333 149.333333 0 0 1 298.666666 0c0 51.626667-49.066667 124.586667-149.333333 215.253333z" p-id="9700"></path><path d="M933.546667 788.053333l1.92-3.84 1.28-3.84c0-1.28 0-2.56 1.066666-4.053333a33.066667 33.066667 0 0 0 0-6.613333V490.666667a42.666667 42.666667 0 0 0-85.333333 0v249.386666l-251.52 108.16-258.56-159.146666h-1.493333a14.08 14.08 0 0 0-4.053334-1.92l-3.84-1.706667h-24.32l-3.626666 1.28a15.36 15.36 0 0 0-4.266667 1.92h-1.493333L170.666667 759.68V490.666667a42.666667 42.666667 0 0 0-85.333334 0v343.466666a38.4 38.4 0 0 0 0 4.693334 24.746667 24.746667 0 0 0 0 3.84l1.28 4.053333a35.626667 35.626667 0 0 0 1.706667 4.266667 7.68 7.68 0 0 1 0 1.92 36.693333 36.693333 0 0 0 5.973333 7.893333 44.16 44.16 0 0 0 12.373334 7.466667l2.346666 1.493333a42.666667 42.666667 0 0 0 7.253334 2.773333h2.56A39.253333 39.253333 0 0 0 128 874.666667a49.066667 49.066667 0 0 0 9.6 0h2.56a38.186667 38.186667 0 0 0 6.826667-2.773334h1.493333l170.666667-94.506666 256 157.013333c1.92 0 3.84 1.493333 5.546666 2.346667a26.026667 26.026667 0 0 0 5.12 2.133333 42.666667 42.666667 0 0 0 10.453334 1.706667H597.333333a49.493333 49.493333 0 0 0 9.6-1.28l2.986667-1.066667 4.266667-1.28 298.666666-128a40.96 40.96 0 0 0 6.613334-3.84l2.986666-2.346667 3.2-2.773333a39.04 39.04 0 0 0 2.773334-3.413333l2.56-3.2z" p-id="9701"></path><path d="M512 320m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z" p-id="9702"></path></svg>'; }); } else { str = str.replace(reMap, ""); } count++; } str = str.replace(/\s+</g, '<'); return str; } } } }
function bbtalkRoll() { const bbtalkLi = document.querySelectorAll('li.bbtalk-list'); if (bbtalkLi.length >= 2) { const curLi = bbtalkLi[0]; const lastLi = bbtalkLi[bbtalkLi.length - 1]; lastLi.parentNode.insertBefore(curLi, lastLi.nextSibling); } }
setInterval(bbtalkRoll, 3000);
document.addEventListener("pjax:complete", (function () { pathInfo = window.location.pathname; bbtalkBanner(); console.log('触发 pjax:complete 事件。') }))
bbtalkBanner();
|
在theme/Butterfly/layout/index.pug
中加入以下内容,icon 我用了阿里的,你可以自己换。
1 2 3 4 5 6 7 8 9 10 11 12
| extends includes/layout.pug
block content include ./includes/mixins/post-ui.pug #recent-posts.recent-posts #bber-talk(style='display: flex; ') a.index-talk-left(style="margin-right: 10px;cursor: pointer;" href='/bb/' title='闪念') i.iconfont.icon-xiaoxi4(style='font-size: 1.2rem; color: var(--guole-font-color);') ul.talk-list(style='margin: 0; padding: 0; height: 36px; overflow: hidden; margin-bottom: -4px; transition: all 1s ease-in-out;') li.item.loading= _p('闪念加载中…') a.index-talk-right(style='text-align: right; position: relative; cursor: pointer; display:none' href='/bb/' title='查看全文') i.iconfont.icon-you(style="margin-left: 5px; font-size: 1.2rem; color: var(--font-color);")
|
css 样式随便找个地方丢进去,全局引用即可。有不对的,在本站再扒一下,太久远了 很难翻。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| #bber-talk { color: var(--guole-font-color); padding: 0.4rem 1rem 0.4rem 0.7rem; cursor: auto; display: -webkit-flex; display: flex; -webkit-align-items: center; align-items: center; overflow: hidden; transition: all 0.3s; z-index: 10; position: relative; animation: slide-in 0.6s 0.4s backwards; will-change: auto; border: 1px solid #e3e8f7; border-radius: 8px; position: -webkit-sticky; position: sticky; }
@media screen and (min-width: 768px) { #bber-talk:hover, .recent-post-item:hover { box-shadow: var(--guole-shadow-main) !important; } .card-widget,.card-widget:hover { box-shadow: var(--guole-shadow-border) !important; } } @media screen and (min-width: 1300px) { #bber-talk { margin-bottom: 1rem; animation: slide-in 0.6s 0s backwards; } }
#bber-talk:not(a) { font-weight: bold; }
#bber-talk svg { fill: currentColor; vertical-align: middle; display: inline; margin-left: 5px; margin-right: 5px; margin-bottom: 2px; }
@media (any-hover: hover) { #bber-talk a.index-talk-left:hover i, #bber-talk a.index-talk-right:hover i { color: var(--guole-theme-color) !important; transition: all 0.3s; } #bber-talk .talk-list:hover { color: var(--guole-theme-color) !important; transition: all .15s ease-in-out; } }
#bber-talk .talk-list li.item a { cursor: pointer; font-size: 16px; }
#bber-talk .talk-list li { list-style: none; width: 100%; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; }
|
大功告成
到这里,应该七七八八了。自己关注上面的微信号,发送以下指令绑定。Binding_Key
是云函数配置的环境变量。
假如我的Binding_Key
环境变量中配置为123456
(实际使用时,尽量设置复杂点啊……比如 32 位随机字符串),那么绑定的命令就是:/b bb,123456
云函数代码中,我用了一些正则,如果换成你自己的,自行检查更新下正则内容……我大部分都是匹配 cdn.guole.fun
这个域名,处理一些逻辑的。上传图片 / 视频的逻辑,已抽取成环境变量,可自由配置(正则也做了环境变量导入)。如果你没有添加 自定义域名,那云函数index.js
里不要加serverURL
:
1 2 3 4 5 6
| AV.init({ appId: LeanCloud_ID, appKey: LeanCloud_KEY, masterKey: LeanCloud_MasterKey, });
|
哔哔秘笈
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 「哔哔秘笈」 ================== /l 查询最近 10 条哔哔 /l 数字 - 查询最近前几条,如 /l3 --------------- /a 文字 - 最新一条原内容后追加文字 /a 数字 文字 - 第几条原内容后追加文字,如 /a3 开心! --------------- /f 文字 - 最新一条原内容前插入文字 /f 数字 文字 - 第几条原内容前插入文字,如 /f3 开心! --------------- /s 关键词 - 搜索内容 --------------- /d 数字 - 删除第几条,如 /d2 --------------- /e 文字 - 编辑替换第 1 条 /e 数字 文字 - 编辑替换第几条,如 /e2 新内容 --------------- /nobber - 解除绑定
|
接下来,发条说说试试看吧。也欢迎关注我的,试试效果。
关注我的公众号「哔哔闪念」先行体验