普通视图

2025 重新定义的生活

2025年12月25日 12:00

AI 生产力革命与迷茫

四年前我准备读研,那时对图像视频处理很感兴趣,所以最开始想学计算机视觉。后来发现传统 CV 已经是夕阳产业,被深度学习全面吊打,于是干脆选了 Artificial Intelligence。

当时只是走马观花学一些概念,没想到不到两年,AI 就彻底爆发。2023 年 3 月 GPT-4 发布,震惊了全世界。到今年,AI 更进一步,开始实打实地改变很多行业的工作方式,也改变了很多人的生活方式。

我作为程序员和插画爱好者,几乎在两个方向同时感受到了冲击。一方面是效率暴涨带来的兴奋,另一方面是越来越强的迷茫,甚至演化成一种存在危机。不是那种轻飘飘的焦虑,而是你突然意识到自己赖以立足的东西正在被改写,而且改写得非常快。

插画:从多年练习到输入一句话

原创画师可能是最早被冲击的一批。Stable Diffusion 刚出现时,画师群体曾激烈抵制。可怕的是,再后来就听不见他们的声音了。并不是他们妥协了,而是 AI 作品真的太好、太快,快到很多争论甚至来不及发生就已经失效。

随后有一部分原创画师开始接受 AI,把它当成辅助工具,也有大量使用 AI 的新人涌入,用更低门槛、更高产出的方式加入创作。

创作门槛一路下探,从需要数年的学习与练习,到买一块好显卡折腾几天,再到现在打开网页、输入想法、等几秒钟。更进一步,插画到动画、到视频也在被快速打通。某种意义上,人人都是画师不再是夸张,而更像是正在发生的现实。

编程:从片段补全到接管项目

程序员这边的变化也许会走向同一个结果,只是没有那么立刻和直观。2022 年大家还在用 GitHub Copilot 做自动补全,今年专用 AI Coding 模型与 vibe coding 工具层出不穷,很多时候已经变成你用自然语言描述需求,AI 写代码、改代码、修 bug、补测试,甚至能产出一个完整项目。

对程序员来说,vibe coding 能把生产力拉高数倍,对非程序员来说,coding 也不再是程序员的专属技能。越来越多人不写一行代码,也能做出精美应用,解决自己的真实需求。编程正在从一种职业技能,变成一种人人都能动手的表达方式。

于是,程序员的工作方式正在被改写。传统意义上那种手写代码的「代码匠人」会越来越少,很多方向会像传统 CV 一样被 AI 替代。我也越来越少亲手写代码了,越来越习惯和编辑器右侧触手可及的 AI 沟通。它完成得又快又好,而且越来越好,从工具到助手,再到一种近乎替代本体的存在。

这种替代感最刺痛的地方在于它并不只替代体力活。它在替代判断、套路、经验和熟练度,也在替代我曾经引以为傲的学习速度。我学到的知识在贬值,我积累的经验在贬值,我拥有的技能在贬值,甚至连智力和创造力也在贬值,而且贬值速度似乎远高于我的成长速度。等程序员岗位真的消亡之后,我的价值在哪里。

但当下程序员显然还是无法被完全替代,也许 AI 也会像摩尔定律一样发展到一定程度会碰壁。现在能做的是好好驾驭这股力量,积极参与这次生产力革命,希望能在其中找到自己的位置和无法被替代的独特价值。

迎来新生命

不管 AI 怎么发展,碳基生命孕育新生命的过程,在可预见的将来还无法被硅基生命取代。

知道发小(老婆)怀孕的那一刻,我更多是手足无措。但从那时起,很多心理和生活习惯就悄悄围绕这个即将到来的新生命发生变化。

发小的怀孕过程漫长又辛苦。前期孕吐昏天黑地,嗅觉变得异常敏感,平时正常的气味也会难以忍受,喜欢的食物吃不下去,激素让情绪波动更明显。中期好一些,后期肚子越来越大,腰酸背痛、睡眠下降、行动不便,走路都成问题。但发小还是表现出了惊人的坚韧和乐观,承受住了这些考验,还把产检、备产、育儿安排得井井有条。

与此同时,我也亲眼见证了生命如何一点点成形:从一个 2mm、模糊不清的小点开始——

慢慢初具人形——

再到四肢、五官清晰可见,能在肚子里动来动去、打嗝、做体操。几个月来,我几乎无时无刻不在感叹和敬畏生命的神奇,碳基生命扳回一局!

终于熬到 7 月 27 日晚上,哈哈(女儿)出生。还没来得及舒一口气,就被告知她血氧持续下降,被紧急转入 NICU 重症监护室观察治疗。

当时一边是发小大手术后意识不清,一边是刚出生的哈哈生死未卜。那一夜大概是我人生最黑暗的时刻。发现自己平时拥有的那些判断力、解决问题的能力、甚至乐观,都在这种时刻毫无用处。唯一能做的就是等,等一个并不确定会到来的好消息。

幸好经过几天治疗,哈哈脱离生命危险,有惊无险,出院回家。

经历过 NICU 之后,生活变得顺利起来,家里有发小和女佣照顾哈哈,哈哈每天都在成长,学会抓东西、翻身,开始有情绪、会认人。她也真的很可爱,每次出门遛娃,路人都会凑过来夸几句。

作为新手爸爸当然会疲惫,但更多的是一种很踏实的幸福感,它不需要被证明,也不需要被解释,它只是会在你抱起她的那一刻变得非常确定。

努力搞钱

众所周知,孩子是一只四脚吞金兽。一出生还没出院就吞掉了 S$100000,这件事非常直观地刷新了我对现实的理解。

前面说发小怀孕后,我的心理发生了很多变化,其中最关键的就是金钱观的变化。

以前我当然也想赚钱,但总体是够用就行。钱更像是实现爱好、维持生活的手段,而不是最终目的,所以在赚钱这件事上相对懒惰。比如之前选择工作时,我会毫不犹豫放弃双倍薪资的币安而去更符合爱好的哔哩哔哩,又比如四年前 Web3 牛市满地捡钱,我却无动于衷。这也导致我过去在个人财务上一直挺失败。

现在的这种变化有点像从无所谓直接滑到了过度补偿。我开始更在意安全感,也更容易把安全感和金钱绑定,钱对我的优先级被极大提升,我也更清楚地意识到,很多选择的底气最终还是来自现金流和风险承受能力。

今年我开始更主动地投资加密货币。四年前入行 Web3,去年才开始主动买币,这件事本身也挺神奇。过程非常曲折,中间有一段时间亏了很多钱,也买到了很多教训。好在最终结果还不错,而那些教训甚至比阶段性的收益更宝贵。

今年的收益率曲线(实现方法见 上古神器 Beancount:Crypto 与 AI 时代的复式记账终极方案):

从凭感觉到有体系

我的投资经历可以粗略分成两段,以三月为分水岭。

三月之前,我对策略和 K 线这些知识几乎一无所知,也没用任何分析工具,基本凭感觉和情绪买卖,像一种定投,跌了卖点,涨了卖点。因为市场整体向上、波动不算剧烈,所以哪怕闭眼瞎买,收益也还不错,甚至让我有点沾沾自喜。

但赌博最怕的就是一开始给你一点甜头。

后来遇到二月关税战带来的市场恐慌大跌,我损失惨重。我当时的心路大概是这样的:第一段下跌时我觉得是绝佳抄底机会,几乎掏出全部家当抄底;市场继续下跌时我开始心态崩,但还想扛一扛,拒绝止损;第三段更猛时我彻底崩溃,把所有持仓割肉卖掉。结果买在高点,卖在最低点,市场又很快反弹。

现在回看,我做得稍微对的一点是胆子小,没有上杠杆,否则更惨。更多的是犯了一堆典型错误,没有策略,被情绪控制,大仓位抄底,没有仓位管理,追涨杀跌…

三月之后,我开始系统学习分析方法和工具,开始设置止损止盈、控制仓位、控制情绪,不再盲目抄底和追涨杀跌,慢慢搭起一套更可持续的交易体系,亏的赚回来了,还额外赚了一些,到了今年的 1011 闪崩,或者年底的牛尾行情,这些下跌幅度比二月份更猛,我也没有再明显亏钱。

工具与策略

交易平台我主要用 OKX,手续费低,闲钱用简单赚币和偶尔闪赚做点低风险收益也不错。行情分析主要用 TradingView,功能强大、指标丰富。冷钱包用 OneKey,热门冷钱包安全性都过关,但 OneKey 的操作体验对我来说是断崖领先。

至于策略,我现在更像是在不断给自己加护栏,而不是追求某种神奇圣杯。我会提醒自己,无论当下多么有把握,市场都可能上涨,也可能下跌,但是不排除横盘的可能。我会合理安排并经常回顾现金、屯币、风险投资的比例。我宁愿少赚钱也不碰自己不理解、或未经充分博弈的东西,有耐心,理解慢即是快,活下来最重要,所以现在基本只交易 BTC、ETH。

我也会严格控制杠杆,绝大多数是 1 倍,最多不超过 3 倍。止损要清晰,愿意认错,不扛单。DeFi 尽量少碰,因为当你不知道理财的利润从哪里来时,说明你就是那个利润。

手动交易只看 4 小时以上的中长线,短线交给自动化策略,尽量减少短期情绪的影响。

一些片段留念:

不合时宜的开源思维

相比金钱观的快速转变,我在技术和产品上的观念转得没那么顺利。今年最主要的工作是 Folo。Folo 本身做了很多,新移动端、本地优先架构、AI 等等。但商业化的实施和验证推进太慢,再加上一些现实因素,后来不得不做出一些仓促转型。很多技术上的投入看起来像自娱自乐。更糟的是,团队里也出现过让人头疼的协作问题,甚至有员工离职后的背刺,搞得鸡飞狗跳,还更狗血地牵扯出其他幕后黑手和一串让人哭笑不得的阴谋故事,这里就不展开了。

回头看,商业化延迟与转型仓促并非偶然,而是从始至终根深蒂固在我和团队的开源思维和技术思维里。

开源本身当然没有问题。开源是一个很好的方式,用来实现爱好、做 side project、学习新知识。问题在于把过多开源思维混进一个需要严谨商业闭环的产品里,下场往往不会好。因为开源默认的是负收益的热情驱动、社区驱动、长期慢速打磨,而商业默认的是资本驱动、资源有限下的快速取舍。

如果把这种错配具体化,它会体现在很多地方。比如产品会缺少严谨的准备,方向更容易摇摆;用户混入大量低付费意愿人群,后续转型阻力较大;过于看重小众反馈,产品容易被拉向小众方向;团队招募与管理过于宽松,执行力不足、内耗严重;浪费大量时间追求没人在意的技术与设计细节的完美,付出巨大的时间机会成本等等。

对我来说,今年像是一堂延迟很久的商业常识课。它并不反对理想,也不反对开源,但它逼着我承认,理想需要边界,热爱需要成本表,选择需要对结果负责。把这些讲清楚很俗,可它就是现实。

而即使脱离现实,在开源象牙塔之内,对于以个人兴趣为主的开源项目来说,作者的爱好也往往会在长期孤独的维护中,在白嫖者和伸手党的理直气壮中,被一点点消磨殆尽。

经历这些事情之后,我开始重新审视开源对我而言究竟意味着什么。我对开源的态度,也从过去的绝对推崇,变成了一种带着警惕甚至轻微 PTSD 的复杂情绪。今后对开源的投入我应该也会更加兼顾现实、克制和有选择性。

赛博生活

来点轻松的,按惯例,总结一下今年的书影音游戏和应用。

应用

年度应用当然是 Folo。

它花掉了我今年大部分时间和精力,也留下了一些痛苦回忆。但如果你有稳定的阅读习惯,也有管理信息源的能力,Folo 依然是我心里目前最好的信息聚合应用。

桌面应用(顺便一提:AI 应用比例挺高,6/13):

  • Notion Calendar:日历客户端,体验比 Apple Calendar 好不少,UI 也更简洁。
  • Spark:邮件客户端。Spark V2 用了很久,V3 刚出时太难用,换到 Mimestream;但 Mimestream 太贵(一年 50 刀),用了一年又换回 Spark,发现现在已经成熟很多,虽然默认设置依然很反人类,但基本都能在选项里改掉。
  • TickTick:待办事项。
  • ChatGPT Atlas:浏览器。只能算暂时最优解;问题依然很多,尤其 DevTools 太差。我感觉还是应该返璞归真:如果 Chrome 正式支持竖标签页,我就会换回 Chrome。
  • Chrome:浏览器。Arc → Dia → ChatGPT Atlas → Chrome,刚写完这篇年终总结 Atlas 自己爆炸了,正好返璞归真了。
  • Warp:终端。
  • Folo:自己做的阅读器。
  • VSCode:代码编辑器。VSCode → Cursor → Codex 出来后又换回 VSCode。
  • Notion:写日记。早些年就是 Notion,后来看到 Obsidian 插件生态好,换到 Obsidian(见 基于 Obsidian 的生活记录系统),但后来发现这些都不重要——写作体验才决定你愿不愿意写、能写多少。Obsidian 受限于 Markdown 能力,始终追不上 Notion,加上 Notion 在 AI 上越投入越多,于是又换回来了。
  • Plex:媒体服务器,看 NAS 上的电影、电视剧和番剧。
  • 网易云音乐:按理说在国外应该用 Spotify,但 Spotify 的推荐实在不对我胃口。网易云音乐又因为版权不提供海外服务,只能在 NAS 跑 UnblockNeteaseMusic 做代理,很麻烦。
  • Telegram / Discord / Slack / WhatsApp / 微信:即时通讯,越来越分散了。
  • OneKey:冷钱包,体验断崖领先(上一节也提过)。
  • Eagle:图片管理。
  • Googly Eyes:一双眼睛。

NAS 在跑的服务:

  • AdGuard Home:广告屏蔽 + 家庭网络更快的 DNS。
  • Fava:记账,见 上古神器 Beancount:Crypto 与 AI 时代的复式记账终极方案
  • Gitea:托管一些不适合公开的代码。
  • Home Assistant:智能家居控制。经常搬家没怎么搞智能家居了,主要用来做备用机充电管理、设备定时开关机。
  • homepage:服务太多,用一个入口集中管理。
  • Nginx Proxy Manager:给各服务分配域名,访问更方便。
  • OpenList:自动下载阿里云盘、PikPak 等保存的内容到 NAS。
  • UnblockNeteaseMusic:让网易云音乐在国外也能用。
  • Uptime Kuma:监控服务可用性。
  • ANI-RSS:追番自动下载订阅番剧。
  • bili-sync:下载哔哩哔哩收藏夹视频到 NAS。

电子产品

年度电子产品是 OneKey Pro。

  • 雷鸟 V3 让我有点失望。我一直对 Meta 的 AI 眼镜很感兴趣,以为过了这么久,国产竞品会更成熟,结果挺拉的。

  • iPhone 13 mini 已经用了四年多,感觉还能再战几年。

  • OPPO Watch X2 倒是很满意。小米手环用腻了,换了一个屏幕更大的智能手表,用起来很顺手。它有全智能和轻智能两种模式,全智能是完整 Android,轻智能像大号手环,续航超强,健康功能也不错,还能提醒我高血压风险。

  • 以及 OneKey Pro 和 OneKey Classic 1S Pure。

影视

年度影视是《链锯人 总集篇》。

节奏比 TV 版好很多,不少拖沓部分被剪掉,整体观感提升明显。真希望鬼灭也能出个总集篇。

今年时间基本都花在 Folo 上了,所以影视数量很惨淡。

番剧:

  • 链锯人 剧场版:蕾塞篇(第二喜欢,蕾塞好女人)
  • 链锯人 总集篇
  • 鬼灭之刃:无限城篇 第一章 猗窝座再袭(专门去电影院看了。可能期望太高,有点失望;对猗窝座无感)
  • 章鱼噼的原罪(新番里最喜欢)
  • 永久的黄昏
  • 间谍过家家 第三季
  • 光逝去的夏天
  • 琉璃的宝石
  • 启示录酒店

电视剧:

  • 行尸走肉 1-11 季
  • 怪奇物语 1-4 季(在新加坡环球影城万圣节活动看到怪奇物语主题鬼屋,以为没看过,就补了,结果发现三年前已经看过……)
  • 黑镜 第七季
  • 爱,死亡和机器人 第四季
  • 无耻之徒 1-3 季
  • 长安的荔枝
  • 最后生还者 第二季

电影:

  • 浪浪山小妖怪
  • 哪吒之魔童闹海

图书与游戏

今年一本书都没看完,略过。

游戏和发小打了几个小时《双影奇境》,还没通关,还打了几天《王者荣耀》,没有其他的了。

其他值得一提的

健康

去年体检发现肝指标非常差,再这样下去可能要出大问题。之后被发小强制要求 10 点上床,作息规律很多,现在大概是 12 点睡、8 点起。但今年没体检,还不知道指标有没有改善。

哈哈出生后家里有女佣做饭,饮食更健康了。我还戒了碳水,把豆浆当主食,体重轻了 10 斤,但离标准体重还差 10 斤。运动依然几乎没有,不过每天出门遛娃,步数还是多了一点。

另一方面身体机能的衰退也越来越明显。主要体现在精力的下降和已知薄弱部位上。筋膜炎、颈椎病、膝盖疼痛、牙周炎等老毛病都有所加重,精力也大不如从前,今年开始习惯通过外力(咖啡因、牛磺酸)来维持工作效率,但也导致了依赖,尝试停止牛磺酸摄入时就会出现剧烈头痛,只能通过止痛药来缓解。

居住

家里多了哈哈之后变得异常拥挤,于是换了更大的房子,从新加坡女皇镇搬到偏远的裕廊西。因为位置更偏,空间更大,房租反而更便宜,自然环境也更好。旁边还有个 Jurong Lake Gardens 的超大国家公园,很适合遛娃。

旅游

发小怀孕中期在家憋得快抑郁了,而中期也是相对适合出门的阶段,于是抓紧安排了两个轻松的地方。

大理的主题是吃。吃了好多菌子,菌汤超级鲜美,也确实可能有危险。喝之前饭店还会取样,如果中毒要带着样本一起去医院。

在新加坡可没那么多好吃的。

洱海有一股让人平静的力量。之前跟发小私定终身就是在洱海上,再回来,已经是一家三口了。洱海也见证了我们从发小,到情侣,再到夫妻,再到父母的变化,我对发小的爱也随着时间沉淀越来越深沉。

巴厘岛则是彻底放空。找了个豪华度假区,天天游泳、晒太阳,让人短暂地忘记日常的加速。

以及新加坡环球影城万圣节惊魂夜。今年有四个主题鬼屋,怪奇物语、新加坡最凶灵之地、道诡异仙、泰国死亡低语者,非常可怕,全程在尖叫。

有趣的小事

NAS 坏了一块硬盘,自动强制只读了,但数据没丢。

过生日到处去薅羊毛。

看发小拼乐高。

2026 愿望

我对 2026 的愿望很朴素。希望身体别再用坏消息提醒我,希望能继续赚到钱,希望能早点拿到新加坡身份,让未来少一些不确定性。

上古神器 Beancount:Crypto 与 AI 时代的复式记账终极方案

2025年12月12日 16:58

记账

记账这件事,本质上是在回答三个问题:

  • 钱从哪来?
  • 钱现在在哪?
  • 钱最后去哪了?

听起来很简单,但只要你的金融活动稍微复杂一点,这三问立刻变成一道「多变量方程」:

  • 多个账户:银行卡、支付宝、信用卡、交易所、链上钱包……
  • 多种资产类型:储蓄、贷款、投资……
  • 多种货币:CNY、USD、BTC、各种山寨币……
  • 多个时间维度:分期、预支、借贷……

也正因为如此,我前后换过好几套方案。

2016–2021:网易有钱 - 当时需求很朴素:记人民币收入、储蓄、日常开销。它能自动同步银行流水、支付宝微信账单,对国内场景非常方便。

2021–2024:MoneyWiz - 出国后我需要多币种,并且主要使用英国的银行账户。MoneyWiz 在多币种、海外账户同步上很顺手。

2024:我开始记录加密资产,这时候情况开始非常复杂了,我不只想知道「总额涨了多少」,而是想要:每一笔买入/卖出的明细、不同资产的占比、每日的浮盈浮亏、更严谨的收益率计算。

MoneyWiz 对投资/加密资产支持很有限;我又试了有知有行,它能记录投资前后总额并算收益率,但无法记录到每笔明细,很多价格需要手动维护,浮盈浮亏也无法自动计算。

最后我接触到复式记账和 Beancount,彻底解决了我的问题。

我才意识到,很多时候我们觉得记账麻烦,并不是 UI 不够漂亮、交互不够顺滑,而是记账方法不够科学。当方法足够科学时,你甚至不需要 UI,一个 CLI + 几个纯文本文件 + 一套清晰语法,就能把财务世界描述得非常准确。


复式记账法

复式记账乍看很反直觉,但一旦掌握,会发现它非常强大和灵活,而且会迫使你用更清晰的方式理解金融活动。

五个桶(账户类型)

复式记账会把所有账户归到五类(可以理解成五个装豆子的桶):

  • 资产 Assets:现金、银行存款、有价证券、链上资产……
  • 负债 Liabilities:信用卡欠款、房贷、车贷……
  • 收入 Income:工资、奖金、利息……
  • 花费 Expenses:吃饭、购物、旅行、订阅……
  • 权益 Equity:调节/归档用的「底座」,例如期初余额、误差修正等

所有金融活动都是「桶之间的转移」

  • 收入 → 资产:发工资(收入桶减少,资产桶增加)
  • 资产 → 花费:买东西(资产桶减少,花费桶增加)
  • 负债 → 资产:借钱(负债桶增加,资产桶增加)
  • 资产 → 负债:还款(资产桶减少,负债桶减少)
  • 花费 → 资产:退货(花费桶减少,资产桶增加)
  • 资产 → 收入:投资亏损(资产桶减少,收入桶减少)

你会发现:按这种符号约定,收入账户的余额经常是负数。这不是 bug,而是记账视角不同。

可以这样想象:把「收入」想成一个装着你一生(过去和未来)劳动成果的桶。每次你拿到工资,其实是在从这个桶里取出一部分,放进你的资产里。你能取的越多,收入桶里的数值就越往负的方向走。

所以收入为负数并不表示「你欠了收入」,只是因为你在从这个桶取出资产。这也涉及到复式记账法的一个核心规则:一笔交易里,所有数字相加必须等于 0。这也是它检验正确性的关键:你少写一笔、写错一位数,系统就会当场报警。

在 Beancount 常见的符号约定下,你会看到类似这样的「会计恒等式」写法:

(收入 + 负债) + (资产 + 花费) = 0

你也可以把它理解为更传统的表达:你赚到的、借来的,最终要么变成你拥有的资产,要么被花掉。


Beancount

Beancount 是目前我用过最舒服、也最适合复杂财务世界的纯文本复式记账工具。

它不是复式记账软件的开创者,更早的代表是 Ledger。但 Beancount 在 Ledger 的概念上做了不少改良,也经历了时间的考验(2007 年开始)。

学习 Beancount 最直接的方法是看官方文档:Getting Started with BeancountBeancount Language Syntax,或者看这篇文章也很不错:Beancount —— 命令行复式簿记 | wzyboy’s blog

这篇博客我不会展开讲语法和使用细节,而是用几个例子让你快速感受 Beancount 的强大之处和为什么说它更适合 Crypto 和 AI 时代。

处理复杂交易

使用 Beancount 可以优雅处理复杂交易,以下例子涉及多对多,多账户 + 多人复杂交易,但是可以描述很清楚

2016-02-05 * "饭店" "和室友吃饭"
  Assets:Cash:Wallet                         -300.00 CNY ; 我用现金支付
  Assets:Receivables:Bob                     -200.00 CNY ; 室友帮我付的现金
  Expenses:Food:DiningOut                    +250.00 CNY ; AA 我的一半
  Assets:Receivables:Bob                     +250.00 CNY ; AA 室友的一半

这里同时发生了:

  • 你付了部分现金
  • 室友也付了部分现金
  • AA 分摊
  • 你对室友产生/抵消应收

最后只需要看 Assets:Receivables:Bob 余额就可以知道你欠室友多少钱,看 Expenses:Food:DiningOut 余额就可以知道每个月吃饭开销花了多少钱。

如果用「单式记账 + 备注」硬堆,后面基本一定算不清,而复式记账把结构直接写出来,之后统计就变得非常自然。

处理多币种交易

跨币种交易通常涉及汇率问题,Beancount 也能优雅处理

2016-02-10 * "商店" "买东西"
 Assets:Cash                                 -200.00 USD
 Liabilities:CMB:CreditCards                 -650.00 CNY @@ 100.00 USD
 Expenses:Clothing:Pants                     +150.00 USD
 Expenses:Clothing:Shoes                     +150.00 USD

这类「刷卡以人民币记账、但实际消费以美元计价」的场景,在跨境生活里非常常见。Beancount 的表达方式很直接:用一条记录把两边币种的关系和当时汇率绑定住,之后查询/报表都能基于它继续推导。

更适合 Crypto 交易

Beancount 不限制货币单位:USD、CNY、BTC、USDT、甚至任何你自定义的 token 都可以。再配合类似 SQL 的查询工具 bean-query,拿来做加密资产投资分析非常顺手。

举一个看起来很复杂,但记录很简洁的例子:

2025-07-23 price BTC 1 USD

2025-07-24 * "OKX" "买BTC"
  Assets:Crypto:USD            -150 USD
  Assets:Crypto:BTC            149 BTC  {1 USD}
  Expenses:TradingFee:Crypto      1 USD

2025-07-24 * "OKX" "买BTC"
  Assets:Crypto:USD             -60 USD
  Assets:Crypto:BTC             19 BTC  {3 USD}
  Expenses:TradingFee:Crypto      3 USD

2025-07-24 * "OKX" "卖BTC"
  Assets:Crypto:BTC           -198 BTC  {} @ 2 USD
  Assets:Crypto:USD             298 USD
  Income:Investment

2025-07-25 price BTC 4 USD

这个例子里包含了:

  • 多次买入,成本不同
  • 手续费
  • 卖出时自动匹配成本({} 让 Beancount 自己去找)
  • 市价(price)随时间变化

仅基于这些记录,Beancount 可以自动算出:

  • 总收益/已实现收益/未实现收益
  • 各类收益率(例如资金加权收益率 Modified Dietz、时间加权收益率 TWRR、甚至你自定义的任意收益计算)

换句话说:你只负责把事实写清楚,任何复杂计算都可以交给 Beancount。


我的 Beancount 实践

把账本当代码管理。

编辑器

我用 VSCode,配合 BeancountBeancount Formatter 和以下配置实现语法高亮、自动补全、错误检查、格式化等。

{
  "beancount.mainBeanFile": "main.bean",
  "beancount.runFavaOnActivate": false,
  "beancount.completePayeeNarration": true,
  "beancount.fixedCJKWidth": true,
  "beancount.inputMethods": ["pinyin"],
  "beancount.instantAlignment": false,
  "editor.formatOnSave": true
}

项目结构

Beancount 天然支持模块化:不同类型交易拆到不同文件,再 include 进主入口,维护成本非常低。

.
├── books
│   ├── 2025
│   │   ├── 2025.bean
│   │   ├── cn.bean
│   │   ├── crypto.bean
│   │   ├── daily.bean
│   │   ├── periodic.bean
│   │   ├── prices.bean
│   └── books.bean
├── scripts
│   ├── importer-dbs-credit.js
│   ├── importer-dbs-debit.js
│   ├── importer-okx.js
├── accounts.bean
├── commodity.bean
├── config.pbtxt
├── dashboards.yaml
├── main.bean

main.bean 是入口文件,集中放配置、插件和 include:

option "title" "DIYgod's Book"
option "operating_currency" "USD"
option "booking_method" "LIFO"
option "account_rounding" "Equity:Rounding-Error"

plugin "beancount_periodic.recur"
plugin "beancount_periodic.split"

2024-01-01 custom "fava-option" "auto-reload" "true"
2024-01-01 custom "fava-option" "default_page" "income_statement/?conversion=units&interval=day"

2024-01-01 custom "fava-extension" "fava_portfolio_returns" "{
  'beangrow_config': 'config.pbtxt',
}"

2024-01-01 custom "fava-extension" "fava_dashboards" "{
  'config': 'dashboards.yaml'
}"

include "accounts.bean"
include "books/books.bean"
include "commodity.bean"

accounts.bean 定义所有账户(略去部分):

; Expenses 费用 —— 外出就餐购物旅行等
1995-06-09 open Expenses:Food:Restaurant
; ... 省略若干行 ...

; Income 收入 —— 工资奖金等
2020-04-01 open Income:Salary
; ... 省略若干行 ...

; Assets 资产 —— 现金银行存款有价证券等
2024-01-01 open Assets:Bank:DBS SGD
2024-01-01 open Assets:Crypto:OKX:BTC BTC
; ... 省略若干行 ...

; Liabilities 负债 —— 信用卡房贷车贷等
2024-01-01 open Liabilities:Bank:DBS SGD
; ... 省略若干行 ...

; Equity 权益 —— 用于存放某个时间段开始前已有的豆子
2024-01-01 open Equity:Opening-Balances
2024-01-01 open Equity:Rounding-Error

commodity.bean 定义货币/资产,并用 price 指定价格来源(后面会讲自动更新):

2025-01-01 commodity USDT
  name: "Tether"

2025-01-01 commodity BTC
  name: "Bitcoin"
  price: "USD:coinbase/BTC-USD"

2025-01-01 commodity USD
  name: "United States Dollar"

2025-01-01 commodity QQQ
  name: "NASDAQ-100 Index"
  price: "USD:alphavantage/price:QQQ:USD"

books/2025/prices.bean 用于记录每日价格(自动生成),让持仓浮盈浮亏可被计算:

2025-01-01 price BTC 93346.48 USD
2025-01-02 price BTC 94384.76 USD
; ... 省略若干行 ...

几种常见的 Crypto 交易

下面这些例子来自我的实际习惯:交易不是手填的,而是通过 scripts/importer-okx.js 从 OKX 交易记录自动生成。

稳定币现货交易

1)买入:用 USDT 买 BTC + 手续费

2025-11-14 * "OKX" "Buy BTC with USDT"
  Assets:Crypto:OKX:USDT                       -3022.493 USDT
  Assets:Crypto:OKX:BTC                     0.0302113288 BTC {100000 USDT}
  Expenses:TradingFee:Crypto:OKX

这里我把手续费留空,让 Beancount 自动补齐(利用「每笔交易加起来为 0」的规则)。

但如果你的 BTC 可能来自 USDT、USDC 等不同稳定币,之后统计收益会涉及汇率与币种转换。为了让分析更统一,我常用一个过渡账户把成本统一成 USD:

2025-11-14 * "OKX" "Buy BTC with USDT"
  Assets:Crypto:OKX:USDT                       -3022.493 USDT
  Equity:Exchange                               3022.493 USDT
  Equity:Exchange                              -3022.493 USDT @ 1 USD
  Assets:Crypto:OKX:BTC                     0.0302113288 BTC {100000 USD}
  Expenses:TradingFee:Crypto:OKX

Equity:Exchange 的作用很像「统一换算层」:把不同稳定币交易都折算成 USD 口径,后续报表会非常舒服。

2)卖出:卖 BTC 得 USDC + 自动算收益

2025-09-18 * "OKX" "Sell BTC for USDC"
  Assets:Crypto:OKX:BTC                        -0.100011 BTC {} @ 117600 USD
  Equity:Exchange                         11756.00101788 USD
  Equity:Exchange                        -11756.00101788 USD @ 1 USDC
  Assets:Crypto:OKX:USDC                  11756.00101788 USDC
  Expenses:TradingFee:Crypto:OKX             -5.29258212 USD
  Income:Investment
  • {} @ 117600 USD:成本价由 Beancount 自动从历史记录里匹配,卖出价是 117600 USD
  • 多出来的部分进 Income:Investment:收益就这样自然出来了

如果你不需要统一换算,也可以简化成:

2025-09-18 * "OKX" "Sell BTC for USDC"
  Assets:Crypto:OKX:BTC                        -0.100011 BTC {} @ 117600 USDC
  Assets:Crypto:OKX:USDC                  11756.00101788 USDC
  Expenses:TradingFee:Crypto:OKX             -5.29258212 USDC
  Income:Investment

币币现货交易

用 BTC 买 ETH

2025-08-22 * "OKX" "Buy ETH with BTC"
  Assets:Crypto:OKX:BTC            -0.025828016279999998 BTC {} @ 112480.295 USD
  Equity:Exchange                       2,905.1428904392 USD
  Equity:Exchange                      -2,905.1428904392 USD
  Assets:Crypto:OKX:ETH                     0.6820274505 ETH {4224.16 USD}
  Income:Investment
  Expenses:TradingFee:Crypto:OKX            0.0001705495 ETH {4224.16 USD}

币币交易的成本/收益更容易乱,但把过程统一到 USD 口径后,就会变得非常清晰:用 BTC 换到的 ETH,有明确的 USD 成本;手续费和之后的收益也能被正确计入。

U 本位合约

U 本位合约在交易所内部不是真的卖出 BTC,但为了把盈亏落到资产变化上,我用一个「借币卖出/买回」近似模型来表达:

  • 开仓做空:等价于「借 BTC 卖出,换成 USDT」
  • 平仓:等价于「用 USDT 买回 BTC 归还」
  • 盈亏会自然体现在最后多出来(或少了)的部分上
2025-12-01 * "OKX" "合约 BTC-USDT 做空开仓"
  Assets:Crypto:OKX:Futures:BTC                      -10 BTC @ 100000 USD
  Equity:Exchange                                1000000 USD
  Equity:Exchange                               -1000000 USD @ 1 USDT
  Assets:Crypto:OKX:Futures:USDT                 1000000 USDT

2025-12-10 * "OKX" "合约 BTC-USDT 做空平仓"
  Assets:Crypto:OKX:Futures:USDT                 1000000 USDT
  Equity:Exchange                                1000000 USDT
  Equity:Exchange                               -1000000 USDT @ 1 USD
  Assets:Crypto:OKX:Futures:BTC                      -10 BTC {80000 USD}
  Income:Investment

真实合约还会涉及保证金、资金费、强平、结算币种差异等。更严谨的做法可以继续拆分账户模型,但这套写法已经能在大多数场景里把结果记清楚、算出来。

币本位合约

币本位合约更复杂:仓位通常是「USD 计价的 BTC 价格敞口」,而不是固定数量 BTC。想按每日价格计算浮盈浮亏会很复杂,需要「双账模型」(事实账 + 估值账)。

我目前选择先把它简化成:平仓时手动算出以 BTC 为单位的收益,只记录最终 BTC 变化,只是这样会牺牲当前持仓未实现盈利的数据:

2025-11-21 * "OKX" "合约 BTC-USD 1.0"
  Income:Investment
  Assets:Crypto:OKX:BTC                0.007433571994392 BTC {85400 USD}

自动化脚本 + AI 记账

通过自动化脚本和 AI,可以极大简化记账工作量,让「记账」变成「对账」。

自动生成交易记录

Beancount 是纯文本,这意味着它天生适合自动化生成记录和更新资产价格。

社区里有很多导入工具,比如:beangulpsmart_importer

但我自己用得最多的还是自写脚本(例如 OKX 的 scripts/importer-okx.js、DBS 的 scripts/importer-dbs.js)。原理都类似:从接口或者网页获取交易数据 → 转成 Beancount 文本。

对于一些没有接口、甚至没有账单导出的账户(比如我现在用的 Trust),直接截图丢给 AI 识别也很省心:

自动更新价格

Beancount 本身是离线系统,不会自动从互联网拉价格,所以我每天会把最新的币价/股价写进 books/2025/prices.bean,让报表能计算当日估值与未实现盈利。

官方提供了开箱即用的 beanprice,支持 Coinbase、Coinmarketcap、Alphavantage 等来源,一个命令就能更新:

bean-price -i --update-rate daily --no-cache main.bean >> books/2025/prices.bean

AI 分析与可视化

很多人把记账停在「记下来」就结束了,但真正有价值又容易被遗忘的是记录之后如何分析。

Beancount 在分析和可视化能力上非常夸张:

  • 可以装插件的 Web 界面:Fava
  • 类 SQL 查询语言:BQL(Beancount Query Language)

先看一些我常用的效果:

投资品的价格走势图(Fava 自带):

不同投资品的收益曲线,以及各种加权收益率(插件 Fava Portfolio Returns):

特定资产的变化曲线、分布占比、列表(插件 Fava Dashboards + 自定义 BQL):

更爽的是:现在有了 vibe coding,时代变了。 现在甚至不需要先学会 BQL 怎么写——可以直接让 AI 写。

比如我会这样提需求:

利用 Fava Dashboards 插件,在 dashboards.yaml 编写 BQL 实现一个展示我所有加密货币持仓随时间变化的曲线图。

曾经要折腾好久的个性化财务分析,现在可能就是一句话。


结语

我用了 Beancount 一年,几乎全部需求都很好得到了满足,非常满意,也在公司内部做过分享。这篇博客就是把分享内容整理成文字,希望能把它推荐给更多人。

我也承认,Beancount 的门槛对大多数人来说不低——它更像「写账本的编程语言」,而不是一个点点点的 App。 但我同样相信:随着 AI 发展,这些门槛会越来越低。未来每个人都可以用更低成本驾驭这种强大的工具,实现对自己财务的全面掌控。

如果你也正在经历:

  • 多币种生活
  • 投资品越来越多
  • 账越记越乱
  • 分析越来越复杂

那我建议你至少花半天时间试试 Beancount。 它可能会让你第一次觉得:原来我终于能把钱讲清楚了。

一个六岁开源项目的崩溃与新生

2024年3月11日 03:29

我有一个维护了六年的开源项目—— RSSHub,它正在面临崩溃

背景

表面上,它有接近 30k Stars、900 多 Contributors、每月 3 亿多次请求和数不清的用户、每月几十刀的赞助、有源源不断的 issue 和 pr、代码几乎每天更新,非常健康和充满活力,但在不可见的地方,持续数年高昂的维护时间成本、每月一千多刀的服务器费用、每天重复繁琐且逐渐积累的维护工作,都让它在崩溃的边缘反复横跳

项目是六年前开发的,不少当时以 Next Generation 为口号的时髦 Node.js 技术栈和依赖库已经成为时代眼泪,现在看非常陈旧,很多现在流行的新技术没法应用,比如 JSX、TypeScript、Serverless 等;它的架构也非常不合理,每个路由的信息散落在多个地方,开发或者变更一个路由需要多处修改,一个地方去注册路由,一个地方去编写路由脚本,一个地方去编写 Radar 规则,一个地方去编写文档…这增加了很多工作量,也很容易出错,之前路由少的时候并不是个问题,但现在已经变得难以忍受

在如此糟糕的基础架构下能保持现状已经是竭尽全力,开发新功能更是无本之木,只会增加以后更新的难度,所以我有时候脑子蹦出的新奇想法也很难实现

要解决这些问题,唯一的办法是使用现代化的框架和新设计的架构来重写内核,但随着路由越来越多,改造成本也越来越高,每个基础改动可能都需要多达数月的工作量,所以虽然问题越来越严重,但秉承着又不是不能用的原则一拖再拖

但这又是不得不做的事情,所以我抽空花了几个月的时间重新设计和重写了它

技术栈更新

koa -> Hono

第一步也是最基础和难度最大的是换掉之前使用的 Web 框架 koa,作为六年前流行的下一代 Web 框架,作者早就弃坑了,调研之后决定换用对 JSX、TypeScript、Serverless 支持最好的 Hono

它们的 API 差异很大,需要重写所有中间件和替换所有路由中使用的 koa API

主要改动: https://github.com/DIYgod/RSSHub/pull/14295

image

Hono 作者也很喜欢这个改造

https://twitter.com/yusukebe/status/1762801106340782222

JavaScript -> TypeScript

改用 TypeScript 可以避免很多类型问题和低级错误,最重要的是可以保证数百名贡献者保持一致难以出错和后续贡献的路由代码质量不至于太糟糕

主要改动:

image

https://twitter.com/DIYgod/status/1764360942035312879

CommonJS -> ESM

ESM 是几年前一些 Node.js 核心开发者强推的规范,它有一些优点,但最多的是与之前 CommonJS 不兼容带来的生态割裂和功能简化带来的诟病

经过这几年的发展,现在可以说大部分场景勉强可用了,tsx 也为 CommonJS 和 ESM 混用的场景提供了支持

虽然已经尽了最大努力,但还是有一些 CommonJS 代码暂时难以迁移,导致现在只能使用 tsx 运行,与一些 Serverless 比如 Vercel 没法兼容,但也有机会后续慢慢解决

主要改动:

image

image

art-template -> JSX

art-template 是一个支持 koa 的模板引擎,记得六年前还有一个更流行的模板引擎,但是不记得名字了,选用 art-template 是因为那个更流行的我当时没看懂,这个很简单

Hono 自带了 JSX 支持,JSX 就不用多介绍了,根正苗红的 JavaScript 的语法扩展,等同于用 React

主要改动:

Jest -> Vitest

Jest 是曾经流行的测试框架,但是在 ESM 时代到来之后就越来越不行了,对 ESM 的支持一直是实现性「experimental support」,现在更流行的是 Vitest 了

主要改动: https://github.com/DIYgod/RSSHub/commit/38e42156a0622a2cd09f328d2d60623813b8df28

Got -> ?

目前使用的 Got 也已经是不积极维护的状态了,也没有找到好的替代品,后续也许会换成原生 Fetch 或者自封装的 Fetch,还没有动手

新路由标准

我自己能力还是不够的,在与社区开发者们讨论的过程中学习和改进了很多,过程很有意思:https://github.com/DIYgod/RSSHub/issues/14685

主要改动: https://github.com/DIYgod/RSSHub/pull/14718

image

历史

新标准主要为了解决路由信息过于分散的问题,这次应该算第三版

第一版来自 RSSHub 开发阶段,当时没有预见到路由数量会有这么多,所以几乎没什么规划,所有路由在同一个文件中注册,然后再去增加路由脚本和文档,后来这个文件越来越大,很容易冲突,另外所有路由脚本都会在启动阶段被加载,程序性能越来越差

第二版来自 NeverBehave 维护的时期,引入了命名空间,切割了 router.js、radar.js,同命名空间的路由集中在了一个同文件夹中和一个或多个 Markdown 文档中,还实现了懒加载,极大提升了可维护性和性能,但还是会分散在多个文件中,不同文件的信息也容易出现不一致导致错误

现在

本次把路由文件分为了两类,namespace.ts 和任意名字的路由文件

namespace.ts 会通过导出名为 namespace 的对象来定义命名空间的信息

import type { Namespace } from "@/types";

export const namespace: Namespace = {
  // ...
};

namespace 包含的字段通过 TypeScript 限制为

interface Namespace {
  name: string;
  url?: string;
  categories?: string[];
  description?: string;
}

这些信息会经过编译后被文档和 RSSHub Radar 利用

路由文件会通过导出名为 route 的对象来定义路由的信息

import { Route } from "@/types";

export const route: Route = {
  // ...
};

route 包含的字段通过 TypeScript 限制为

interface Route {
  path: string | string[];
  name: string;
  url?: string;
  maintainers: string[];
  handler: (ctx: Context) => Promise<Data> | Data;
  example: string;
  parameters?: Record<string, string>;
  description?: string;
  categories?: string[];

  features: {
    requireConfig?: string[] | false;
    requirePuppeteer?: boolean;
    antiCrawler?: boolean;
    supportRadar?: boolean;
    supportBT?: boolean;
    supportPodcast?: boolean;
    supportScihub?: boolean;
  };
  radar?: {
    source: string[];
    target?: string;
  };
}

之前 route.js mantainer.js radar.js 和文档的信息都被集中在这一个文件中,减少了多处定义也减少了出错的可能

实现

实现逻辑就是开发环境通过遍历整个 route 文件夹,找到所有 namespace.ts 和路由文件,读取信息,加载路由,在生成环境使用提前编译好的路径列表来避免遍历和不必要的加载过程,代码在:https://github.com/DIYgod/RSSHub/blob/master/lib/registry.ts

文档也是通过遍历 route 文件夹,找到所有需要的信息然后合成一系列的 Markdown 文件,不再需要手动维护,代码在:https://github.com/DIYgod/RSSHub/blob/master/scripts/workflow/build-routes.ts

当然使用之前路由标准开发的路由都需要迁移到新标准而不是直接放弃掉,已经通过脚本批量抓取整理信息后做了替换,但特别是文档比较混乱也有很多错误,所以抓取的信息也有很多错误,只能在后续逐渐人工修改了

未来

通过这一系列改进,RSSHub 终于能够扔掉历史包袱,安心开发新功能了,这里列出我积累的一些想法抛砖引玉:

  • 既然 RSSHub 是一个数据集合,用途不一定只有 RSS,JSON 输出功能可以做一些增强,作为通用的 RESTful API 来使用,比如可以提供获取下一页接口或者输出类似 Twitter 关注数的非 feed 数据
  • 用户系统和用户自定义配置,生成自己的私有订阅地址 #14706
  • 路由错误通知和健康度检测 #14712
  • 与 RSS3 节点的联动和加密货币收益共享 https://twitter.com/rss3_/status/1731822029199094012
  • AI 翻译和摘要
  • 更详细的实例数据分析及反向推导自动推荐的 Radar 规则
  • 与本地浏览器或客户端绑定的 RSSHub 实例,有希望真正解决反爬难题

最后,开源是一件很昂贵的事情,RSSHub 能活到现在离不开这些开发者的帮助

以及这些赞助的好心人

如果 RSSHub 正在帮助你,也希望你可以积极参与进来,为信息自由的未来贡献一份自己的微小力量

和帕鲁生活在一起的两周

2024年2月19日 03:20

宝可梦幻想

image

哪个小男孩没幻想过生活在这样一个充满宝可梦伙伴的世界,它们能听懂且愿意倾听我们说话,有着不同的特点和强大的技能,而且都很可爱,我们可以跟它们睡觉吃饭、并肩作战探索世界,永远不再孤独,不管是谁都可以找到最适合自己的宝可梦

这一切早就可以发生,但这一切终究没有发生,宝可梦复杂的版权问题和躺平在功劳簿上的版权公司 Game Freak 让我们的幻想一直也仅仅是幻想

当然也有人说最近的几个新作阿尔宙斯、朱紫比之前好多了,“虽然好多了,但只是臭味稀薄一点的狗屎罢了”,剑盾、阿尔宙斯、朱紫、Pokemon Sleep 游戏时长并不短的宝可梦玩家 DIYgod 义愤填膺地说道

帕鲁实现

image

但只要幻想一直存在,变成现实也只是时间问题,横空出世的幻兽帕鲁给所有宝可梦粉丝带来了一个一本满足的平行幻想世界,终于苦于没有 Windows 的我也专门买了一台 ROG Ally 来沉迷其中

游戏中与帕鲁的互动是前无仅有的,衣食住行战斗探索的过程都与帕鲁深度融入,真的是和帕鲁生活在一起

从刚落地进入初始台地互动就开始了,我们的第一个落脚点不是自己造的,而是跟捣蛋猫一起造的

image

image

再到后来帕鲁包办我们的一切后勤,我们的每一顿大餐,都需要经历播种、浇水、收割、搬运、存储、烹饪,分别由有着不同特长和分工的帕鲁来完成,会制冷的企丸丸去运行冰箱,会喷火的燎火鹿去烹饪

image

在我们外出探索时,过河骑滑水蛇,赶路骑云海鹿,翻山越岭骑烽歌龙

image

在我们战斗时,举着火绒狐充当、喷火器,骑着暴电熊扫射机关枪,甚至虽然不太人道,可以把电棘鼠当作手榴弹扔出去,同时据点里的焰煌在帮我们炼钢造枪,阿努比斯在帮我们手搓子弹

image

从徒手撸树到机关枪横着走,从无人问津到人满为患,在不知不觉的建造中我已经在帕鲁世界中倾注了太多的感情

从饿着肚子的第一天

Pal   6_2_2024 下午 9_54_32

到现在

Pal   19_2_2024 上午 2_45_40

Pal   18_2_2024 上午 12_42_29

一些额外的探索

数据展示

image

毕竟是单机游戏,数据都在本地,可以很方便地做处理

有一个开源项目可以把存档文件解析成 JSON 方便第三方开发和展示 https://github.com/cheahjs/palworld-save-tools

还有一个开源项目可以把 JSON 再生成一个前端页面 https://github.com/zaigie/palworld-server-tool

于是我把存档同步到 NAS 上,再在 NAS 上部署一个展示页面,就可以很方便地查看我的帕鲁们了 https://pal.diygod.me

模组

帕鲁有很丰富的社区模组,比如把皮皮鸡变成坤或者把帕鲁变成宝可梦什么的,还有一些可以在不影响游戏平衡的前提下极大提升游戏体验

我用的主要来自一个模组社区 Nexus Mods,他们有一个很方便的模组安装和管理工具 Vortex Mod Manager,以下是我正在使用的模组

  • Basic MiniMap:增加一个不知道为什么官方没做的感觉很基础的小地图功能,但是记得到设置里把旋转关掉

  • Better Night Light:晚上太眼瞎了,啥都看不见,像得了夜盲症一样,用这个模组可以变亮一些,还改了一个绝美的星空

    Pal   18_2_2024 下午 8_05_05

  • Golden Statue of Power:把力量石像变得金灿灿的更有质感

    Pal   18_2_2024 下午 9_06_28

  • Play as ZoeHide All Human Hair All female Head All HeadEquip:隐藏掉原有的装扮,自己变成老婆佐伊

  • Paimon Replace DaeDream:把寐魔变成派蒙

    Pal   19_2_2024 上午 2_09_33

  • Pal Analyzer:捕捉前就可以看到帕鲁的属性和特质,掌机需要到设置里改下显示为 always on

    Pal   18_2_2024 下午 7_45_54

  • Stuck Pal Rescuer:救命模组,据点里的帕鲁经常被 bug 卡住然后饿死,这个模组可以自动检测帕鲁是否卡住并重置

  • FSR3 支持:官方不支持 A 卡的帧生成,用上这个 Roy Ally 可以在中等画质从 40 多帧提升到 70 多帧

Twitter 对开源项目发起 DDoS 攻击

2024年1月28日 18:24

背景

Twitter 被马斯克收购后,从去年 8 月开始,他们对开源第三方集成和第三方客户端进行了一系列明里暗里的打压和攻击,这样做是为了阻止用户通过非官方客户端访问和使用 Twitter,来增加公司的广告和会员营收

而开源社区中以 Nitter 和 RSSHub 为代表的开源项目并没有放弃向信息自由的努力,通过众多聪明的开源开发者们想出来的一个个奇妙操作(issue),在一轮轮封锁和反封锁的对抗中短暂占了上风,其中最流行的做法是通过 Android 客户端使用的接口功能生成临时账号(细节

经过

在两天前(1月26日),许多 Nitter 实例的运行者和开发者报告称,他们正在使用的接口已被封锁。与此同时,他们的实例也开始遭受报复性 DDoS 攻击

image

image

起初,我并没有太在意这件事情。毕竟,谁会相信 Twitter 官方会做出如此令人不耻且自降身段的 DDoS 行为呢?我对此深感怀疑

然而,昨天当我打开 RSSHub 的 GitHub 仓库时,却意外地发现了以下内容

image

最近一个月的请求数达到了 4.5 亿,比正常水平高出 50%(正常水平仅为 3 亿多)

然后登录 Cloudflare 查看日志

image

自从 26 日 0 点(当天官方接口被封锁和 Nitter 遭受 DDoS 攻击)以来,RSSHub也一直遭受大规模的 DDoS 攻击。最近两天,请求量是平时的 170 多倍,每秒约 1 千次请求

尽管数量看起来可怕,但 Cloudflare 出色的缓存功能已经成功缓存了超过 99% 的 DDoS 请求,甚至没有触发报警

image

RSSHub 的负载均衡和自动扩容功能非常完善,没有受到太大的压力

https://twitter.com/DIYgod/status/1745090590419619865

🤣 就这样一直没有发现

进一步分析发现,所有请求都来自 IP 地址为 139.255.221.98 的设备。这些请求都是针对 /twitter/keyword 路由,并且后面跟着一串不同且无意义的参数

image

我很清楚为什么只针对 keyword 路由,尽管代码中没有明确表达,但根据我的使用经验,此路由使用的搜索接口受到最严格的访问频率限制,通过攻击该路由可以实现最大化效果。由此可以推断出 DDoS 攻击者也对 Twitter 的接口非常熟悉

虽然无法直接证明是官方人员所为,但各种无法解释的“巧合”已经清楚地表明了事情的真相,马斯克的简单粗暴行事风格也正在深刻影响这家公司

影响

进一步封锁 API 和进行 DDoS 攻击这两个操作可以说非常有效

Nitter 开发者 zedeus 表示 Nitter 已死

image

Twitter Monitor 开发者 MANKA 表示不愿意再浪费时间

https://twitter.com/manka_takami/status/1751450519829418342

nitter-status 开发者更是直接放出了告别页面

image

看起来就到此为止了吗?不,这远非终点。自由无法被阻挡,我们还有很多事情可以做

image

29日 更新:RSSHub 已经恢复

https://twitter.com/DIYgod/status/1751941869616148806

如何优雅编译一个 Markdown 文档

2024年1月18日 20:50

Markdown 是一种广泛使用的轻量级标记语言,允许人们使用易读易写的纯文本格式编写文档,也是 xLog 主要使用的文章格式,本文就以 xLog Flavored Markdown 为例来说明如何优雅地解析一个 Markdown 文档

架构

解析过程可以用这样一个架构来表示:

flowchart TB subgraph input Markdown end subgraph unified subgraph remark Markdown:::inputClass --string--> remark-parse:::remarkClass --mdast--> remarkPlugins[remark plugins]:::remarkClass remarkPlugins --mdast--> remark-rehype:::remarkClass & mdast-util-toc:::remarkClass end subgraph rehype remark-rehype --hast--> rehypePlugins[rehype plugins]:::rehypeClass rehypePlugins --hast--> hast-util-to-text:::rehypeClass & hast-util-to-html:::rehypeClass & hast-util-to-jsx-runtime:::rehypeClass end rehypePlugins --hast--> unist-util-visit:::rehypeClass end subgraph output mdast-util-toc --tocResult--> TOC:::inputClass hast-util-to-text --string--> plainText[Plain Text]:::inputClass hast-util-to-html --string--> HTML:::inputClass hast-util-to-jsx-runtime --JSX.Element--> ReactElement[React Element]:::inputClass unist-util-visit --custom--> Metadata:::inputClass end style input fill:#bbf7d0,stroke:#4ade80,color:#15803d classDef inputClass fill:#22c55e,stroke:#16a34a style output fill:#bbf7d0,stroke:#4ade80,color:#15803d style unified fill:#bfdbfe,stroke:#60a5fa,color:#1d4ed8 style remark fill:#fecaca,stroke:#f87171,color:#b91c1c classDef remarkClass fill:#f87171,stroke:#dc2626 style rehype fill:#fef08a,stroke:##facc15,color:#a16207 classDef rehypeClass fill:#facc15,stroke:#ca8a04

关键概念:

  • unified:通过语法树和插件来解析、检查、转换和序列化内容的库
  • remark:unified 的生态项目之一,由插件驱动的 Markdown 处理库
  • rehype:unified 的生态项目之一,由插件驱动的 HTML 处理库
  • mdast:remark 使用的用于表示 Markdown 的抽象语法树规范
  • hast:rehype 使用的用于表示 HTML 的抽象语法树规范

简单来说就是把 Markdown 文档交给一个 unified 生态的解析器解析成 unified 可识别的语法树,再通过一系列 unified 生态的插件转换为需要的内容,再通过一系列 unified 生态的工具库输出为需要的格式,下面就从 解析、转换、输出 这三个步骤来分别说明

解析 Parse

flowchart TB subgraph input Markdown end subgraph unified subgraph remark Markdown:::inputClass --string--> remark-parse:::remarkClass end end style input fill:#bbf7d0,stroke:#4ade80,color:#15803d classDef inputClass fill:#22c55e,stroke:#16a34a style unified fill:#bfdbfe,stroke:#60a5fa,color:#1d4ed8 style remark fill:#fecaca,stroke:#f87171,color:#b91c1c classDef remarkClass fill:#f87171,stroke:#dc2626

无论输入是 Markdown、HTML 还是纯文本,都需要将其解析为可操作的格式。这种格式被称为语法树。规范(例如 mdast)定义了这样一个语法树的外观。处理器(如 mdast 的 remark)负责创建它们。

最简单的一步,我们需要解析的是 Markdown,所以这里就应该使用 remark-parse 来把 Markdown 文档编译成 mdast 格式的语法树

对应 xLog Flavored Markdown 中的

const processor = unified().use(remarkParse)

const file = new VFile(content)
const mdastTree = processor.parse(file)

转换 Transform

flowchart TB subgraph remark remark-parse:::remarkClass --mdast--> remarkPlugins[remark plugins]:::remarkClass remarkPlugins --mdast--> remark-rehype:::remarkClass end subgraph rehype remark-rehype --hast--> rehypePlugins[rehype plugins]:::rehypeClass end style remark fill:#fecaca,stroke:#f87171,color:#b91c1c classDef remarkClass fill:#f87171,stroke:#dc2626 style rehype fill:#fef08a,stroke:##facc15,color:#a16207 classDef rehypeClass fill:#facc15,stroke:#ca8a04

这就是魔法发生的地方。用户组合插件以及它们运行的顺序。插件在此阶段插入并转换和检查它们获得的格式。

这一步最为关键,不仅包含了从 Markdown 到 HTML 的转换,还包含我们想在编译过程中夹带的私货,比如增加一些非标准的语法糖、清理 HTML 防止 XSS、增加语法高亮、嵌入自定义组件等

unified 的插件非常多,更新也比较及时,基本需求几乎都能满足,对于不能满足的特定需求,自己编写转换脚本也很容易实现

里面有一个特殊的插件是 remark-rehype,它会把 mdast 语法树转为 hast 语法树,所以在它之前必须使用处理 Markdown 的 remark 插件,在它之后必须使用处理 HTML 的 rehype 插件

xLog Flavored Markdown 中就加入了非常多的转换插件

const processor = unified()
  .use(remarkParse)
  .use(remarkGithubAlerts)
  .use(remarkBreaks)
  .use(remarkFrontmatter, ["yaml"])
  .use(remarkGfm, {
    singleTilde: false,
  })
  .use(remarkDirective)
  .use(remarkDirectiveRehype)
  .use(remarkCalloutDirectives)
  .use(remarkYoutube)
  .use(remarkMath, {
    singleDollarTextMath: false,
  })
  .use(remarkPangu)
  .use(emoji)
  .use(remarkRehype, { allowDangerousHtml: true })
  .use(rehypeRaw)
  .use(rehypeIpfs)
  .use(rehypeSlug)
  .use(rehypeAutolinkHeadings, {
    behavior: "append",
    properties: {
      className: "xlog-anchor",
      ariaHidden: true,
      tabIndex: -1,
    },
    content(node) {
      return [
        {
          type: "text",
          value: "#",
        },
      ]
    },
  })
  .use(rehypeSanitize, strictMode ? undefined : sanitizeScheme)
  .use(rehypeTable)
  .use(rehypeExternalLink)
  .use(rehypeMermaid)
  .use(rehypeWrapCode)
  .use(rehypeInferDescriptionMeta)
  .use(rehypeEmbed, {
    transformers,
  })
  .use(rehypeRemoveH1)
  .use(rehypePrism, {
    ignoreMissing: true,
    showLineNumbers: true,
  })
  .use(rehypeKatex, {
    strict: false,
  })
  .use(rehypeMention)

const hastTree = pipeline.runSync(mdastTree, file)

下面介绍部分用到的插件

  • remarkGithubAlerts:增加 GitHub 风格的 Alerts 语法,演示
  • remarkBreaks:不再需要空一行才能被识别为新的自然段
  • remarkFrontmatter:支持前置内容(YAML、TOML 等)
  • remarkGfm:支持非标准的 GitHub 在原版 Markdown 语法上扩展的一系列语法(但其实这系列语法已经被非常广泛使用,成为了事实意义上的标准)
  • remarkDirective remarkDirectiveRehyp:支持非标准的 Markdown 通用指令提案
  • remarkMath rehypeKatex:支持复杂的数学公式,演示
  • rehypeRaw:支持 Markdown 中夹杂的自定义 HTML
  • rehypeIpfs:自定义插件,为图片、音频、视频支持 ipfs:// 协议的地址
  • rehypeSlug:为标题添加 id
  • rehypeAutolinkHeadings:为标题添加指向自身的链接 rel = "noopener noreferrer"
  • rehypeSanitize:清理 HTML,用于确保 HTML 安全避免 XSS 攻击
  • rehypeExternalLink:自定义插件,给外部链接添加 target="_blank"rel="noopener noreferrer"
  • rehypeMermaid:自定义插件,渲染绘图和制表工具 Mermaid,本文的架构图就是通过 Mermaid 渲染的
  • rehypeInferDescriptionMeta:用于自动生成文档的描述
  • rehypeEmbed:自定义插件,用于根据链接自动嵌入 YouTube、Twitter、GitHub 等卡片
  • rehypeRemoveH1:自定义插件,用于把 h1 转为 h2
  • rehypePrism:支持语法高亮
  • rehypeMention:自定义插件,支持 @DIYgod 这样艾特其他 xLog 用户

输出 Stringify

flowchart TB subgraph unified subgraph remark remarkPlugins[remark plugins]:::remarkClass --mdast--> mdast-util-toc:::remarkClass end subgraph rehype rehypePlugins[rehype plugins]:::rehypeClass rehypePlugins --hast--> hast-util-to-text:::rehypeClass & hast-util-to-html:::rehypeClass & hast-util-to-jsx-runtime:::rehypeClass end rehypePlugins --hast--> unist-util-visit:::rehypeClass end subgraph output mdast-util-toc --tocResult--> TOC:::inputClass hast-util-to-text --string--> plainText[Plain Text]:::inputClass hast-util-to-html --string--> HTML:::inputClass hast-util-to-jsx-runtime --JSX.Element--> ReactElement[React Element]:::inputClass unist-util-visit --custom--> Metadata:::inputClass end classDef inputClass fill:#22c55e,stroke:#16a34a style output fill:#bbf7d0,stroke:#4ade80,color:#15803d style unified fill:#bfdbfe,stroke:#60a5fa,color:#1d4ed8 style remark fill:#fecaca,stroke:#f87171,color:#b91c1c classDef remarkClass fill:#f87171,stroke:#dc2626 style rehype fill:#fef08a,stroke:##facc15,color:#a16207 classDef rehypeClass fill:#facc15,stroke:#ca8a04

最后一步是将(调整后的)格式转换为 Markdown、HTML 或纯文本(可能与输入格式不同!)

unified 的工具库也很多,可以输出各种我们需要的格式

比如 xLog 需要在文章右侧展示自动生成的目录、需要输出纯文本来计算预估阅读时间和生成 AI 摘要、需要生成 HTML 来给 RSS 使用、需要生成 React Element 来渲染到页面、需要提取文章的图片和描述来展示文章卡片,就分别使用了 mdast-util-toc、hast-util-to-text、hast-util-to-html、hast-util-to-jsx-runtime、unist-util-visit 这些工具

对应 xLog Flavored Markdown 中的

{
  toToc: () =>
    mdastTree &&
    toc(mdastTree, {
      tight: true,
      ordered: true,
    }),
  toHTML: () => hastTree && toHtml(hastTree),
  toElement: () =>
    hastTree &&
    toJsxRuntime(hastTree, {
      Fragment,
      components: {
        // @ts-expect-error
        img: AdvancedImage,
        mention: Mention,
        mermaid: Mermaid,
        // @ts-expect-error
        audio: APlayer,
        // @ts-expect-error
        video: DPlayer,
        tweet: Tweet,
        "github-repo": GithubRepo,
        "xlog-post": XLogPost,
        // @ts-expect-error
        style: Style,
      },
      ignoreInvalidStyle: true,
      jsx,
      jsxs,
      passNode: true,
    }),
  toMetadata: () => {
    let metadata = {
      frontMatter: undefined,
      images: [],
      audio: undefined,
      excerpt: undefined,
    } as {
      frontMatter?: Record<string, any>
      images: string[]
      audio?: string
      excerpt?: string
    }

    metadata.excerpt = file.data.meta?.description || undefined

    if (mdastTree) {
      visit(mdastTree, (node, index, parent) => {
        if (node.type === "yaml") {
          metadata.frontMatter = jsYaml.load(node.value) as Record<
            string,
            any
          >
        }
      })
    }
    if (hastTree) {
      visit(hastTree, (node, index, parent) => {
        if (node.type === "element") {
          if (
            node.tagName === "img" &&
            typeof node.properties.src === "string"
          ) {
            metadata.images.push(node.properties.src)
          }
          if (node.tagName === "audio") {
            if (typeof node.properties.cover === "string") {
              metadata.images.push(node.properties.cover)
            }
            if (!metadata.audio && typeof node.properties.src === "string") {
              metadata.audio = node.properties.src
            }
          }
        }
      })
    }

    return metadata
  },
}

这样我们就优雅地从原始 Markdown 文档开始,获得了我们需要的各种格式的输出

除此之外,我们还能利用解析出的 unified 语法树来编写一个可以左右同步滚动和实时预览的 Markdown 编辑器,可以参考 xLog 的双栏 Markdown 编辑器(代码),有机会我们下次再聊

2023 平稳过渡

2024年1月8日 22:23

</2023>
<2024>

发小觉得今年经历了巨大的变化,从苏格兰搬到新加坡,从未婚到已婚,从一家两口变成了一家三口,但我觉得都在预料之中,所以感觉还算很平稳

身边的物品最能反映一个人的状态,所以还是跟 2022 一样从年度最佳开始

🏆 年度最佳

🍿 影视

看过的 28 部动漫和 12 部剧集和 8 部电影:https://movie.douban.com/people/62759792/collect?mode=list

而去年是 19 部动漫和 18 部剧集和 6 部电影,二次元含量显著上升了

DIY 年度番剧: 《鬼灭之刃 锻刀村篇》

image

尽管有数集节奏过缓,然而该剧的燃点皆燃,泪点亦催人泪目,尤以最后以为会是虐人的剧情却华丽逆转为甜蜜结局,令人心潮澎湃,难以自抑

去年最佳是《鬼灭之刃 游郭篇》,今年是《鬼灭之刃 锻刀村篇》,已经可以预料到了明年是《鬼灭之刃 柱训练篇》,希望可以保持下去

除此之外还有《天国大魔境》、《间谍过家家》、《【我推的孩子】》(第一集)、《为美好的世界献上爆焰!》也非常喜欢

DIY 年度剧集: 《最后生还者 第一季》

image

非常还原游戏,又胜于游戏,主角也非常契合角色,完美的一季,HBO 太强了

📚 图书

album_temp_WeRead_2023_Year_In_Review_11704021416

今年看过的 22 本书:

  • 《被讨厌的勇气》
  • 《长安的荔枝》
  • 《那些古怪又让人忧心的问题》
  • 《上帝掷骰子吗》
  • 《人类灭绝》
  • 《来自新世界》 (两本)
  • 《莱博维茨的赞歌》
  • 《赡养人类》
  • 《朝闻道》
  • 《埃隆・马斯克传》
  • 《巨人的陨落》(三本)
  • 《世界的凛冬》(三本)
  • 《翦商》(在读)
  • 《自私的基因》(在读)
  • 《一想到还有 95% 的问题留给人类,我就放心了》(在读)
  • 《猫咪家庭医学大百科》(在读)
  • 《未来简史》(在读)

去年是 9 本,没想到今年不知不觉看了这么多,大部分是旅游和 Boox Leaf 3 的功劳

DIY 年度图书:《人类灭绝》

image

差点被奇怪的标题和封面错过,是我看过最悬疑、血腥、脑洞的一本书,作者从三条看似不相关的线分别展开,最后再完美融合,揭露一个最大的阴谋

如果有一天智人不再是地球最聪明的物种,智人一定会毫不犹豫暴露出残忍丛林天性,不一定是因为书里假设的超人类新物种的出现,AI 的技术爆炸也许会让这一天马上到来

非常期待有一天被拍成电视剧

排名第二的是 《上帝掷骰子吗》

读到了很多难以置信的物理知识和猜想,比如空间和时间无法无限分割下去,它们不是连续的;所有观测者死掉的世界都没意义,所以观测者只会一直活下来,无法自杀,无法死亡,所有人都会量子永生;世界是不确定的,世界的底色不是因果律而是概率,上帝是掷骰子的。简直比科幻还要魔幻

排名第三的是《巨人的陨落》

世纪三部曲中的第一部,是那种开头比较难读下去但是一旦投入进去就无法自拔的书,学到了很多欧洲历史看到了时代变迁

🕹️ 好物

DIY 年度好物: Boox Leaf 3

image

第一次用电纸书,续航足够长,刷新速度也很快,内置墨水屏版微信读书,让我多看了很多书,缺点就是长时间拿着有点硌小拇指,还好买了 Randy 推荐的手机气囊支架解决了

其他还有一些值得推荐的小东西

Followcat 猫咪项圈

image

可以配套一个 AirTag 壳装入 AirTag 来定位猫咪,不仅可以预防出门跑丢,在家里不知道藏在哪里的时候也更好找了

博士眼镜

IMG_5716

线下配眼镜要几千块,一直让我觉得眼镜是个很珍贵的物品,平时需要小心翼翼地保护,但发现差不多的蔡司镜片加镜框线上只要 200 多,差价很夸张,实际使用也感觉不出什么区别

索尼 ZV1 M2 Twitter

image

从第一代 ZV1 换到了第二代,虽然画质没什么升级,但解决了很多痛点,使用体验好了非常多,包括人类可以使用的新版菜单、更适合 vlog 的 18mm 焦距、内置指向性麦克风、视频拍照切换实体按键、USB-C 等

👩🏻‍💻 应用

DIY 年度应用:Arc

image

竖向标签栏、工作区用了就回不去,而且并不是简单地把标签栏竖过来,很多交互细节还是挺用心的

另外分享一些自己在使用的喜欢的应用

  • 邮件客户端:Mimestream
  • 浏览器:Arc
  • 命令行:Warp
  • Docker 管理:OrbStack
  • 时间追踪:Toggl Track
  • 笔记:Obsidian
  • RSS 阅读:RSSHub、Reeder、Miniflux、RSS to Telegram Bot、RSSHub Radar
  • 待办事项:TickTick
  • 图片管理:Eagle
  • 记账:MoneyWiz
  • 影音:Plex、Alist、AutoBangumi、UnblockNeteaseMusic、Xiaoya
  • 自动化:n8n
  • 网站统计:Umami
  • 加密钱包:OneKey、MetaMask
  • 翻译:Bob、沉浸式翻译
  • 广告拦截:AdGuard Home、AdGuard for Mac
  • 截图:Snip
  • 休息提醒:BreakTimer
  • 足迹:世界迷雾
  • 睡眠记录:Pokemon Sleep
  • 应用管理:Homebrew Cask

🎮 游戏

DIY 年度游戏:《塞尔达传说:王国之泪》

image

今年玩的唯一的游戏

🧸 生活

🏡 饮食居住

11 月从贫瘠的苏格兰来到了生活便利的新加坡,生活质量还是高了一大大大截

  • 饮食:跟英国比新加坡简直是天堂,终于有了便宜好吃的外卖和餐厅
  • 住:租房更贵更小了,但多了游泳池和健身房,位置也很好,也算一丝慰藉

🛫 旅游

发小去年 12 月滑雪扭伤了腿,所以前几个月哪都没去

  1. 5 月跟 Harry 去 Lochgelly 上山徒步 Twitter

我和 Harry 背着三大桶水累得在地上爬,发小健步如飞

image

image

image

  1. 跟粗粗去了土耳其 Twitter Twitter Twitter Twitter Twitter Twitter Twitter

景区里好玩的很多,山路自驾、游泳、跳海、跳伞、骑马、热气球、晒太阳,大海和热气球风景也超好看,就是价格很感人,景点物价很高,会宰游客,离开景点区域又很贫瘠

带着游泳圈在水里泡了几天,差一点学会了游泳

image

image

image

image

image

image

image

  1. 8 月去了伦敦、剑桥、利兹、约克 Twitter Twitter Twitter Twitter Twitter Twitter

带发小妈妈在英国到处玩

看悲惨世界话剧 Do You Hear the People Sing Twitter

image

唐顿庄园里面 Cosplay Twitter

image

  1. 被 Harry 带着逛了爱丁堡神奇的化石店和外科手术博物馆

外科手术博物馆展示了泡在福尔马林中成堆的尸体和各个部位肢体,发小看得津津有味

跟 Harry 在爱丁堡海边放火烤棉花糖 Twitter Twitter

烤棉花糖是外面脆脆的里面软绵绵的很奇妙,Harry 做的酥肉也超级好吃

image

  1. 9 月去了法国、德国、奥地利、匈牙利、捷克

该玩的都玩了但时间很赶,走马观花

  1. 10 月去了成都、重庆、郑州、乌鲁木齐、上海

在成都见了Tyzual七夏浅笑,去看了大熊猫,在重庆大吃特吃,在乌鲁木齐吃羊肉配皮芽子

成都大熊猫基地

image

乌鲁木齐的大草原

image

  1. 11 月去了香港

香港金融科技周见到了很多同事,涨了很多见识,也是成立了两年多的公司的联合创始人们第一次团聚

IMG_3450_副本

🤼‍♀️ 社交

今年不但去了很多地方还见了很多朋友

Harry粗不粗Tyzual七夏浅笑花生chTonyBruce、Anni、Atlas、birdring、Dmoo、Jeff、Joshua、Kate、Maggie、Usagi、Yingzi、birdring、yingzi,大部分是同事,都是远程工作见一面着实不容易

🐱 家庭

回老家结婚了,过程很累,但看到发小穿上婚纱打扮一番一切都值了 Twitter

4803c6bf4efd14f8a6085d8586cc3aff

image

一回国就跟酸奶团聚了,还一起来了新加坡 Twitter

被问的最多的是 “分开了两年多,酸奶还认识你么”,虽然我永远无法知道答案,但从黏人程度来看,我倾向于是认识的

image

🏊 技能

游泳

从小学过几次,大学还上过游泳课,但都没有学会

如上所述,今年在土耳其旅游时候套个游泳圈泡了几天大海,克服了对游泳的恐惧,大概学会了自由泳的姿势但是不会换气

到新加坡之后又在小区的游泳池泡了几周后完全学会了,现在每天都会跟发小去游泳,运动强度足够大又有水冷,已经是我最喜欢的运动了,感觉之前错过了很多乐趣

IMG_5549 2 Large

播客

为了宣传 xLog,接受了 Web Worker 的邀请录了人生第一次播客,主播辛宝、刘威、小白菜都很专业,过程意外地非常顺利,也没有任何冷场

但是作为一个资深社恐,虽然第一次很顺利,应该也很难鼓起勇气进行下一次了

打麻将和打掼蛋

跟着发小各去了一次打麻将和打掼蛋,学会了这两种游戏,但是要说话所以都没有第二次了

🌌 印象深刻的日常

过生日发小精心设计的 DIY 诱捕器

过生日 st 送的黑哥哥祝福

在发小学校穿苏格兰裙拍婚纱照

在格拉斯哥参加了发小的毕业典礼

在新加坡过圣诞节

在新加坡跨年看烟花表演,好像还是第一次看这么盛大的烟花表演,之前看过最大的是迪士尼

还有陪发小第一次去看演唱会,第一次去抓娃娃

🪄 生活管理

跟去年差不多,主要还是在使用这一套基于 Obsidian 的生活记录系统,现在日记是这样的结构

当天发生的事情、正在进行中的事项、日程表、富兰克林美德表

image

再补充上用 TickTick 的 Habit 来记录每天每周各个 OKR 目标完成得怎么样,用 Toggl Track 记录做各个事情的时间,比之前的在 Obsidian 里 all in one 方便很多

image

🌿 输出

👩🏻‍💻 代码

Contributions 快赶上 2019 年巅峰时期了

contributions

又是没有新项目的一年,今年中前期还是以 xLog 为主,后期也做了一些 RSSHub 和 RSSHub Radar 的工作,和为公司 2024 年的一些转变做准备

xLog 开发得热火朝天(相关 Twitter 列表),成功举办了 10000 刀的创作者激励活动,吸引了大批优秀的创作者,最多时候还每周有十几个社区开发者来贡献代码一起完善 xLog,完成了数不清的 Feature。现在有 3k 多创作者在上面发布了 26k 多高质量内容

RSSHub 重构了文档,让它更容易被维护,改造成了以英文为优先语言;用更现代化的架构 Plasmo 完全重写了 RSSHub Radar,变得更好看更稳定更容易维护,还能自动发布到各大应用商店。现在 RSSHub 官方实例每月承载三亿多次请求,docker 有一千五百多万次下载,RSSHub Radar 有差不多 9 万周活用户

📙 文字

这一年文字也非常高产

认真写了 9 篇博客

水了 243 条 Twitter

列表

🎥 影音

今年在 B 站发布了两个 Vlog,旅游积攒了好多素材,至少还有 6 个视频还没剪,很惭愧

《女友浴室沉思录,非正常录音》

《英国旅游 VLOG | 爱丁堡圣诞节》

录了一个播客《和 xlog.app 的作者 DIYgod 聊区块链和博客平台、前端学习和生活感悟》

https://www.xiaoyuzhoufm.com/episode/645a76f67d934b85051081c8

🎊 2024

⭐️ OKR

  • O1: In excellent health

    • KR1: Body control, body weight 70kg and body fat 17%
    • KR2: Early to bed and early to rise, 12:00-8:00
    • KR3: Healthy Eating, light and less, increase the proportion of white meat and reduce the sugar
    • KR4: More exercise, at least 3 times per week
  • O2: Highly productive like a sow

    • KR1: Blog, at least 1 blog per month
    • KR2: Vlog, publish the 6 vlogs from last year
  • O3: Knowledge overflows my brain

    • KR1: Reading, at least 7 hours per week
    • KR2: Professional learning, at least 5 hours per week
    • KR3: English learning, at least 5 hours per week
  • O4: I am Franklin

    • KR1: Guarantee 1 virtue per week
    • KR2: Record virtue status daily
  • O5: Run like clockwork

    • KR1: Write bullet journal daily
    • KR2: Bookkeep weekly and monthly
    • KR3: Do things on a daily schedule
  • O6: Writing code is like writing the poem of life

    • KR1: Focused work for at least 7 hours per day
    • KR2: Actively maintain and contribute to open source projects for at least 3 hours per week
    • KR3: Develop a new RSS reader using Tauri, with comprehensive support for RSSHub and AI
  • O7: Combat inflation

    • KR1: Achieve a 10% annual return through simple and healthy financial management strategies

轻松创建一万个 Twitter 账号

2023年12月15日 18:14

Information freedom does not naturally evolve, it degrades.
—— Open Information Manifesto

Twitter 在 8 月决定了全面限制公开访问和 API 接口,导致第三方集成均无法再正常工作。开放用户数据被绑架成私人敛财工具,曾经的 Open Web 标杆 Twitter 竟沦落到这种境地,数字奴隶制在最不应该的地方出现,令人唏嘘。这也致使许多用户流向 Fediverse,但社交关系和习惯一旦形成,要让其迅速改变并不易,更多人还是选择了忍受,Musk 也是看穿了这一点才有恃无恐

然而,我们也不能武断地说 Twitter 封闭,毕竟它仍然开放了一个起步价为每月 4 万美元,上限不设的企业 API

什么?你说用不起?

那么你可以像我一样,通过创建一万个账号以绕开封锁

尽管 Twitter 限制了所有公开访问,但我们发现新下载的 Twitter 移动客户端仍可以正常查看用户动态。这为我们提供了潜在的利用方法,通过抓包,我们可以看到客户端是通过请求一系列特殊接口来创建一个权限较低、频率限制严格的临时账号。我们可以用这个账号获取我们需要的大部分数据。然而,这种账号对请求频率的限制非常严格,因此需要大量的这样的账号才能满足基本的使用需求。同时,每个 IP 在一段时间内只能获取一个临时账号,因此我们也需要大量的 IP 代理

具体拆包和抓包过程可以参考 BANKA 的《怎么爬 Twitter(Android)》。站在 BANKA 肩膀上,我们可以写出一个这样的注册脚本(来自 Nitter - Guest Account Branch Deployment):

#!/bin/bash

guest_token=$(curl -s -XPOST https://api.twitter.com/1.1/guest/activate.json -H 'Authorization: Bearer AAAAAAAAAAAAAAAAAAAAAFXzAwAAAAAAMHCxpeSDG1gLNLghVe8d74hl6k4%3DRUMF4xAQLsbeBhTSRrCiQpJtxoGWeyHrDb5te2jpGskWDFW82F' | jq -r '.guest_token')

flow_token=$(curl -s -XPOST 'https://api.twitter.com/1.1/onboarding/task.json?flow_name=welcome' \
          -H 'Authorization: Bearer AAAAAAAAAAAAAAAAAAAAAFXzAwAAAAAAMHCxpeSDG1gLNLghVe8d74hl6k4%3DRUMF4xAQLsbeBhTSRrCiQpJtxoGWeyHrDb5te2jpGskWDFW82F' \
          -H 'Content-Type: application/json' \
          -H "User-Agent: TwitterAndroid/10.10.0" \
          -H "X-Guest-Token: ${guest_token}" \
          -d '{"flow_token":null,"input_flow_data":{"flow_context":{"start_location":{"location":"splash_screen"}}}}' | jq -r .flow_token)

curl -s -XPOST 'https://api.twitter.com/1.1/onboarding/task.json' \
          -H 'Authorization: Bearer AAAAAAAAAAAAAAAAAAAAAFXzAwAAAAAAMHCxpeSDG1gLNLghVe8d74hl6k4%3DRUMF4xAQLsbeBhTSRrCiQpJtxoGWeyHrDb5te2jpGskWDFW82F' \
          -H 'Content-Type: application/json' \
          -H "User-Agent: TwitterAndroid/10.10.0" \
          -H "X-Guest-Token: ${guest_token}" \
          -d "{\"flow_token\":\"${flow_token}\",\"subtask_inputs\":[{\"open_link\":{\"link\":\"next_link\"},\"subtask_id\":\"NextTaskOpenLink\"}]}" | jq -c -r '.subtasks[0]|if(.open_account) then {oauth_token: .open_account.oauth_token, oauth_token_secret: .open_account.oauth_token_secret} else empty end'

以及这样的批量注册脚本(来自我自己):

const got = require('got');
const { HttpsProxyAgent } = require('https-proxy-agent');
const fs = require('fs');
const path = require('path');

const concurrency = 5; // Please do not set it too large to avoid Twitter discovering our little secret
const proxyUrl = ''; // Add your proxy here

const baseURL = 'https://api.twitter.com/1.1/';
const headers = {
    Authorization: 'Bearer AAAAAAAAAAAAAAAAAAAAAFXzAwAAAAAAMHCxpeSDG1gLNLghVe8d74hl6k4%3DRUMF4xAQLsbeBhTSRrCiQpJtxoGWeyHrDb5te2jpGskWDFW82F',
    'User-Agent': 'TwitterAndroid/10.10.0',
};

const accounts = [];

function generateOne() {
    // eslint-disable-next-line no-async-promise-executor
    return new Promise(async (resolve) => {
        const timeout = setTimeout(() => {
            // eslint-disable-next-line no-console
            console.log(`Failed to generate account, continue... timeout`);
            resolve();
        }, 30000);

        const agent = {
            https: proxyUrl && new HttpsProxyAgent(proxyUrl),
        };

        try {
            const response = await got.post(`${baseURL}guest/activate.json`, {
                headers: {
                    Authorization: headers.Authorization,
                },
                agent,
                timeout: {
                    request: 20000,
                },
            });
            const guestToken = JSON.parse(response.body).guest_token;

            const flowResponse = await got.post(`${baseURL}onboarding/task.json?flow_name=welcome`, {
                json: {
                    flow_token: null,
                    input_flow_data: {
                        flow_context: {
                            start_location: {
                                location: 'splash_screen',
                            },
                        },
                    },
                },
                headers: {
                    ...headers,
                    'X-Guest-Token': guestToken,
                },
                agent,
                timeout: {
                    request: 20000,
                },
            });
            const flowToken = JSON.parse(flowResponse.body).flow_token;

            const finalResponse = await got.post(`${baseURL}onboarding/task.json`, {
                json: {
                    flow_token: flowToken,
                    subtask_inputs: [
                        {
                            open_link: {
                                link: 'next_link',
                            },
                            subtask_id: 'NextTaskOpenLink',
                        },
                    ],
                },
                headers: {
                    ...headers,
                    'X-Guest-Token': guestToken,
                },
                agent,
                timeout: {
                    request: 20000,
                },
            });

            const account = JSON.parse(finalResponse.body).subtasks[0].open_account;

            if (account) {
                accounts.push({
                    t: account.oauth_token,
                    s: account.oauth_token_secret,
                });
            } else {
                // eslint-disable-next-line no-console
                console.log(`Failed to generate account, continue... no account`);
            }
        } catch (error) {
            // eslint-disable-next-line no-console
            console.log(`Failed to generate account, continue... ${error}`);
        }

        clearTimeout(timeout);
        resolve();
    });
}

(async () => {
    const oldAccounts = fs.readFileSync(path.join(__dirname, 'accounts.txt'));
    const tokens = oldAccounts.toString().split('\n')[0].split('=')[1].split(',');
    const secrets = oldAccounts.toString().split('\n')[1].split('=')[1].split(',');
    for (let i = 0; i < tokens.length; i++) {
        accounts.push({
            t: tokens[i],
            s: secrets[i],
        });
    }

    for (let i = 0; i < 1000; i++) {
        // eslint-disable-next-line no-console
        console.log(`Generating accounts ${i * concurrency}-${(i + 1) * concurrency - 1}, total ${accounts.length}`);

        // eslint-disable-next-line no-await-in-loop
        await Promise.all(Array.from({ length: concurrency }, () => generateOne()));
        fs.writeFileSync(path.join(__dirname, 'accounts.txt'), [`TWITTER_OAUTH_TOKEN=${accounts.map((account) => account.t).join(',')}`, `TWITTER_OAUTH_TOKEN_SECRET=${accounts.map((account) => account.s).join(',')}`].join('\n'));
    }
})();

这些脚本已放到了 RSSHub 仓库: https://github.com/DIYgod/RSSHub/blob/master/scripts/twitter-token/generate.js

在使用前,你需要填入你购买的 IP 代理服务地址。脚本会自动处理超时、请求等错误,并且以 5 并发来自动获取临时账号,当获取到 1000 个账号后将会停止。需注意并发不要设置得过高,我从观察发现,当 Twitter 发现大量请求时会暂停接口一段时间

我购买了 5 家代理服务以进行测试,感觉效果相差无几,选择一个最便宜的服务就可以。通常,最低价的 1G 套餐就足够获取大约几万到十几万个账号了。我目前找到的最便宜的服务是 proxy-cheap,如果你有更好的选择请告知我

这种方法已经在 Nitter 上稳定运行了一段时间,现在也已实装到了 RSSHub 及其官方示例上,我们可以宣布与邪恶 Twitter 奴隶主的战争已经阶段性胜利

优雅使用 Cloudflare WARP 应对 RSSHub 反爬难题

2023年8月18日 06:01

🕊️ 本文送给更开放的互联网


起因是看到 @geekbb 介绍 Warp 的推文。尽管 Warp 已经发布了很长时间,就保护 IP 隐私而言,它并没有 iCloud Private Relay 好用,我也没有魔法上网的需求。但是我突然意识到,我还是有隐藏 IP 的需求。

在开发 RSSHub 的几年中,我发现提供公共 API 的站点非常少,许多站点还会采取严格的反爬控制来限制其平台内容的获取。有些站点会屏蔽同一 IP 发出过多请求,而还有一些站点则会全面屏蔽常见云服务器厂商的 IP 地址。因此,仅仅为了获取最新几条内容更新却变得非常困难。

lord-of-the-rings-my-precious

这种情况需要使用代理,但是专门的爬虫代理通常价格昂贵,性价比极低,如果 Cloudflare WARP 的无限流量和丰富的 IP 资源能被 RSSHub 利用就太棒了。RSSHub 已经支持了通用的代理协议,只要能将 WARP 包装为通用的 proxy 就可以。

image

虽然无法直接在命令行环境中方便地使用官方客户端,但这么容易想到的点子肯定已经被别人实现过了。我在 GitHub 上找到了一个封装的 Docker。

然后只需要在 RSSHub 的 docker-compose.yml 中再添加这样一个 service 来启用代理服务

warp-socks:
    image: monius/docker-warp-socks:latest
    privileged: true
    volumes:
        - /lib/modules:/lib/modules
    cap_add:
        - NET_ADMIN
        - SYS_ADMIN
    sysctls:
        net.ipv6.conf.all.disable_ipv6: 0
        net.ipv4.conf.all.src_valid_mark: 1
    healthcheck:
        test: ["CMD", "curl", "-f", "https://www.cloudflare.com/cdn-cgi/trace"]
        interval: 30s
        timeout: 10s
        retries: 5

最后给 RSSHub 加一个 PROXY_URI 环境变量来使用代理

PROXY_URI: 'socks5h://warp-socks:9091'

我选取了一个我经常使用的 hotukdeals 路由(英国版的什么值得买)进行测试。该站点会屏蔽所有 DigitalOcean 的 IP,因此一直处于 403 状态。

image

加上 WARP 后可顺利访问

image

此外,我发现每次重启 WARP 时,都会输出新的 IP。尽管我没有时间验证,但我感觉 IP 应该会经常自动更改,这对解决反爬是一个好消息。

image

还可以进一步自定义 WireGuard 的配置,包括使用付费版 WARP+ 和自定义 endpoint,以获取可能更好的结果。

生成 WireGuard 配置文件可以使用

刷 WARP+ 流量和筛选 endpoint 可以使用

有说法是 WARP+ 的速度并无明显差异(《WARP、WARP + 速度对比,以及 WARP 速度上限》),但是是否影响反爬效果还需要进一步验证。

如果一切顺利,RSSHub 官方实例中许多严格反爬的路由应该能重新使用。我将在几天内进行验证并在此更新。

image

在博客融入一个跨平台作品集

2023年8月8日 06:34

长久以来

我一直将个人博客视为一个理想的展示个人 IP 的 “个人网站”,而不仅仅是发布文章的平台。我曾在 2014 年初学编程时使用 WordPress 建站 《世界,你好!》;入了前端坑后,在 2017 年我转向了 Hexo 《做了一点微小的改动》;Web3 飞升后 2022 年我换成了 xLog 《第一个开源链上博客系统 xLog》。然而,无论我使用什么博客系统,一直都存在一个问题,那就是如何优雅地汇集和展示我在其他平台发布的作品,最好还能直接显示外站的数据。我之前通常以文章形式发布作品,并在文章中附上链接,然而这样做显然不够优雅,读者还需要额外点击链接进行跳转。

灵感降临

我在学习达芬奇剪辑时,发现了影视飓风的网站,它通过外链方式列出了他们在 B 站发布的视频,其中包括标题、封面图、发布时间、播放量等信息。这个发现给了我启发,我完全可以在 xLog 上制作一个装载了我在各个平台作品的作品集,这里面可以有我发布在 B 站的视频、我在 GitHub 上维护的仓库、我参与的小宇宙播客甚至是我在 pixiv 上创作的画作。这样,当人们访问我的博客时,将不只是看到文章,而是会看到更丰富多元的我,这让我的博客更接近一个真正意义上的 “个人网站”。

下手

想法萌发后,实现就简单了。

  1. 对 xLog 后台进行了优化和清晰的分类:文章、页面、作品集,以消除类型增多后可能带来的用户困扰。

image

  1. 设计了一个全新的编辑页,不同于文章和页面,这里只保留封面、标题、摘要、发布时间,并新增外部链接字段。

image

  1. 实现了作品信息的自动填充功能,减轻了手动输入的负担。这是通过获取链接的 Open Graph 信息实现的,涉及到的字段包括 og:image og:title og:description og:date

image

  1. 把作品展示在首页和独立的作品集页

image

  1. 数据的获取和展示,对于 “偷数据” 经验丰富的 RSSHub 作者来说,这是得心应手的一环,首先针对 bilibili、小宇宙、GitHub、pixiv、Twitter 这几个平台进行了抓取,获取到播放量和评论数并在 xLog 的卡片上进行展示,同时考虑到源站可能的压力和反爬,我特别设置了足够长的数据缓存。

image

如今,这个简单实用的小功能已经落地实现了,可以看看我的作品集页,你是否也想要尝试在 xLog 建立属于自己的个人作品集呢?

对 Newsletter 说不

2023年7月13日 02:31

Image

衰退无处不在,这是很正常的现象。人们自然而然地更倾向于短平快的消费方式。然而,我一直无法忍受的一种奇怪趋势是,在一些地方,人们将 RSS 抛弃,转而使用 Newsletter。

本质上,电子邮件是一种私密的双向通信机制,而 RSS 是一种开放的单向通信机制。使用电子邮件进行私密的更新和文章推送是没有意义的,RSS 是更自然的选择。强行将这些功能应用于电子邮件可能会导致许多问题。这些问题让我觉得 Newsletter 就像一个没有能力但却拼命想证明自己的暴君,无法很好达到发布者期望的效果,又过分侵犯了用户的选择和效率。

五宗罪

封闭限制

RSS 是一种开放协议,它允许用户自主订阅和拉取感兴趣的网站的 RSS 源,获取最新的更新和文章,无需他人的许可。用户还可以通过阅读器的个性化设置自由选择所需内容,并且可以使用多种渠道接收通知,甚至可以使用 Telegram Bot 进行订阅。

image

而 Newsletter 是由发布者推送到用户私人邮箱的订阅信息,整个过程依赖于平台方,渠道全程封闭。这种封闭极大地限制了用户的选择权,用户被迫在平台的许可下,通过特定渠道特定格式接收固定信息

繁琐低效

相比之下,RSS 更加简洁高效。订阅源可以集中管理,分类、收藏、订阅和取消订阅的过程也非常简单。

image

而 Newsletter 则会将各种各样的邮件混合在一起,非常分散且难以管理。你很难知道自己到底订阅了哪些内容,它们什么时候会突然出现。而且,内容格式也是各种各样的,查看和阅读起来非常混乱,所以你也不能将一篇文章进行收藏,更不用说方便的第三方集成了。

信息过载

Newsletter 很难对内容进行有效分类和过滤,又与所有正常邮件混合在一起,需要花费精力手动整理。这很容易导致信息过载和垃圾邮件的问题。

而 RSS 可以很方便地进行分类和过滤,对于不重要的内容,你也可以一键全部标记为已读瞬间解脱,完全没有压力。

image

image

更新周期长

对于 RSS 的更新虽然不算实时,但一般以小时计,类似 RSSHub 等自建服务,甚至可以做到每分钟更新。相比之下,Newsletter 的更新周期,以天甚至周月计,明显滞后了许多。

隐私和安全风险

RSS 的开放性体现在它不需要用户提供个人信息,从而确保了更好的隐私性和安全性。然而,Newsletter 至少需要提供一个邮箱地址,这增加了数据泄露或滥用的风险。更有甚者,电子邮件可能包含恶意链接或附件。

也有一些优点

尽管我对 Newsletter 的低效和局限性持批评态度,但我也承认它有其优点,它的流行也有一些合理性,特别是在卖方市场的情况下对于一些发布者来说。Newsletter 可以让他们获得更多的控制权和点击率,可以更容易知道有谁订阅了他们的内容,并通过邮件通知更强烈地唤起用户的注意。

然而,站在用户的角度,我必须明确表示,我更偏爱 RSS。我不愿意以放弃自己的选择权和效率来迎合发布者的控制欲。在我看来,获取信息的权力应该掌握在我自己手中,而非其他人或机构。所以,我在这里对 Newsletter 说不。

4 月新番太好看了!吹爆!

2023年5月25日 07:32

今年从 1 月就一直没什么好看的作品,到了 4 月突然爆发,让我非常激动。现在播出过半了,是时候好好说说感受了

以下当然不能囊括 4 月所有的好作品,因为实在太多了,只是说一说符合我口味的几部,按我个人的喜爱程度来排序

以下会包含大量剧透,还没看的小伙伴请酌情观看

鬼灭之刃刀匠村篇

刀匠村是为鬼杀队锻造日轮刀的刀匠们居住的地方,位置及其隐蔽且高度保密,这部讲的是刀匠村被上弦之肆・半天狗和上弦之伍・玉壶找到并发动偷袭,正在刀匠村的炭治郎、恋柱・甘露寺蜜璃、霞柱・时透无一郎、不死川玄弥努力保护刀匠们,对抗上弦的故事

上弦之肆・半天狗这个角色非常有趣,本体又小又丑,非常弱小无助又胆小,总感觉像是来送人头的

image [BeanSub&FZSD&LoliHouse] Kimetsu no Yaiba - 51 [WebRip 1080p HEVC-10bit AAC ASSx2]-0001 image

但实际上非常扮猪吃老虎,控制的几个鬼(还不知道他们是什么关系)喜、怒、哀、乐、憎,都非常强,压迫感很足,本体防御力也无敌,被大家排着队砍也砍不死

image image

弥豆子的表现也很亮眼,鬼化弥豆子超帅

image

image

还有可爱的蜜璃

虽然整体节奏还是略显拖沓,但经过游郭篇的洗礼,我本身预期也很低,目前已经远远超出了我的预期,文戏比之前明显少了好多,打斗也更加燃爆,看得非常上头

image

image

再加上我有粉丝加成,荣登我的周指活排名第一名

谢谢飞碟社款待

类别评分 / 10
剧情7
画面10
个人加成10

为美好的世界献上爆焰!

脑子有问题的红魔族第一天才中二病魔法师慧慧的故事,喜欢的东西是爆裂魔法,特技是爆裂魔法,兴趣是爆裂魔法,唯一的真爱也是爆裂魔法

慧慧小时候被一位使用爆裂魔法的巨乳魔法师所救而爱上了爆裂魔法

[Lilith-Raws] Kono Subarashii Sekai ni Bakuen wo! - 01 [Baha][WEB-DL][1080p][AVC AAC][CHT][MP4]-0008

虽然爆裂魔法极不实用且难以操纵,是出了名的搞笑魔法,“只有学得上级魔法才能独当一面。爆裂魔法之流不过是搞笑魔法罢了”,但慧慧还是不顾嘲笑,为了爱好而独自努力

[Lilith-Raws] Kono Subarashii Sekai ni Bakuen wo! - 01 [Baha][WEB-DL][1080p][AVC AAC][CHT][MP4]-0002 [Lilith-Raws] Kono Subarashii Sekai ni Bakuen wo! - 01 [Baha][WEB-DL][1080p][AVC AAC][CHT][MP4]-0147

最后使用所有技能点数学到了爆裂魔法

比黑色还要黑 暗之漆黑
融合著我之真红吧
觉醒的时刻已经到来
坠入无谬之境界
形成无形之扭曲
出现吧!
Explosion!

[Lilith-Raws] Kono Subarashii Sekai ni Bakuen wo! - 05 [Baha][WEB-DL][1080p][AVC AAC][CHT][MP4]-0055

之后的外出冒险也很有趣

[Lilith-Raws] Kono Subarashii Sekai ni Bakuen wo! - 07 [Baha][WEB-DL][1080p][AVC AAC][CHT][MP4]-0012

[Lilith-Raws] Kono Subarashii Sekai ni Bakuen wo! - 08 [Baha][WEB-DL][1080p][AVC AAC][CHT][MP4]-0021

之前看《为美好的世界献上祝福!》就被慧慧的中二吸引了,结果在红魔乡发现慧慧的中二只是一个普通水平,印象最深的是魔法学校最重要的一节课是战斗前要摆什么样的中二姿势

[Lilith-Raws] Kono Subarashii Sekai ni Bakuen wo! - 01 [Baha][WEB-DL][1080p][AVC AAC][CHT][MP4]-0096 [Lilith-Raws] Kono Subarashii Sekai ni Bakuen wo! - 01 [Baha][WEB-DL][1080p][AVC AAC][CHT][MP4]-0094 [Lilith-Raws] Kono Subarashii Sekai ni Bakuen wo! - 01 [Baha][WEB-DL][1080p][AVC AAC][CHT][MP4]-0190

缺点也是有的,作画穷了点,有不少崩坏的地方,有些画面还是挺出戏的,特别是扎堆在作画全都不要钱一样的 4 月新番中显得更惨了(但还算说得过去)(后面还有个说不过去的)

类别评分 / 10
剧情8
画面6
个人加成10

天国大魔境

刚说的精美作画就来了,从第一集就被震撼到了,不要钱一样的作画,末日废土乌托邦剧情,流畅的打戏完美的运镜,轻松愉快加简单的涩涩又不失紧张刺激的氛围把控,就像完美的考试标准答案一样,刚开始还担心开头质量过于高了后面会不会质量下降,但是完全没有!

剧情还没有完全揭秘,现在知道的是成为废墟的日本栖息着一种食人的怪物蛭子,人们艰难地生存着,还有一个号称 “天国” 的乌托邦,被安全的围墙覆盖,里面科技发达,被圈养的孩子们生活富足

墙外很危险

image

墙内看似天国但剧情暗示了更大的危险

[SweetSub&LoliHouse] Heavenly Delusion - 05 [WebRip 1080p HEVC-10bit AAC ASSx2]-0006 image

生活在墙外的主角真流和斩子被托付前往天国寻找和真流长相一样的人,期间遇到了各种各样的人

不愿相信儿子已经变成没有人性的怪物蛭子的母亲,诱骗路人喂给儿子吃,最后被儿子变成的蛭子杀死

image [SweetSub&LoliHouse] Heavenly Delusion - 02 [WebRip 1080p HEVC-10bit AAC ASSx2]-0001

梦想跟看上的客人爱爱来赚大钱的酒店小老板,也会跟骗子强盗合作诱拐冒险者

image image

做人体实验的冷血医生,又只是一个为了让妻子可以作为人类有尊严死去的痴情男,成功让妻子死去后抱着妻子的尸体自杀

image image image

而义正严词反对人体实验的伦理组织首领只是为了给医生下圈套抢夺资源

image [SweetSub&LoliHouse] Heavenly Delusion - 08 [WebRip 1080p HEVC-10bit AAC ASSx2]-0013

每个人都能被不长的篇幅刻画地如此鲜活,在这样的废土世界,努力艰难不择手段地活着,即使罪大恶极也能让我深深理解,感到同情和惋惜

让我对制作组献上最大的敬意,如果排除个人因素,这部作品将是遥遥领先的 4 月霸权

类别评分 / 10
剧情10
画面10
个人加成5

地狱乐

在德川幕府时期,有一个鲜花遍地,蝴蝶翩飞的名为 “神仙岛” 的地方,前往该岛的调查队只有极少数的残肢和鲜花能够随着小船漂流回来

最强死刑犯忍者画眉丸与处刑人山田浅右卫门佐切一同被派往此岛,寻找不老不死的仙药以换取无罪释放,并在岛上与怪物以及其他死刑犯互相残杀

里面的怪物是这样的画风

灶神

image image [BeanSub&FZSD&LoliHouse] Jigokuraku - 05 [WebRip 1080p HEVC-10bit AAC ASSx2]-0035

天仙

[BeanSub&FZSD&LoliHouse] Jigokuraku - 07 [WebRip 1080p HEVC-10bit AAC ASSx2]-0032

被天仙抓到会被炼成丹

[BeanSub&FZSD&LoliHouse] Jigokuraku - 07 [WebRip 1080p HEVC-10bit AAC ASSx2]-0031 [BeanSub&FZSD&LoliHouse] Jigokuraku - 05 [WebRip 1080p HEVC-10bit AAC ASSx2]-0033

非常诡异荒诞

每个死囚也都有自己的故事,并不是黑白分明,经历都很有趣

image image

还出现了一个怪力萝莉

image

仙药和小岛的真相依然扑朔迷离,后面肯定还会有更诡异更强大的怪物,很期待

类别评分 / 10
剧情8
画面8
个人加成0

【我推的孩子】

妇科男医生和早逝女病人遇难转生为自己单推偶像星野爱的双胞胎孩子阿库亚和露比,三年后星野爱被害身亡,阿库亚立志寻找母亲遇害的真相和真凶,露比梦想着成为母亲那样的偶像,兄妹应对演艺圈的各种纷争和阴谋

一个多小时的首集太惊喜了,像看了一部电影,我觉得可以算得上 4 月最佳单集了

但可能是开头调起太高了,期待也拉得很高,后面几集略显平淡了一点,估计是高低高的节奏,现在是憋大招中,最后会有一个超感人哭爆的结局,准备好纸巾了

虽然如此,但如果中间也能再多给星野爱一些画面就更好了

另外它的 OP 也超好听,我单曲循环了好几天

类别评分 / 10
剧情7
画面7
个人加成0

我家的英雄

疼爱独生女零花的微不足道上班族・鸟栖哲雄,某天因为工作关系和开始一个人生活的零花相约见面,却发现她脸上有被殴打的痕迹。在回家的路上,哲雄看见了像是犯人的男人,并尾随在其後。隔天偷偷回到女儿的公寓,结果却发生了使整个家庭的命运为之一变的事件。父亲为了女儿、为了家庭,走上了修罗之道。高潮迭起的悬疑故事开幕。

image

剧情很精彩,但制作非常非常贫穷,挺可惜的,就是刚才说的说不过去的那个

好在剧情底子非常强,再垃圾的制作也能感受到挺强的紧张感,也能捏着鼻子津津有味地看下去

就不截图了,画面实在看不下去

类别评分 / 10
剧情8
画面1
个人加成0

偶像大师 灰姑娘女孩 U149

讲述一群小学生偶像在早期没有制作人、没有工作的情况下,仍然一步步向着梦想前进的故事。

其实也没什么剧情,就是过家家... 但是很可爱,非常刑

image image image image image

类别评分 / 10
剧情1
画面9
个人加成0

跃动青春

岩仓美津未从乡下的小初中,以第一名的成绩考入了东京的高升学率高中。
这位乡村神童心怀完美的人生蓝图、独自来到东京。她成绩优异,却与他人有着独特的距离感,稍显格格不入。
她虽然偶尔会失败,但还是凭借天真的性格一点点打动班上的同学,使他们那各不相同的性格逐渐交叠。
相遇、相知、最终心意相通。
人人都会有心烦和焦躁之时。而无可替代的朋友,定将带来互相理解的契机。
这是个偶有杂音却能让人不知不觉快乐起来的校园生活喜剧!

image

不管是画面还是剧情看着都很舒服,作品本身是非常棒的,喜欢,但这种平淡剧情不是我喜欢的类型,只能简单吹爆

类别评分 / 10
剧情8
画面8
个人加成-5

其他

另外还有《机动战士高达 水星的魔女》《我心里危险的东西》《放学后失眠的你》也是我正在追的,但我平时能看番的空闲时间没那么多,这几部进度还没跟上,等我全看完可能会写一篇 4 月新番的总结,有机会到时候再一起说

最后祝大家追番愉快!玩王国之泪之余也不要忘记追番哦!

我得了软件更新强迫症

2023年4月9日 07:00

我必须让身边所有软件都保持最新版本,就像走路不能踩到地砖缝缝一样,没有意义,但也危害不大,所以我放任它发展

image

操作系统和固件

最基础的,大到主力生产力 MacBook,小到吃灰的 Dockcase 扩展坞,都要更新,这是我的 Checklist,基本上靠更新提醒就够了

设备截图
MacBookimage
NASimage
路由器image
iPadIMG_0531
iPhoneIMG_6D8DCEB3D457-1
小米手环IMG_3744
小米家居IMG_3745
相机Pasted image 20220627175100
PS5image
Switchimage
Dockcase 扩展坞Pasted image 20220504125702

应用

操作系统下的应用也需要更新

MacBook

Homebrew Cask 出现之前 macOS 的应用更新一直是一个难题,App Store 的自动更新最好用但是应用很少,曾经困扰了我很久,但现在没问题了

对 macOS 的应用我秉持这样的原则:

  • 第一顺位 Web 版,比如:Discord、Slack、Telegram、Spotify
  • 第二顺位 App Store 版
  • 第三顺位 Homebrew
  • 除此之外不安装

其中 Web 版不需要更新,App Store 会自动更新,Homebrew 只需要每天执行下命令

brew update && brew upgrade && brew cu -a -y && brew cleanup

但 App Store 有时候会好几天才能自动更新上,这不能忍,安装上 mas 用命令行触发强制更新,再加上鼠须管的词库更新和 alias,下面就是我的总命令了

alias up="brew update && brew upgrade && brew cu -a -y && brew cleanup && mas upgrade && ~/plum/rime-install iDvel/rime-ice:others/recipes/full"

image image

NAS

有两部分,群晖的套件会自动更新,Docker 部分用 Watchtower 自动更新

image

其他

iPad 和 iPhone 应用虽然也会自动更新,但也是经常好几天才更新,所以每天早上起床第一件事就是手动刷一下这两个的更新,其他不那么常用的设备就等提醒或自动更新,这个还是能忍的,毕竟我的强迫症还没那么严重

插件

应用内的插件也需要更新

浏览器

通常会自动更新,但也有遇到卡住几天都没更新的情况,还不是很懂,偶尔会去手动点下 Update,这些是我目前用到的浏览器插件

image

Obsidian

Obsidian 的 community plugins 和 themes 都需要更新,官方没有自动更新的方法,可以通过再装一个插件 Beta Reviewers Auto-update Tester 来实现自动更新,这些是我目前用到的 Obsidian 插件

image

VS Code

VS Code 有很好的自动更新,这些是我目前用到的 VS Code 插件

image image

Home Assistant

Home Assistant 就没那么顺利了,我装了一个 Home Assistant Community Store (HACS) 来获取更多的集成,但它没有自动更新,也没有更新提醒,要点进去才能看到

image

项目依赖

项目的依赖也需要更新,我用 Dependabot 来自动提交依赖更新的 pr,对于有完善自动化测试的项目,比如 RSSHub,就可以再用 Github Action Merge Dependabot 自动合并,一秒都不用多等

  automerge:
    if: github.actor == 'dependabot[bot]' && github.event_name == 'pull_request'
    needs: [ jest, puppeteer, docs, all ]
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
      contents: write
    steps:
      - uses: fastify/github-action-merge-dependabot@v3
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          target: patch

Changelog

默默更新有时候还是不够的,对一些重要或者感兴趣的软件我会用 RSS 订阅它们的 Changelog,及时了解它们都更新了什么

image

目前想到的只有这些了,习惯了的话检查一遍很快的,就能换来一整天的舒适

不要学我,即使学了也不要说是我教的

令我痛心的三部反乌托邦动漫

2023年4月2日 12:50

image

请欣赏 「DIY 的夺命歌单」

四月对我而言是一个充满悲伤的月份。尽管《四月是你的谎言》已经过去了很多年,但每当看到「四月」这两个字,仍会立刻触发我的 PTSD,再加上今天我刚读完一本反乌托邦题材的小说《来自新世界》。多种思绪糅杂在一起,让我不禁回想起了那些令我痛心的「重量级」反乌托邦动漫。

DARLING in the FRANXX

“如果把动画比作足球的话,这部动画大概就是日本国家队了,就是普通不看足球的人也会期待日本队的表现,也会去谈论足球,所以希望自己的动画也能被这样对待。不单单锦织敦史的粉丝、TRIGGER 的粉丝、A-1 的粉丝这些平时看动画的观众,希望平时不看动画的观众也能享受这部作品。” — 鸟羽洋典

制片人的这番话一点也不夸张。这部动漫的制作阵容极为豪华,集结了业界的大佬和顶级声优,因此在尚未播出之前就被所有人寄予厚望,以至于还没播出就已被推上神坛。

然而,神作的道路并非顺利。从爱奇艺和 B 站的霸权争夺,到驾驶姿势引发的女权争议,最后在 B 站 UP 主 Lexburner 的口嗨和群众的恶意举报中达到高潮,赵弹来袭。

image

这些历史背景现在看来其实都不值一提,只是说明当时的热度和争议之高。毕竟最后还是用作品说话,而这部作品简直太符合我的口味了,反乌托邦的背景、活泼可爱打破常规的 02、恰到好处的涩涩、科幻、鬼、悬疑...

1B89028C35E9F0136C0A628BA6DD978B

当时的我茶不思饭不想,在一声声 Darling Darling 中逐渐迷失了自我,每周就指着 Darling 活了,我把博客主题改成了 Darling 的配色和配图,头像和名字也换成了 Darling,买了手办、画册一切能买的周边,甚至学着画画专门临摹 02,逢人就吹爆,只要看了 Darling 我们就是好朋友种种

image

image

再回过神来就是惨绝人寰的烂尾了,就像回应开播时的轰轰烈烈一样,烂得也彻彻底底没有任何余地,但可能是大脑对强烈刺激的自我保护,我现在已经不太记得当时的具体情景了,只记得一个巨大的花嫁扎古噩梦...

这部由如此豪华的制作团队打造的作品,竟然如此令人失望。至今我仍无法理解。

约定的梦幻岛

一年后,我又发现了一部绝赞的反乌托邦番剧

image

在我吹爆《约定的梦幻岛》第一季的时候就有人提醒我:Darling 尸骨未寒,你怎么敢。我却不以为意,认为这只是巧合。毕竟,梦幻岛是根据广受好评的漫画改编的,有着坚实的剧情基础,与原创动画 Darling 根本不能相提并论,第一季播完我还沾沾自喜:看,这次没事吧。

然而,两年后,第二季开播了。

我仍然清晰地记得刚开播时,我坐在电脑前激动地第一时间观看。然而,越看越觉得不对劲。到了第四集,剧情已经无法挽回。后来我勉强捏着鼻子又看了几集,但发现我的意志力实在不足以支撑我继续观看。

按理说,漫画已经取得成功,剧情不成问题;第一季动画也获得了好评,拥有广泛的粉丝基础。只要按部就班地制作第二季,绝对会成为另一部神作。然而,动画剧情却砍掉了许多原创桥段,换上了毫无逻辑的编剧所编织的剧情。实在难以想象制作过程中究竟发生了什么。

还好,同期播放的巨人稳得很,让我没有过于悲伤。

image

进击的巨人

这是一部我从 2013 年一直吹到 2022 年的作品。

image

image

作为一部自 2013 年就开播的动漫,《进击的巨人》在所有动漫中一直拥有极高的地位,堪称元老级存在。许多人正是从这部作品开始入坑看动漫,它是很多人的 “启蒙” 作品,也是我极力推荐给发小观看的第一部动漫。这部动漫也是根据漫画改编的,但与梦幻岛不同的是,其剧情、设定、丰富度和热度都高出好几个层次。更重要的是,接手最终季制作的 MAPPA 公司在质量和口碑上都绝对令人放心,不会出现梦幻岛那样的问题。它的剧情连载了那么长时间,久经时间考验,也不会出现 Darling 的情况,因此我一直信心满满地放心吹爆。

然而后来的事情许多人都知道了,动画史上空前绝后的恶劣事件发生了。这不是恶搞,不是开玩笑,更不是烂尾,而是一个充满仇恨、对爱、正义和和平的人类主流价值观怀有敌意的精神变态作者,对喜爱他和他的作品的观众进行了深思熟虑、蓄谋已久的极端恶意伤害。呕!

image

image

image

image

虽然已经过了很久了,但我还是久久无法平静,我恨啊,不仅仅是痛心,更是咬牙切齿的仇恨。

image

其它

image

image

image
image
image

image

当然,这些只是玩梗。

最后,真诚祝愿四月新番开门大吉、一切顺利!

❌