鹅玉
混乱5月, 摔过陡坡

27.May.2025

我要平等的嫉妒每一个会背书的人

要考试了, 但是复习不好, 所以还是以下半年的考试为目标复习, 这次考试就算球了吧

虽然当初学习计划做的很好, 但是实际执行起来, 我就很容易受到各种因素影响. 比如领导下发了任务, 比如偷偷竖起耳朵偷听旁边的小姐姐跟人聊天, 比如游戏队友拉我一起去百战, 比如打开书总想摸摸手机, 比如听到了好听的音乐想入非非, 又比如好不容易专心了却又困了…

总之找了很多借口, 复习缓慢却终于迎来了考试日.

考试日的前几天前, 看着deadline一步步接近, 终于开始牙床着急上火. 记得上次同一个位置上火是我毕业答辩前. 所以这次它一发动攻击, 我就意识到, 我太急了, 不能再这样下去了. 近几年身体都不算好, 所以考试考不过就承认自己是个失败者好了, 不要跟身体过不去😂. 正好还去看了一次老中医, 听大夫自信的跟我说, “没事的, 放宽心, 我这几副药下去, 火退的干干净净…”

我不甘心啊, 考前一天的晚上还点灯熬油临阵磨枪. 你看, 我是知道努力的, 虽然晚了点.

然后就出发奔赴考场.

emm…看到考题的时候我就笑了. 😑好, 很好, 特别好. 完美高效的避开了我的知识储备. 怎么能这么背呀. 第一场是基础知识, 全是选择题, 给了150分钟的时间, 只能提前30分钟交卷. 就这种不会靠蒙的考试, 哪里需要120分钟, 我生生的玩了60分钟的手指.

印象深刻有一道题, 我恍惚进考场前瞄过相关知识点. 考题是问这个有几种, 我突然就记忆模糊了, 分不清4还是5. 讲道理啊, 我本能就觉得该猜5, 因为一般分类的时候就爱分5种, 于是我一开始选了5. 后来玩手指的时候, 就反复犹豫啊, 就琢磨啊, 为什么我对4那么有印象, 哎呀不会是4吧, 然后改成了4. 交卷后, 我第一时间验证, 丫的答案是5! 好气😡!!!

下午的论文, 都是我没用过/听过的技术, 4选1作答. 我反复在两个可能能靠上点的题目之间反复摇摆, 就听着旁边童鞋噼里啪啦, 噼里啪啦的打字声. 啊, 他们复习的怎么这么好羡慕啊. 最后时间赶得紧, 只勉强编了2k字, 压线了最低标准. 与我约考的妹纸锐评, “这个考试就是这样, 跟世界分享你新编的项目.”

行吧, 总算是考完了.(‾◡◝)

也不知道是为什么, 就当天考试回家后浑身上下特别的疲劳. 第二天正好是周末, 我在家瘫了一整天, 什么都塌不下心做不进去, 摆烂一整天.

跟typecho分手的过往

一直对typecho心情复杂. 我主要受不了发日志需要进入网站后台, 而且虽然都是md, 我用vscode编写后, 拷贝进typecho里还要单独在调整一下格式, 就真的很麻烦. 还有一个对它很难受的地方是, 代码的缩进有时候莫名的不对, 就我反复调整半天也不行, 特别奇怪. 最让我担心的一点是, 经常看到有博主分享被人暴力请求网站管理地址, 还有一个我偷偷关注的博主, 网站后台被人破门而入, 然后页面数据被加了一些红橙绿青蓝紫的内容. (这个博主没有详细说怎么破的, 更具体的我也不知道.) 那之后修改typecho后台地址就加入了我的::Todo清单.

也可能是真的不爱学习, 就看书的时候就说, 哎呀折腾吧, 折腾吧, 把typecho换了吧, 换了就不担心了. 就各种邪恶小人在我脑中折磨我诱惑我, 所以5月10日的终于跟typecho分手了. (明明前不久刚刚折腾过typecho…)

1.导入旧文章

其实我的typecho数据一直保存在sqlite里, 所以备份一直不是我的痛点. 导出数据的话, 我用python就写了个简单的脚本, 把数据直接按照我需要的结构导出到md里.

导出的过程真的挺无痛的, 我觉得能分享的不多.

导出所有日志.

1
SELECT * FROM `typecho_contents` WHERE `type`='post';

导出指定日志所有分类/标签

1
SELECT * FROM `typecho_relationships` JOIN `typecho_metas` WHERE `typecho_relationships`.`mid`=`typecho_metas`.`mid` AND `typecho_relationships`.`cid`=日志id

typecho正文第一行是<!--markdown-->. 在导出的时候可以去掉它.
剩下就是对字段了.

2.导入旧评论

静态博客本身没有评论系统, 第三方评论选择也比较多. 我看了一圈, 最后选定了twikoo. 主要是它云函数部署方案比较多特别香, 我一开始想选择能托管的, 这样我就省事多了. 结果试了一圈, Hugging Face莫名的部署失败; Netlify一注册就被ban了, 申诉流程上恰好蓝屏再也进不去, 发邮件也没用; Vercel打不开约莫是墙了? 最后无奈打算自部署. 我优先考虑是打算私有部署, 但是一直失败, 提示代码里有问题. (我改了改也还差东西.) 最后无奈选择docker, 没想到这个反而是曲折进程中最顺利的那个.

既然自部署了, 索性就用LokiJS数据库了, 这样数据也方便备份.

所以下面, 我需要的就是把旧评论导入到twikoo中. 我看了官方推荐的typecho方法, 是通过接口灌入的, 我数据库文件都在手里了, 还通过接口走岂不是太麻烦了. 所以我直接用python生成了LokiJS数据库里的数据, 然后把服务器上的数据库文件直接替换了, 重启docker就好了.

数据的转换其实也就是对字段, 这里大概记一下这个过程.

 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
# 假设typecho原有的一行数据存放于row字典中
# row = {列名:数据}
coid = "%s" % hash(row['coid']) # twikoo的id是hash过的
created = row['created'] * 1000, # 时间戳位数不太一样
data = {
    # 标识符
    "_id": coid,
    # 评论人数据
    "uid": "", # 不知道怎么来的, 我直接随便给了一个
    "nick": row['author'],
    "mail": row['mail'].lower() or None,
    "mailMd5": md5(row['mail'].lower().encode('utf-8')).hexdigest() if row['mail'] else None,
    "link": row['url'],
    "ua": row['agent'],
    "ip": row['ip'],
    "master": True if row['author'] == 我本人的名字 else False,
    # 评论数据
    "url": url,
    "href": row['cid'], # 页面地址, 在typecho和hugo之间可能有变化
    "comment":"<p>%s</p>\n" % row['text'], # 评论内容, 这里其实需要细致转换一下, 我实在是懒得弄了, 就随便写了一下. 所以有的旧评论显示样式变了, 但是反正内容都在的.
    # 回复数据
    "isSpam": True if row['status'] == 'spam' else False,
    # 时间
    "created": created, # 时间戳位数不太一样
    "updated": created,
    # 不知道啥
    "meta":{
        "revision": 0,
        "created": created,
        "version": 0
    },
    "id": coid
}
# 回复数据
if row['parent'] != 0:
    data["pid"] = "%s" % cidmap[row['parent']] # 父id
    data["rid"] = "%s" % cidmap[find_root(row['coid'])] # 根id
# 这里要特别写一下, pid,rid也都是hash的id
# 且, 原typecho的评论有pid, 但是没有rid, 所以需要自己统计一下
# cidmap中存的就是coid和他hash后的值, cidmap = {coid: hash(coid)}
# find_root()是递归寻找给定coid的根id的方法

3.去掉了热力图, 又加上了简单说说

换hugo后, 我移植了之前的typecho主题(还在赏味期), 也保持了只输出近两年日志的内容控制方案. 但之前使用的全年日志热力图就不再保留了. 考虑是, 热力图这个玩意给我施加了很大的更新压力.

于是原有的位置有点空, 我又想起了当初在外网部署memos时, 可以通过获取memos的数据显示说说内容. 但是上次更新我已经把memos放内网使用了, 为这点小事再部署一个显然有点高射炮打蚊子. (最近觉得memos太能更新了, 还经常有接口变动实在是好麻烦啊.)

经过在网路高强度冲浪后, 学习到了说说可以有如下的解决方案.

  1. memos
  2. 长毛象
  3. artitalk
  4. status.cafe

memos被我啪掉之后, 下一个就在琢磨毛毛象, 我也确实在用. 考虑到并不想把所有社交网络都串成一串, 希望能保留一些马甲不互通, 所以暂且放弃.

之后pass掉的下一个选择就是artitalk了, 通过leancloud实现的可实时发布说说的js. 我不想注册LeanCloud, 所以直接跳过.

在neocities高速冲浪后发现有人在用status.cafe做静态页面的说说挂件. 去瞅了瞅, 觉得很不错, 于是就注册了. 结果苦等两天也没收到验证邮件😥.

在绝望之余, 某天早饭突然想起不如干脆就用评论系统实现得了, 这样的优点在于我的说说和评论可以放在一起, 方便一起备份.

所以其实说说还能再加上两个解决方案.

  1. twikoo(理论上任意评论系统)
  2. 直接写在md里

然后在我决定要放弃status.cafe时, 收到了它的验证通过邮件. 呜呜呜, 太难了. 其他的方案网上可以找到的教程很多, 所以下面就打算写下twikoo与status.cafe两种方案的解题思路. (status.cafe写思路的魔法师也有不少, 不过中文站用的不多, 还是打算记录一下.)

其实这些方案在实现过程都是同样的两个步骤.

→ js获取/构造结构化的说说数据.
→ 使用html+css样式渲染.

除了markdown里直接写外, 渲染的部分大家都一样, 就主要写下获取数据吧.

3.1 status.cafe解题思路

status.cafe是一个可以发布状态的网站, 并且提供api可以获取自己发布的状态, 也可以rss订阅, 然后提供给静态网页使用. 中文站点用的人比较少, 我看到很多外文个人站都在使用. 虽然页面看着像是上世纪的, 但是它的时间线上总是刷的很快, 我没事的时候也喜欢去上面点点. 有人的status.cafe主页装修的超级好看, 各站也超级美丽, 是我冲浪收集灵感的源泉之一.

说到status.cafe我必须要提一嘴注册. 它注册后需要等待审核, 可是审核看起来是站长手工通过的, 因为我真的等了好几天. 😥😥😭差点就要放弃了.

  1. 获取最新一条说说

    status.cafe官方提供了一个结构, 但是我觉得不好看. 所以我就直接把其中的api拿来用.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    <p id="last-shuoshuo">数据获取中...</p>
    <script>
        fetch("https://status.cafe/users/您的用户名/status.json") //用户名换在这里
        .then( r => r.json() )
        .then( r => {
            var lastdudu = '';
            if (!r.content.length) {
                lastdudu = "No status yet."
                return
            }
            //用户名: r.author
            //说说内容: r.content
            //表情: r.face
            //相对评论时间: r.timeAgo
            last_shuoshuo = r.face + r.content + '<time>' + r.timeAgo + '</time>';
            const elementById = document.getElementById("last-shuoshuo");
            elementById.innerHTML = last_shuoshuo;
        })
        .catch(err => {
            // 发生错误
            console.error(err);
        });
    </script>
    
  2. 如果想获取更多的说说

    如果想做一个说说单页, 那么就需要能够获取更多的说说信息. 我们可以通过rss地址来操作.

    rss地址: https://status.cafe/users/您的用户名.atom

    然后获取的数据可以通过js来拿到具体需要的字段, 再进行样式渲染. 这块比较简单了, 就是相应的取字段的过程, 看到很多关于这块的怎么操作的攻略啦, 就不再过多的叙述.

status.cafe提供一个个人主页, 会把你所有的状态显示在一起, 有一点文字微博的味道. 它也是可以装修的, 在设置中, 可以包含一个css样式, 通过它来打造自己的主页.

3.2 twikoo解题思路

用twikoo来解题的话, 其实就是偷偷藏一个页面写自己的评论, 然后在说说的位置调用这些评论显示, 就假装有一个说说页面啦.

  1. 用接口获取最新评论

    看Twhikoo的api, 使用Get recent comments能够获取最新的评论, 我们就用这个来实现功能. 因为在存档页留的位置有限, 所以我只要说说后台页最新的一条评论.

     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
    
    <shuoshuo>
        <p id="last-shuoshuo">数据获取中...</p>
        <script>
            twikoo.getRecentComments({
            envId: '您的环境id', // 环境 ID
            // region: 'ap-guangzhou', // 环境地域,默认为 ap-shanghai,如果您的环境地域不是上海,需传此参数
            urls: [ // 要求云函数版本 >= 1.6.27。不包含协议、域名、参数的文章路径列表. 这块填入说说后台页面的地址.
                '/说说后台页面.html'
            ],
            pageSize: 1, // 获取多少条,默认:10,最大:100 (我只要1条, 如果考虑到被游客发现后台页面并往内评论的话, 可以多获取一些)
            includeReply: false // 是否包括最新回复,默认:false
            }).then(function (res) {
            console.log(res);
            // 返回 Array,包含最新评论的
            //   * id:           评论 ID
            //   * url:          评论地址
            //   * nick:         昵称
            //   * mailMd5:      邮箱的 MD5 值,可用于展示头像
            //   * link:         网址
            //   * comment:      HTML 格式的评论内容
            //   * commentText:  纯文本格式的评论内容
            //   * created:      评论时间,格式为毫秒级时间戳
            //   * avatar:       头像地址(0.2.9 新增)
            //   * relativeTime: 相对评论时间,如 “1 小时前”(0.2.9 新增)
            var last_shuoshuo = '';
            for (var i = 0; i < res.length; i++) {
                var nick = res[i].nick;//访客姓名
                if (nick == '...') { //这里填写主人的大名!
                    //do something....
                    //你可以按自己的需求每条处理数据, 然后把它们append在一起
                    //我只要一条所以直接改写内容了.
                    var content = res[i].commentText;//评论内容
                    var updatedAt = res[i].relativeTime;//评论时间
                    last_shuoshuo = content + '<time>' + updatedAt + '</time>';
                } else {
                    last_shuoshuo = '大人, 您的秘密被坏人发现哒'; //因为我只获取了一条, 所以如果不是我本人的就是坏人哒!
                }
                const elementById = document.getElementById("last-shuoshuo");
                elementById.innerHTML = last_shuoshuo;
            }
            }).catch(function (err) {
            // 发生错误
            console.error(err);
            });
        </script>
    </shuoshuo>
    

    大概意思就是这样, 仅供参考~

    好吧, 如果打算做一个说说显示页面, 可以考虑多获取一些数据, 并过滤掉非本人的评论, 然后整个页面像memos/毛毛象那样展示.

有了数据之后, 就差显示了. 这块萝卜青菜各有所爱, 我就不献丑啦.

与hugo相恋的日子

使用静态博客后, 我终于不用再"本地编辑好md-复制到typecho后台-调整日记格式"这样的制造流程了, 这让我轻快不少. 可能是ide用的太顺手了, 就上班搬砖间隙摸个鱼写两笔, 无声无息, 格外的适应. 能换到静态博客, 真的是太好了.

因为我控制了输出的内容范围, 以前在用typecho时, 看早期日记就很麻烦, 只能从后台一篇篇点过去看. 现在全是文本存在本地后, 我想看哪篇直接打开就好啦, 也不用等待网络响应, 整个过程非常愉悦!

唯一有一点我很在有意见的地方是, hugo模板里我插了不少控制语句, 在生成成html后会带来大量的空行, 特别不爽, 我现在还不知道怎么弄掉他们. 不过没有什么不能驯服的, 我先再忍忍.


本来这篇日记的大纲里我还想分享下我最新的心上之物, 但是已经哔哔太多内容了, 超过了我的忍耐极限, 就下次再提吧.

p.s.
这篇笔记扔在草稿堆里比较长的时间, 主要是最近一段时间真的焦灼又混乱. 随便写了一些折腾细节, 希望能给一些有同样需求的魔法师大人带来一些启发.


- CATALOG -
comments 🙊🙉🙈