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 //30day
}),
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 //30day
}),
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 无法播放视频问题

  1. 原因:Safari 处理video标签时,会先发一个获取视频 1 字节信息的请求,拿视频封面之类的。只有用户点击播放时,才会再发一个请求,获取后面的完整内容。这里需要 cos 正常响应range请求。但是我原本使用了本站的cdn.guole.fun来获取媒体资源,但这个 CDN 加速域名配置的加速类型是“ CDN 网页小文件”,似乎会覆盖导致存储桶本身自动给 .mp4 文件设置的Content-Type: video/mp4 头部信息不生效,Chrome 中正常,但 iOS Safari 中就不行(iOS 里的 Chrome / 微信内嵌浏览器都使用 Safari 内核……)
  2. 解决:测试直接放存储桶的资源链接,是可以正常播放的。但是我这边重新设置cdn.guole.fun的加速类型为“音视频点播”时报错,设置不成功。我就曲线解决了,重新创建了个media.guole.fun加速域名(与cdn.guole.fun都关联同一个存储桶……),专门用来读取多媒体资源,cdn.guole.fun先保留用作它用,后续考虑要不要删除重新创建为“音视频点播”(2023.6.26 已删除重建解决)。
  3. 创建好相关域名后,拆分成:顶级域名、二级域名、子域,在下面环境变量中配置既可。

综上所述,如果你准备新建一个存储桶用来放说说多媒体资源,一定记得选加速类型为“音视频点播”。不建议直接暴露使用存储桶的原始域名(没有 CDN 加速)。同时,存储桶和加速域名设置好防盗链,免得被盗刷……

原文

分享下本站说说的发展历程。

早前,使用黑石哔哔、木木的 bber 、小康的 ispeak 等,后来腾讯云开发收费了,就陆续没法用了。

目前本站哔哔存储在 LeanCloud (其他 MongoDB 啥的应该也没问题),/bb/ 和首页轮播是我写了个 Vercel 云函数接口,加了个缓存功能,同时避免 LeanCloud 的访问秘钥在前端被泄露。欢迎扫描关注,先行体验效果再决定要不要折腾。

哔哔闪念 这个端午竟然没出去玩!!就把之前用木木的 bber 复刻了出来,同时新增了发视频、发位置、发链接卡片等功能。微信其实还支持个语音消息,由于是 amr 格式音频,浏览器没法播放,服务端复用 FFmpeg 等插件转换格式,又一直环境不对……心态崩了,干脆摆烂……谁搞定了,欢迎 PR 或下面留言通知我啊!

注:本文包含的微信发说说功能,部署在腾讯云 Serverless 里,其他 Vercel 、Railway 应该也没问题,我没折腾。之前域名备案,一定要有服务器之类的 IP ,就买了三年 Serverless 套餐,个人使用不超量的话也很便宜,所以 也不能闲着便宜了腾讯不是……

效果演示

支持特性

  • 包含
    • 部署在云函数上,无需服务器
    • 使用微信随时随地发布闪念瞬间(memos 很香,但是得有机器……)
    • 在原来 @木木木bber-weixin 基础上升级而来,新增支持:发位置、发链接卡片、发视频功能
    • 发图片:直接拍照片或发本地图片给公众号既可(存在腾讯云 cos ,原有的去不图床逻辑没删,但我也没验证);
    • 发视频:录视频、或本地视频直接发给公众号既可(存在腾讯云 cos,支持 .mp4 / .flv / .ts ,其他格式受腾讯 CDN 支持范围影响,不支持 Safari 播放);
    • 发位置:需要在 /bb/ 页面引入资源,详见使用指南;(其他页面嵌入地图,见另一个插件:hexo-tag-map
    • 发链接卡片:直接分享卡片到公众号;
    • 发语音:本来想借用 FFmpeg 在服务端转换语音音频 amr 为 mp3,前端再调 aplayer 等播放,但是夭折了……云函数里一直搞不定 FFmpeg 的环境……

部署到 LeanCloud

这部分很简单,到 LeanCloud 注册个 国际版 账号,然后创建一个应用,比如就叫 bb,然后新建 2 个集合 contentUserBindingStatus

LeanCloud 可以在 LeanCloud 导入导出功能里,把你原来的数据导进去(建议部署后,发布 1 条消息,数据库自动创建字段后,再导出到本地,按照字段映射手动修改后,再导入以前的数据进去。做好备份,大不了多试几次)。content 集合差不多有这几个字段:

字段名类型说明
objectIdString唯一标识符,LeanCloud 自动创建,无视它;
ACLACL读写权限,LeanCloud 自动创建,无视它
MsgTypeString微信消息类型,前端用来处理一些逻辑
contentString说说内容
fromString说说来源,自定义
otherString一些特殊消息,比如位置消息,保存脚本,前端插入后渲染出地图
createdAtDate创建日期时间,LeanCloud 自动创建,无视它;若导入数据,得修改这个字段,ISO 格式的时间戳,比如 2023-06-24T07:20:55.650Z
updatedAtDate更新日期时间,LeanCloud 自动创建,无视它

UserBindingStatus 集合不用管,字段云函数会自动创建。(其实 content 集合应该也不用手动创建字段……只是我没试过)

获取 LeanCloud 凭证

  1. 上述 content 和 UserBindingStatus 集合需要在同一个应用里,这样 appid 才一样;
  2. LeanCloud 应用凭证 界面(设置——应用凭证),找到 AppID / AppKey / MasterKey 信息,记录下来下文使用。

获取 LeanCloud 凭证

获取腾讯云 API 访问密钥

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

QcloudCOSDataFullControl
4. 记录下 SecretId / SecretKey

腾讯云 API 访问密钥

获取微信公众号凭证

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

获取微信公众号凭证

创建云函数

  1. 访问腾讯云云函数平台 腾讯云 Serverless;
  2. 创建一个云函数,选择 从头开始,函数类型:事件函数;函数名称:自定义;地域:中国香港(要访问 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 请自行手动创建):

keyvalue
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图片 / 视频的上传方式,可选值:cosqubu,要使用发视频功能必须配置为cosqubu我没测试…
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 ,此项必填
  1. 提交保存环境变量后,回到函数配置界面,点击部署
  2. 在云函数触发管理中,记录下访问路径

完成微信公众号配置

在刚才停留的公众号配置界面,服务器地址填写上面记录的云函数访问路径,然后提交,应该就成功了。

IP 白名单:一定要配置这个,不然发图、视频时,取不到微信 Access token 。ip 就是上面云函数的固定出口 IP 地址。

ip白名单 这个地方如果微信校验不通过,重点检查云函数环境变量 Token 与微信这边填的是否相同,验证云函数执行方法是否为index.main_handler,浏览器访问云函数访问路径,显示403错误应该就没问题;

Hexo 创建说说页面

  1. 在 Blog 根目录,执行hexo new page bb新建一个页面,bb是说说页面路径,可以自定义;
  2. /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
    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
    ---
    title: 哔哔闪念
    date: 2021-06-07 17:29:43
    type: "bb"
    aplayer: false
    top_img: false
    ---

    {% note success %}
    本页记录 @夜的第八章 的胡思乱想
    {% endnote %}

    <style>
    #article-container img {margin: auto !important;}
    #bbtalk {
    margin: 0 2px 56px 16px;
    }
    #bbtalk div.timenode {
    background: unset;
    box-shadow: unset;
    border: var(--style-border);
    margin-top: 1.8rem;
    }
    #bbtalk div.timenode:hover {
    background: unset;
    box-shadow: unset;
    border: 1px solid rgba(28,105,237,0.5);
    }
    @font-face {
    font-family: "iconfont";
    src: url("//at.alicdn.com/t/font_1755564_z4mhxbw13mq.eot?t=1586882866168");
    /* IE9 */
    src: url("//at.alicdn.com/t/font_1755564_z4mhxbw13mq.eot?t=1586882866168#iefix")
    format("embedded-opentype"),
    /* IE6-IE8 */
    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"),
    /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
    url("//at.alicdn.com/t/font_1755564_z4mhxbw13mq.svg?t=1586882866168#iconfont")
    format("svg");
    /* iOS 4.1- */
    }
    #bbtalk img {
    display: block;
    }
    .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";
    }

    [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;
    }
    /* bb样式 */
    /* 移动端自定义布局适配 */
    @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;
    }
    }
    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;
    color: var(--color-meta);
    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 1rem 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;
    /* transition: opacity 0.5s; */
    }
    div.timenode .body {
    margin: 0 15px;
    padding: 0 16px 16px 16px;
    border-radius: 8px;
    /* background: #f6f6f6; */
    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.6rem 0;
    }

    /* 视频样式 */
    #bbtalk-content video {
    margin-top: 0.8rem;
    }

    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 {
    /* background-color: #f596aa; */
    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: block;
    padding: 0 1rem;
    height: 40px;
    font-weight: 500;
    text-align: center;
    transition: all 0.5s ease-out;
    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;}
    </style>

    <div id="bbtalk"><p class="info"></p></div>
    <div id="loading"><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;">~&nbsp;我是有底线的&nbsp;~</div>
    <link rel="stylesheet" href="//unpkg.com/hexo-tag-map/lib/leaflet@1.7.1.css">
    <script data-workbox="" src="//unpkg.com/hexo-tag-map/lib/leaflet@1.7.1.js"></script>
    <script data-workbox="" 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@1.0.3.js"></script>
    <script>
    bbtalk.init({
    bbtalk_name: '夜的第八章', //昵称
    bbtalk_avatar: '/img/gl.jpg',//头像
    bbtalk_avatar_svg: '', //头像后的图标,可以为空用磨人的
    });
    </script>
    上面的 timeago 可以直接去官网下载一个,放到本地目录。BBtalk@1.0.3.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
    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
    var bbtalk = {
    limit: 12,
    bbtalk_name: 'BBTalk',
    bbtalk_avatar: 'https://example.com/avatar.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;
    }
    };

    var loading = document.querySelector('#loading');
    var loadMoreBtn = document.querySelector('#load-more-btn');
    var bbTalkBottomTip = document.querySelector('#bbtalk-bottom-tip');
    var urlinfo = window.location.pathname;
    let page = 1;
    let isLoading = false;

    function fetchContent() {
    if (!loading || !loadMoreBtn || isLoading) return;
    isLoading = true;
    loading.style.display = 'block';
    loadMoreBtn.style.display = 'none';
    const timestamp = Math.floor(Date.now() / 1000 / 60);
    fetch(`https://cdn.guole.fun/json/bb-json/bbtalk_page${page}.json?t=${timestamp}`)
    //fetch(`https://leancloud.guole.fun/1.1/classes/content?page=${page}&limit=${bbtalk.limit}&order=-createdAt`)
    .then(response => response.json())
    .then(data => {
    data.results.forEach(item => {
    let timenode = document.createElement('div');
    let time = formatTime(item.createdAt);
    let time_ago = timeago.format(time, 'zh_CN');
    let content = urlToLink(item.content);
    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" id="bbtalk-content">
    <div class="bbtalk-media" id="bbtalk-media"></div>
    ${content}
    </div>
    <div class="bbtalk-from" style="background-color: ${fromColor};color: var(--bbtalk-from-color)">
    <span>${item.from}</span>
    </div>
    </div>
    `;
    timenode.classList.add('timenode','wow','animate__zoomIn');
    const BBTalk = document.getElementById('bbtalk');
    if (BBTalk) BBTalk.appendChild(timenode);
    if (item.MsgType === 'location' && item.other) {
    const assets = document.createElement('script');
    assets.innerHTML = `${item.other}`;
    document.body.appendChild(assets);
    }
    },
    );
    page++;
    isLoading = false;
    loading.style.display = 'none';
    loadMoreBtn.style.display = 'block';
    bbTalkBottomTip.style.display = 'none';
    const info = document.querySelector('p.info');
    if (info) info.innerHTML = `共计发送 ${data.count} 条闪念`;
    if (data.results.length < bbtalk.limit) {
    loadMoreBtn.style.display = 'none';
    loading.style.display = 'none';
    bbTalkBottomTip.style.display = 'block';
    }
    })
    .catch(error => {
    console.error(error);
    isLoading = false;
    loading.style.display = 'none';
    loadMoreBtn.style.display = 'block';
    });
    }

    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) {
    const re = /(?<!class="bbtalk-url" id="bbtalk-url".*)(?<!<img\s+[^>]*\bsrc=["'])(?<!<video\s+[^>]*\bsrc=["'])\bhttps?:\/\/\S+?(?!.*\.(png|jpe?g|webp|gif|mp4|mp3|amr|speex)$)[\w\/\-.]+\b/g;
    const re_tagImg = /<img [^>]*src=['"]([^'"]+)[^>]*>/g;
    const re_mdImg = /!\[(.*?)\]\((.*?)\)/g;
    const re_forpic = /\bhttps?:[^:<>"]*\/([^:<>"]*)(\.(jpeg)|(png)|(jpg)|(webp)|(gif))/g;
    const music_type = /(&songmid=)|(song\?id=)|(songDetail)/g;
    const music_id = /((?<=\?id=)(.*?)(?=&uct))|((?<=\&songmid=)(.*?)(?=&type))|((?<=\&id=)(.*?)(?=&ADTAG=))|((?<=playlist\?id=)(.*?)(?=&userid=))|(?<=playlist\/).*$|((?<=h5_playsong\&mid=)(.*?)(?=\&no_redirect))|((?<=songDetail\/)(.*?)(?=\?songtype=))/g;
    const aplayer_assets = /Z4C7B3E9F1A2D6G8H0J5I1K9L3M7N8O1P2/g;
    let type, server, music;

    str = str.replace(re_tagImg, (_, url) => url)
    .replace(re_mdImg, (_, __, url) => url)
    .replace(re_forpic, url => `<a href="${url}" target="_blank" data-fancybox="group" class="fancybox"><img src="${url}"></a>`)
    .replace(aplayer_assets, url => `<script async onload="initAPlayer()" src='${bbtalk.aplayer_js}'></script>`)
    .replace(re, website => `<a href='${website}' rel='noopener noreferrer' target='_blank'>外部链接</a>`); //其他超链接

    if (music_type.test(str)) {
    type = 'song';
    server = /y\.qq\.com/g.test(str) ? 'tencent' : 'netease';
    music = str.match(music_id);
    str = `<meting-js server="${server}" type="${type}" id="${music}"></meting-js>`;
    }

    if (window.qqWechatEmotionParser) {
    str = qqWechatEmotionParser(str);
    }

    return str;
    }

    if (loadMoreBtn && urlinfo === '/bb/') {
    loadMoreBtn.addEventListener('click', fetchContent);
    }

    window.addEventListener('DOMContentLoaded', function () {
    if (urlinfo === '/bb/') {
    fetchContent();
    }
    });

    //pjax 兼容,没使用可以不加
    document.addEventListener("pjax:complete", (function () {
    urlinfo = window.location.pathname;
    loading = document.querySelector('#loading');
    loadMoreBtn = document.querySelector('#load-more-btn');
    bbTalkBottomTip = document.querySelector('#bbtalk-bottom-tip');
    page = 1;
    isLoading = false;
    if (urlinfo === '/bb/') {
    fetchContent();
    loadMoreBtn.addEventListener('click', fetchContent);
    }
    }));

首页轮播说说

自定义或放到其他能全局引入的 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
//- Guo Le's Blog
//- https://guole.fun/
//- 2023.5.10

//首页轮播闪念
function addBB() {
const urlinfo = window.location.pathname;
if (urlinfo === '/') {
if (document.querySelectorAll('ul.talk-list').length > 0) {
let jsonUrl = '';
jsonUrl = 'https://api.guole.fun/bbtalk?page=1&limit=10';
//console.log('获取到 jsonUrl' + jsonUrl)
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="item item-' + (i + 1) + '"><a href="/bb" style="color: var(--font-color);/*font-weight: normal;*/">' + newdataTime + ': ' + urlToLink(item.content) + '</a></li>';
});

const talkList = document.querySelector('ul.talk-list');
const loadingItem = document.querySelector('ul.talk-list > li.item.loading');
const talkRight = document.querySelector('a.index-talk-right');

loadingItem.style.display = 'none';
talkRight.style.display = 'unset';
talkList.insertAdjacentHTML('beforeend', bberHtml);
}
};
xhr.send();

function urlToLink(str) {
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="18" height="18"><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>音乐分享';
});
}

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 = /<video[^>]*>[\s\S]*?<\/video>/is;
const reMap = /(<div[^>]*>(.*?)<\/div>|<\/div>)/is;
let count = 0;

str = str.replace(reA || 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>';
});

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>';
});

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 roll() {
const listLi = document.querySelectorAll('.talk-list li');
if (listLi.length >= 2) {
const curLi = listLi[0];
const lastLi = listLi[listLi.length - 1];
lastLi.parentNode.insertBefore(curLi, lastLi.nextSibling);
}
}

setInterval(roll, 3000);
}
}
}

function initBlog() {
addBB();
}
//方法调用
// window.onload = function() {
// initBlog()
// }
document.addEventListener('DOMContentLoaded', function () {
initBlog()
})

//workbox 兼容
document.addEventListener("workbox:success", (function () {
initBlog()
}))

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 轮播 */
#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;
/* margin-top: 1rem; */
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是云函数配置的环境变量。

1
/b bb,${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,
//serverURL: 'https://leancloud.guole.fun',
});

哔哔秘笈

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 - 解除绑定

接下来,发条说说试试看吧。也欢迎关注我的,试试效果。

关注我的公众号「哔哔闪念」先行体验

哔哔闪念