普通视图

发现新文章,点击刷新页面。
昨天 — 2026年4月13日首页

虚惊一场

作者 obaby
2026年4月13日 13:26

春节过后,就去过一次台球厅,之前充的钱大概还剩个四百多。这次这么着急想去,是因为上次去的时候,感觉老板和员工全部都换了一个认识的也没有。

而之所以隔了这么久没去,是因为没有球搭子,自己一个人去也着实没什么意思。好不容易约上一个哥们去打会儿球,路上我还说:『我得赶紧把球杆拿走了,老板换了好几波了感觉。』

台球厅提供免费的球杆寄存服务,最开始的时候会给各密码锁的小柜子。自己设置好密码就可以了,然而,后来某一天去玩的时候发现球杆明显被人用过。之所以会被发现,是因为自己的球杆是粉色的,连巧克粉都是粉色的。球杆从杆盒里拿出来之后,却在皮头上看到了明显的蓝色的巧克粉使用痕迹。两次打球之间的时间间隔也有点长,调监控看谁用的感觉也不大值当的。也就不了了之,后来发现有人会把球杆寄存到前台。这次时间之后,也就把球杆寄存到了前台。之所以能被开锁是因为那个密码想有个超级密码,可以强制开启,而开启时候随便设置一个新的密码就能再锁上了。这也就解释了为什么之前有时候自己明明输对了密码却打不开箱子,明显是被改了密码。

 

比较尴尬的地方在于,这只球杆的杆盒有点长,粉皮的后备箱竟然塞不进去,唯一一个方向能塞进去那就是斜着,不过这样塞进去就没法放其他的东西了。所以现在车上放着是另外一只球杆。

多年以前在厦门的时候买的第一支球杆现在在大白上,买的第二支球杆送了别人。现在还有三只球杆,每辆车上一支,常去的球厅一支,现在球厅的球杆不能放了也就只能带着了。

到了台球厅前台,发现前台寄存的球杆竟然都不在了。问了下老板,老板说球杆都被放到寄存柜内了,问有没有加之前老板的微信可以问一下。

一通交涉之后,说寄存到时候应该都拍照了,让提供照片,他们去找。

随便拿根球杆先去玩着,过了半了多小时,有个哥们过来说,找到了,在15号柜。一通折腾之后,总算是找到了。拿出来之后,看了下批头,依然是有些蓝色的使用痕迹。

猜测应该是又被别人用了,看来是时候换个台球厅了。

今天中午终于没有剩饭需要消灭了,之前回老家晚上开车的时候因为玻璃太脏别了大奔。现在雨过天晴,该去洗洗车了,不然玻璃真的是看不清楚了,尤其是晚上开车,后方情况完全看不清楚。洗白白,晚上就能看清楚了,虽然每蹭到,但是回想起来仍心有余悸。

刚扫完码,过来一个大爷说,这台机器的显示屏坏了,但是不影响使用,直接按钮就可以了。显示屏到时无所谓,能用就完了。洗到一半,隔壁车位来了一辆开奔驰的小姐姐。

竟然也是直接一个人洗车~~

昨天以前首页

苟延残喘

作者 obaby
2026年4月10日 15:23

经历一轮一轮的裁员,这两年差不多每最长半年裁一次。研发人员的规模也终于裁到了原来的1/3,整天嚷嚷着使用ai提效,其实我觉得,按照现在的趋势和他们的逻辑,研发完全裁掉,就剩下几个领导,一个人养上20只龙虾,这就足够了,让龙虾24小时工作。

没到这个时候,走了不见得不好,留下来也不见得好。做得乱七八糟的项目,日常维护和新系统的开发都是问题,人少了,活多了。我总觉得过了长江就没个正常的所谓互联网公司,尤其是山东这个地界,的确是垃圾。在一轮轮的淘汰中,苟延残喘到了现在。一种用的那台破电脑也苟延残喘到了现在,每天都在提示磁盘空间不足,想办法清理一点之后,第二天继续提示,如噩梦一般挥之不去。

终于昨天实在受不了,重装了系统。

这256的硬盘,的确是小了点。

装完系统,安装价格必须的工具,就差不多占用了一半的磁盘空间。之前还给硬盘分了两个分区,现在看来,分不分的也没啥大用,就这点片空间,也不够干啥的。能往移动硬盘放的,就放移动硬盘把。现在常用的工具比之前也少了很多,只需要一个cursor、hbuilder 、xcode 之类的东西就够了,剩下的东西暂时也不需要安装,等哪天用到了再说吧。

从年前开始折腾,到现在又好几个月了,想着做点自己的事情,却一直没能抽出什么时间,google play就这么拖着,拖着。华为应用商店,商户号卡住了,也没什么进展,找技术支持也没解决,后续更是不知道如何处理,准备上个阉割版。不过最近折腾几个月,把三星应用商店给上了,也算是有那么一丢丢的进步。

https://apps.galaxyappstore.com/detail/ma.dayi.app?ads=ddb0e6f9&directOpen=true&nonOrgType=fce692ba&source=GBadge_01_8594313_tag

这几天连续折腾下来,也有点上火,对象牙疼了一个星期,现在感觉自己也开始牙疼了,昨天看了以下牙龈肿了。听他们说,这一轮流感病毒是专门让人牙疼的,这就让人有些抑郁了。牙疼一周,总是有些让人伤心。

至于这一波能再苟多久,谁也不知道。以后的事以后再说吧。

四月天

作者 obaby
2026年4月7日 13:55

清明节假期还是如约而至了,跟着一起来的还有临近下班的时候收到的一条竞岗通知,让假期最后一天十二点之前提交竞岗申请表。

下班收到这条通知,我并不想现在去填那个申请表,还要领导签字之后上交。由他去吧,到家之后看到手机又有一些消息,打开大概浏览了一下,意思是,如果无法提交纸质版,或者领导无法签字,可以先提交电子版。跟对象提及此事,她说,『该交就交吧。该走的流程还是要走的,如果视在不行,那就拿补偿走人,也挺好的。现在我觉得,你能不能继续干下去都挺好的,能干就干,不能干就走。』稍微顿了以下,继续说:『找时间咱们去看看把公积金提出来吧,我同事都提了好多次了。不过咱们的契税单子没了,不知道能不能提。』

『嗐,担心那个干嘛,直接用手机申请下不就知道了』我一边说,一边去拿手机。支付宝打开公积金小程序,一堆查询走下去,并且有什么异常提示。尝试直接提取,理由选择偿还商业贷款。一步步操作,到最后验证贷款的时候,选择贷款银行,最后竟然只需要一个贷款时间和贷款金额就能查询到贷款信息了,这一点做得的确不错。选择金额之后,点击提交申请,没有提示需要提交任何资料。提交完了才发现,忘了选择银行卡了。竟然填写的是多年以前的交通银行的银行卡。

对象担心银行卡还能不能用,索性直接登录手机银行,发现申请提现的金额已经到账了。现在该想一下怎么处理这笔钱了,第一步想着再去存个定期。然而,前段时间存的一张定期的单子,五年年利率只有1.75。打开兴业银行的贷款明细,看了下贷款利率石3.2。 这还存什么定期,干脆还款得了。

从兴业银行的手机银行再申请提前还款,于是这一笔钱就在手里打个转,过几天就又成了别人的了。看了下贷款信息,70万,还了十年,一共出去了30万,实际剩余的贷款还剩50万。相当于交了10万利息。不过反过来想,这十年租房子十万也不够,两室一厅的房子,十年房租下来也得二十多万了。这么一想,还是得感谢对象的眼光,房子买的早,想尽办法借钱买了这套房子,也得感谢自己不是犟种,觉得租房子也可,没坚持租房子。

至于假期,其实也没什么好的打算,还是带宝子回老家。四月天,正好是在户外放飞的时候。

晚上教练还问,第一天要不要上网球课。刚开始想着可以下课之后再走,晚上八点多开始下雨,十一点多的时候雨逐渐变大,路上也慢慢有了积水,既然如此,那室外的网球场第二天可能也没法打了,不如干脆请假。对象最近牙疼,假期也就不跟着回了,找时间去查一下看看怎么处理。

回老家的路上,前一段还算是比较顺利,绕行机场高速,绕过了最堵的市区高速。然而,等往青银转的时候,提示拥堵距离五公里,磨蹭到匝道入口才发现,车流量实在是太大了,只有一个车道,所有的车不得不慢慢悠悠的往里蹭。高速路况除了这一段,剩下的基本倒是顺畅,下了高速之后,开始另一段拥堵。高速工作人员指挥右转车辆走应急车道。左转的两个车道就只能等交警指挥放行了。

远远的就能看到省道上密密麻麻的车,下高速之后虽然之后十几公里的路程,却开了接近五十分钟,这还是最后到镇上之后抄小道绕过了一部分拥堵路段。

到家之后,宝子的姐姐躲在屋里的帐篷里。就是在室内又搭了一个帐篷,藏在里面。早上走的匆忙,也没买什么东西,让宝子给老太太一个红包,这件事算是过去了。在回来之前还给宝子的姐姐定了一份外卖,拉丝芝士棒,说想吃这个。六个芝士棒拿出来之后,实际他就吃了一个。剩下的几个到了中午才被其他人吃掉。

虽然晚上还在下雨,但是假期第一天的天气还算不错,虽然气温低了点。中午收拾好东西,去上坟。山路边的地里,已经又开始种满了杨树苗,几年前禁止耕地种树的决定,现在看来应该是又被废弃了。现在杨树的价格,却一言难尽,很多租地种树的甚至连租地的钱都挣不出来。尽管如此,还是有大面积的耕地被种满了树苗,好处是不用怎么管理,稍微去外面干点活,总是比种那点粮食收入能好很多。偶尔在破败的院落边上能看到一树桃花,娇艳欲滴。

下午孩子们在玩的时候,突然记起来去年买的那条绳子和滑轮。年前的时候在院子里玩过几次,受限于场地只能拴在门框上,另外一头拴在了墙上。现在孩子们比去年肿了,尼龙绳子又有一定的弹力,稍微一拉伸可能就拖到地上了。想着去户外的树林子重新搭一套滑道,试了几棵树都不大行。这时候姐姐提议用邻居宅基地里面的那几棵树,刚好一头比较高,另外一头比较低。

搬梯子拴上去,试了以下,刚好。

几个孩子就这么在这个简易滑索上玩了大半个下午,剩下的时间爬门楼的平台,通过那个手工捆绑的梯子,爬上来,爬下去。有时候还要带着猫咪一起爬。

坐在月台上,猫咪和小狗就在身边嬉闹。

不过并不是总是那么和谐,有时候狗子也会直接张大嘴咬猫咪的脑袋,这时候猫咪就只剩下望风而逃了。

在家的日子过的也快,转眼一下午的时间就没了。晚上宝子跟她姐姐挤在那个帐篷里睡了一晚上,虽然地方小,但是睡得挺好,第二天早上九点多过去看的时候,还没起。等起床洗刷完依然过了十点,连早饭都省了。

中午包水饺,宝子跟她姐姐一起上阵,包的饺子挺好的,有模有样,这时候二姐说到:『你想想办法,把抽屉的锁给弄开吧,钥匙丢了,已经半年多没开了。』

这个锁其实已经换过一次了,上次也是钥匙丢了。用钢锯条锯开之后,换了一把新的,这次,自然也是同样的方法。出门骑电动车到镇上五金店,买了五根锯条,一把锁,一共花了七块五。老板对于我怎么锯开写字台的锁表示很好奇,我解答说:『就那么直接把锯条伸进去锯就行了。』我说完,他依然一脸不可置信。

到家,带上一副厚手套,大约五分钟,在崩了三根锯条之后终于成功了。

拆掉旧的,装上新的。

不过,这次买的锁头稍微小了一点,周边留出很多缝隙。也无所谓了,能用就行。给老太太留下一把钥匙,另外一把钥匙找地方放了起来。以防哪天钥匙又丢了。

下午吃完饭,宝子们又嚷嚷着要爬梯子。但是鉴于之前扣车上苹果模型的熊孩子还没走,自己就把梯子给撤了。然而,过了不一会儿,宝子跑进来说,滑索坏掉了,拖到地上了。用屁股想也知道,肯定是那个熊孩子上去了,目测近一百斤的体重,那一根尼龙绳子怎么能提供那么大的张力。绳子已经被全部拉了下来,关键是还怕熊孩子玩的时候万一受伤,说都说不清楚,只好把滑索给拆掉了。

下午跟姐姐带着宝子们去外面溜达,小狗也一直跟着,一会儿跑的无影无踪了,不知道什么时候又跑了回来,乐此不疲。路边开满了野花,孩子们也去折了一些。

田里也有些许忙碌的身影,在整理田地。有的在浇麦子,一个熊孩子在低头跑来跑去,看着我们过去,前面有个小狗。熊孩子拾起一块石头开始去追狗子,朝着狗子扔了过去。好在没打中狗子,狗子跑回来了。

刚开始狗子并没发怒,看熊子过来,开始围着自己转圈跑,熊孩子就在后面追,追了一会儿熊孩子看追不上,捡起石头来继续扔狗子。这时候狗子明显怒了,停下来朝着熊孩子龇牙咧嘴,眼看如果熊孩子敢再扔的话,狗子就扑上去了。我只好喝退狗子,把熊孩子也训了一顿,让他赶紧走远点。

回去的时候,也不想再见到那个熊孩子,就直接带着他们下到了沟里,顺便弄了几根杨树条给他们扭了一个哨子。

村里但凡能种树的地方,都种满了杨树。自从没人种地之后,原本经常走的一些小路也就没了踪迹,只能沿着沟底前进。

原本在路上看到几株野果的乔木,想扒出来带回家种下去的,因为没走回头路也未能如愿。现在都开花了,可能哪怕带回家了也不容易成活吧。

夜晚总是如期而至,从来都不会迟到。吃完晚饭也就该回县城了,天黑之后,路况反而没那么拥堵了。

刚开始以为是车贴的膜太黑了,右侧总是看不清楚。路上别到一辆大本,过了一会儿,从右侧超了上来,打开窗户,超我一通比划,可能还有问候吧。不过我没开窗户,一句都没听到。大哥笔画半天之后,超到了自己前面,既然是自己做得不对。那就认怂认骂,老老实实的跟在后面。等到了一个右侧的岔路口,大哥先打了个右转向,又开了双闪。还以为这无牌大哥要停车跟自己干架呢,不过自己超过去之后,对方也没什么反应,应该是对方到了目的地了。停车之后,才发现右侧车床应该是自己用湿巾擦玻璃上的鸟屎的时候,没擦干净,反而抹的那一块更加不清楚了。

把身上的衣服换下来,全部扔到洗衣机,让宝子去洗澡。衣服上占满了猫毛,狗毛,粘了半天也没粘干净,最后直接扔洗衣机给洗了。而至于鞋子,只能等回家之后再洗了。洗衣店的会员卡,基本都用来洗鞋了,价格也挺合适的9.9一双。

最后一天,八点多宝子还没醒。过去把她叫醒,洗刷吃饭,开始往回赶,毕竟作业没写完,下午还有网球课。两天疯玩,体力消耗也蛮厉害的。只是玩的时候从来都不会觉得累吧。

有时候真的羡慕这样的童年,有陪自己一起折腾的父母,也有长时间的陪伴。只是,现在,还是得为了工作绞尽脑汁,甚至有可能哪一天依然需要背井离乡。

人间四月天,总是生机勃勃,至于明天不确定的事情,明天再说吧。

Baby Anti-Spam 自建反垃圾评论系统

作者 obaby
2026年4月6日 18:05

很久之前,就经常收到akismet的授权提醒,对应一个错误码10010。

刚开始还以为是多域名访问导致的授权校验出问题了。后来换了n个key,同时添加了插件hook掉所有的垃圾评论检测逻辑,让全部走统一的域名,结果前几天又收到这个提醒了。

插件代码:

<?php
/**
 * Plugin Name: Akismet 单一主域名(多域名站点)
 * Description: 当站点配置了多个域名时,强制发往 Akismet 的请求只使用一个主域名,避免被计为多站点触发 10010。
 * Version: 1.0
 * Author: obaby
 *
 * 使用:在下方设置 AKISMET_CANONICAL_HOME 为主域名(或留空则用 WordPress「设置」里的站点地址)。
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * 主域名(规范 URL,不要末尾斜杠)。留空则使用 get_option( 'home' )。
 * 例如: https://www.example.com
 */
if ( ! defined( 'AKISMET_CANONICAL_HOME' ) ) {
    define( 'AKISMET_CANONICAL_HOME', 'https://zhongxiaojie.cn' );
}

/**
 * 获取发往 Akismet 时使用的唯一主域名 URL。
 */
function akismet_single_domain_get_canonical_home() {
    $home = AKISMET_CANONICAL_HOME;
    if ( $home === '' || $home === null ) {
        $home = get_option( 'home' );
    }
    return untrailingslashit( $home );
}

/**
 * 将任意 URL 替换为使用主域名的版本(只改 host,保留 path/query)。
 */
function akismet_single_domain_normalize_url( $url, $canonical_home ) {
    if ( empty( $url ) || ! is_string( $url ) ) {
        return $url;
    }
    $parsed = wp_parse_url( $url );
    $canon  = wp_parse_url( $canonical_home );
    if ( empty( $canon['scheme'] ) || empty( $canon['host'] ) ) {
        return $url;
    }
    $scheme = isset( $parsed['scheme'] ) ? $parsed['scheme'] : $canon['scheme'];
    $host   = $canon['host'];
    $path   = isset( $parsed['path'] ) ? $parsed['path'] : '/';
    $query  = isset( $parsed['query'] ) ? '?' . $parsed['query'] : '';
    $frag   = isset( $parsed['fragment'] ) ? '#' . $parsed['fragment'] : '';
    return $scheme . '://' . $host . $path . $query . $frag;
}

/**
 * 统一 verify-key / get-subscription / get-stats 的 blog 为主域名。
 */
add_filter( 'akismet_request_args', function ( $request_args, $path ) {
    $paths = array( 'verify-key', 'get-subscription', 'get-stats' );
    if ( ! in_array( $path, $paths, true ) ) {
        return $request_args;
    }
    $canon = akismet_single_domain_get_canonical_home();
    if ( ! empty( $request_args['blog'] ) ) {
        $request_args['blog'] = $canon;
    }
    return $request_args;
}, 10, 2 );

/**
 * 统一 comment-check(以及 recheck)的 blog、permalink,并把请求里的 HTTP_HOST 等改为主域名。
 */
add_filter( 'akismet_request_args', function ( $request_args, $path ) {
    if ( $path !== 'comment-check' ) {
        return $request_args;
    }
    $canon = akismet_single_domain_get_canonical_home();
    $parsed = wp_parse_url( $canon );
    if ( empty( $parsed['host'] ) ) {
        return $request_args;
    }
    $canon_host = $parsed['host'];

    $request_args['blog'] = $canon;
    if ( ! empty( $request_args['permalink'] ) ) {
        $request_args['permalink'] = akismet_single_domain_normalize_url( $request_args['permalink'], $canon );
    }

    // 让服务端看到的“当前请求”也统一为主域名,减少被计为多站点
    if ( isset( $request_args['HTTP_HOST'] ) ) {
        $request_args['HTTP_HOST'] = $canon_host;
    }
    if ( isset( $request_args['REQUEST_URI'] ) ) {
        $uri = $request_args['REQUEST_URI'];
        $request_args['REQUEST_URI'] = ( is_string( $uri ) && ( $p = wp_parse_url( $uri, PHP_URL_PATH ) ) !== null ) ? $p : '/';
    }
    if ( isset( $request_args['DOCUMENT_URI'] ) ) {
        $uri = $request_args['DOCUMENT_URI'];
        $request_args['DOCUMENT_URI'] = ( is_string( $uri ) && ( $p = wp_parse_url( $uri, PHP_URL_PATH ) ) !== null ) ? $p : '/';
    }
    return $request_args;
}, 10, 2 );

/**
 * 统一 submit-spam / submit-ham 的 blog、permalink。
 */
add_filter( 'akismet_request_args', function ( $request_args, $path ) {
    if ( ! in_array( $path, array( 'submit-spam', 'submit-ham' ), true ) ) {
        return $request_args;
    }
    $canon = akismet_single_domain_get_canonical_home();
    $request_args['blog'] = $canon;
    if ( ! empty( $request_args['permalink'] ) ) {
        $request_args['permalink'] = akismet_single_domain_normalize_url( $request_args['permalink'], $canon );
    }
    return $request_args;
}, 10, 2 );

这次授权的密钥撑得时间稍微长了点,但是最终还是收到了这个提醒,意思是需要订购商业版授权。我这个人站点为了发垃圾评论订购一个商业版授权,确实有些难以接受。

于是,我决定自建反垃圾评论系统,基于scikit-learn实现了现在的这套垃圾评论检测系统,训练数据一部分来源于github的开源数据,另外一个就是我自己博客的评论数据。为了保证样本正例和负例数量差别不至于过大,经过各种方式进行了多轮数据清洗。

如果想要评论识别更加准确,可以提供自己的博客评论数据,如果能提供垃圾评论更好。现在欠缺的主要是垃圾评论数据,正常的评论数据我已经提供几千条数据。

效果测试:

测试地址:https://anti-spam.zhongxiaojie.cn/test/spam

简介:

面向 中英混合 评论的 WordPress 垃圾识别方案:PHP 插件在评论入库前调用 本机 Python 服务,由小型多语种向量模型 + 分类器(或演示用规则)给出垃圾概率。

适合评论量不大、单机部署(例如 4 核 / 8GB RAM 的 Ubuntu),服务与 WordPress 同机时使用 127.0.0.1 即可。

目录结构:

baby_anti_spam/
├── README.md
├── screenshots/             # 文档:服务启动与 curl 自测示意
│   ├── service.png
│   └── test.png
├── service/                 # Python FastAPI 侧车服务
│   ├── .env.example
│   ├── requirements.txt
│   ├── requirements-ml.txt
│   ├── run.py
│   ├── app/
│   │   └── stats_backends/   # 统计存储:sqlite / mysql
│   └── scripts/
│       ├── init_stats_mysql.sql
│       └── init_stats_mysql.py
│       ├── train_sklearn.py
│       ├── download_embedding_model.py
│       └── download_embedding_model.sh
└── wordpress/baby-anti-spam/
    └── baby-anti-spam.php # WordPress 插件

关键配置:

| 变量 | 说明 |
|------|------|
| `SPAM_HOST` | 监听地址,同机建议 `127.0.0.1` |
| `SPAM_PORT` | 端口,默认 `8765` |
| `SPAM_API_SECRET` | **单密钥模式(兼容旧版)**:未配置 `SPAM_API_KEYS` 且未配置 `SPAM_API_KEYS_FILE` 时,仅此密钥有效,等价于 name=`default`、不限流(`max_rpm=0`)。与 WP 插件里填写的密钥一致 |
| `SPAM_API_KEYS` | **多密钥**:JSON 数组。每项为 `name`(唯一,用于统计与限流分组)、`key` 或 `secret`(与请求头一致)、`max_rpm` 或 `rpm`(每分钟最大请求数,`0` 表示不限制)。与 `SPAM_API_KEYS_FILE` 合并时:**先读文件条目,再追加本变量** |
| `SPAM_API_KEYS_FILE` | 可选,指向 JSON 文件,根节点为与上表相同结构的**数组**。文件必须存在,否则进程启动失败 |
| `SPAM_MODEL_PATH` | 训练得到的 `*.joblib` 路径;留空则取决于 `SPAM_FALLBACK_RULES` |
| `SPAM_FALLBACK_RULES` | 无模型文件时是否启用内置极简规则(演示用);生产训练后应设为 `false` 并配置 `SPAM_MODEL_PATH` |
| `SPAM_LABEL_THRESHOLD` | 可选,默认 `0.8`。`spam_score` ≥ 此值时 JSON 中 `label` 为 `spam`,否则为 `normal` |
| `SPAM_DFA_ENABLED` | 默认 `true`。为 `true` 时使用 `dfa-python-filter/keywords` 做敏感词检测;命中则直接 `spam_score=1`、`detail=dfa_sensitive`(早于 sklearn) |
| `SPAM_DFA_KEYWORDS_PATH` | 可选,自定义敏感词文件路径;留空则用 `service/dfa-python-filter/keywords` |
| `SPAM_NON_CHINESE_FLOOR_ENABLED` | 默认 `true`。为 `true` 时若合并后的 author/email/url/text 中**无任何 CJK 表意字符**(主要针对中文训练语料),则将 `spam_score` **至少**抬到 `SPAM_NON_CHINESE_SPAM_FLOOR` |
| `SPAM_NON_CHINESE_SPAM_FLOOR` | 默认 `0.9`。与上项配合,在「无中文」评论上与 sklearn / 规则分取 `max` |
| `SPAM_STATS_ENABLED` | 默认 `true`。为 `true` 时记录每次**成功**返回的 `/v1/classify` 请求与响应(失败 / 401 不落库),并允许 `/v1/mark-spam` 写入 `spam_marks` 表 |
| `SPAM_STATS_BACKEND` | `sqlite`(默认)或 `mysql`。选 `mysql` 时需安装 `pymysql`(已在 `requirements.txt`)并配置下方 MySQL 变量 |
| `SPAM_STATS_DB_PATH` | 仅 `sqlite`:数据库文件路径;留空则为 `service/data/stats.sqlite`(已加入 `.gitignore`) |
| `SPAM_STATS_MYSQL_HOST` / `SPAM_STATS_MYSQL_PORT` | 仅 `mysql`:默认 `127.0.0.1` / `3306` |
| `SPAM_STATS_MYSQL_USER` / `SPAM_STATS_MYSQL_PASSWORD` | 仅 `mysql`:连接账号(`user` 必填) |
| `SPAM_STATS_MYSQL_DATABASE` | 仅 `mysql`:库名(必填),默认示例 `baby_spam_stats` |
| `SPAM_STATS_MYSQL_CHARSET` | 仅 `mysql`:默认 `utf8mb4` |

 

系统服务启动截图:

wp插件配置:

项目地址:https://anti-spam.zhongxiaojie.cn

代码地址:https://cnb.cool/oba.by/baby-wp-anti-spam

说明:如果自己不想训练数据,下载发布版的spam_pipeline.joblib 放入指定目录下配置服务启动即可,baby-anti-spam.zip 为wp插件。

训练耗时大约11分钟:

下载微信公众号的视频

作者 obaby
2026年4月2日 16:46

作为一个专业的程序媛,前端时间折腾龙虾转发公众号的文章到闺蜜圈wiki,之前已经处理了图片和文章的问题,今天转发的时候发现另外一个问题:文章里面的视频无法正常播放。

刚开始的时候想着直接去chrome的缓存里面找,但是试了下chrome://cache发现无效,又不想去找插件来干这件事情。直接去调试工具找对应的视频地址:

然而直接贴到地址栏,直接报403了。

唉,好尴尬,既然有本地缓存文件了。那么直接尝试将接收到的数据流写入到文件呗。找了半天没发现怎么直接把请求到的数据写入到文件,点击开始播放等待缓冲结束。

加载完了右下角的数据也就有了,直接切换成base64,复制粘贴:

然而,尝试decode 之后,播放不了,缺少mp4的头文件,这就挺奇怪的。文件头哪里去了?my_video为通过代码下载的mp4,video为通过base64 处理的图片。

文章测试地址:https://mp.weixin.qq.com/s/heoer_zm4SFwFKsk4tRecQ

看了下是video标签实现的:

<div data-v-c66e8e28="" class="js_inner inner not_fullscreen"><div data-v-c66e8e28="" class="js_video_poster video_poster"><div data-v-c66e8e28="" class="video_mask"></div><video data-v-c66e8e28="" src="https://mpvideo.qpic.cn/0bc3pidsgaahauamxiglsruvo6wden5aoiya.f10002.mp4?dis_k=247900efb8791f0718998ea0813793c9&amp;dis_t=1775118363&amp;play_scene=10120&amp;auth_info=d9/5u/dlYUBWn6qY0Sp2SXM9PUdEOj5CZmQ3H2k2TzNOXXtjTwYQen0+WTMXEzdWIDNuS0hkIHgTMSlENWAcfUpBcQ==&amp;auth_key=ed4a91866522f27b4b89c5e71e04d115&amp;vid=wxv_4453415887525888005&amp;format_id=10002&amp;support_redirect=0&amp;mmversion=false" poster="http://mmbiz.qpic.cn/sz_mmbiz_jpg/GAVxEAgJstytcf0uF3dpdZKia9G96C3loxCNaBrbFLHCiak3GvJDfASC7uYqNjjAZ5e2OHSmHoBQrONRJ8UIq6icJjjFXMfUBtdhy7VWlfb3MM/0?wx_fmt=jpeg&amp;wxfrom=16" webkit-playsinline="isiPhoneShowPlaysinline" playsinline="isiPhoneShowPlaysinline" preload="metadata" crossorigin="anonymous" controlslist="nodownload" class="" style="display: block; width: 655px; height: 492px;"> 您的浏览器不支持 video 标签 </video></div><div data-v-f4ee5450="" data-v-c66e8e28="" class="video_poster__info__play" style="display: none;"><i data-v-f4ee5450="" data-v-c66e8e28="" class=""></i></div><div data-v-f4ee5450="" data-v-c66e8e28="" class="video_poster__info" style="display: none;"><p data-v-f4ee5450="" data-v-c66e8e28="" class="video_poster__info__title" style="font-size: 17px;">继续观看</p><p data-v-f4ee5450="" data-v-c66e8e28="" class="video_poster__info__desc" style="font-size: 12px;"> 孤独症,就是不爱说话吗? </p></div><div data-v-f4ee5450="" data-v-c66e8e28="" class="video_poster__info__mask" style="width: 100%; display: none;"></div></div>

还是说着这个东西还有另外的处理逻辑?哪位大神知道原因还望不吝赐教。

既然decode不行,那就直接上代码吧:

#!/usr/bin/env python3
"""
下载 mpvideo.qpic.cn 等需 Referer 的 MP4(微信视频 CDN)。

Author: obaby
  https://zhongxiaojie.cn
  https://oba.by
"""

import argparse
import sys
import urllib.error
import urllib.request

# 与常见微信内嵌页一致,避免 403
DEFAULT_REFERER = "https://mp.weixin.qq.com/"
DEFAULT_UA = (
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
    "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 "
    "MicroMessenger/7.0.20"
)


def main() -> None:
    p = argparse.ArgumentParser(description="带 Referer 下载 mpvideo MP4")
    p.add_argument("url", help="完整 mp4 URL(含查询参数)")
    p.add_argument(
        "-o",
        "--output",
        default="downloaded.mp4",
        help="保存路径(默认 downloaded.mp4)",
    )
    p.add_argument("--referer", default=DEFAULT_REFERER, help="Referer 头")
    p.add_argument("--user-agent", default=DEFAULT_UA, help="User-Agent")
    args = p.parse_args()

    req = urllib.request.Request(
        args.url,
        headers={
            "User-Agent": args.user_agent,
            "Referer": args.referer,
        },
        method="GET",
    )
    try:
        with urllib.request.urlopen(req, timeout=120) as resp:
            data = resp.read()
    except urllib.error.HTTPError as e:
        print(f"HTTP {e.code}: {e.reason}", file=sys.stderr)
        sys.exit(1)

    out = open(args.output, "wb") if args.output != "-" else sys.stdout.buffer
    try:
        out.write(data)
    finally:
        if out is not sys.stdout.buffer:
            out.close()
            print(f"已写入 {args.output},{len(data)} 字节")
            if len(data) >= 8 and data[4:8] == b"ftyp":
                print("魔数检测:疑似标准 MP4(含 ftyp)")


if __name__ == "__main__":
    main()

现在就可以下载之后,上传了,发布的文章地址:

孤独症,就是不爱说话吗?

💾

弱弱的问一下,我的网站怎么被镜像了嗫?

作者 obaby
2026年4月1日 11:18

其实网站被镜像这件事情,本身没什么稀奇的,如果想搭建一个镜像网站,从零开始也不过个吧小时的时间。

之所以写这个东西,是因为最近有看到好几个人被镜像的,这一个(爱娃子),还有 这一个(我是军爸)。

不过,既然还有人有疑惑,那就简单的教一下大家怎么来镜像个网站吧。

为此,我创建了一个开源项目:

OpenResty + OpenCC 反向代理简繁转换


基于 OpenResty 反向代理上游站点,对 HTML 正文 做 OpenCC 简繁转换(默认:简体 → 繁体,配置文件为 s2t.json)。适合在不改源站的情况下,为访客提供另一种字体习惯版本。

功能概览

能力 说明
反向代理 HTTPS 回源(示例站点:zhongxiaojie.cn),客户端走本机证书与域名。
HTML 简繁转换 仅当 Content-Type 含 text/html 时对整页做 OpenCC UTF-8 转换。
gzip 解压 通过 Lua zlib 尝试解压响应体(与去掉 Content-Encoding 的配合视上游行为而定)。
链接与图片 URL 保护 转换前将 href / src / poster / data-src / srcset 及裸 http(s):// 链接替换为占位符,转换后还原,避免路径或查询串中的汉字被改写导致 404
IPv4 优先解析 resolver … ipv6=off + 变量 proxy_pass,减轻云主机无 IPv6 时对 AAAA 连接失败的问题。
静态资源直过 图片、CSS、JS、字体等扩展名单独 location不做 OpenCC,减轻负担、避免误伤二进制。
动态库加载 对 libopencc.so 按常见路径依次尝试 ffi.load,降低找不到共享库的概率。

限制与说明

  • JSON / JS / CSS 内嵌字符串若不在上述保护规则内,仍可能被转换;重要数据建议不要用全文 HTML OpenCC 硬转。
  • 内联样式 style="background:url(...)" 未单独做保护,若遇少数破图可再扩展规则。
  • 转换配置在 nginx/opencc/opencc-filter.lua 中的 OPENCC_CONFIG(默认 /usr/share/opencc/s2t.json);若需 繁体 → 简体 可改为 t2s.json 等(需系统已安装对应 OpenCC 数据文件)。

部署要求

  • OpenResty(带 lua-nginx-module)。
  • OpenCC 运行时:系统安装 libopencc.so 与词典数据(如 /usr/share/opencc/*.json),并保证 worker 进程能加载到 .so(见下文「共享库」)。
  • Lua 可 require('zlib') 的模块(用于 zlib.inflate,若无 gzip 体则 pcall 失败会跳过解压,不影响后续逻辑)。
  • 上游为 HTTPS 时,本机需能解析并访问该域名(已用 resolver 时 VARIABLE 形式 proxy_pass 才会走指定 resolver)。

部署步骤

1. 安装 OpenCC 与数据文件

以 Debian / Ubuntu 为例(包名因发行版略有差异):

sudo apt update 
sudo apt install -y libopencc1.1 opencc # 或 libopencc2 等,以仓库为准 
或者手工复制 lib64目录下的文件到 脚本对应的路径就是这个 /usr/lib64

 

确认存在词典,例如:

ls /usr/share/opencc/s2t.json

 

2. 确保能找到 libopencc.so

若日志出现 libopencc.so: cannot open shared object file

  • 将库放在系统默认搜索路径,例如 Ubuntu amd64:
  • ldconfig -p | grep opencc

     

  • 若库仅在 /usr/lib64 等非默认路径,可执行(与仓库 fix.md 一致):
  • echo '/usr/lib64' | sudo tee /etc/ld.so.conf.d/usr-lib64.conf sudo ldconfig

     

  • 或在 OpenResty 的 systemd 单元 中设置 Environment="LD_LIBRARY_PATH=/usr/lib64:/usr/local/lib"  后重启。

脚本内已对多路径做了 ffi.load 尝试;仍失败时请对照 ldd 与 opencc 包实际安装位置排查。

3. 部署 Lua 脚本

将 nginx/opencc/opencc-filter.lua 复制到服务端约定路径(与 nginx 配置一致),例如:

sudo mkdir -p /usr/local/openresty/lua 
sudo cp nginx/opencc/opencc-filter.lua /usr/local/openresty/lua/opencc-filter.lua

按需修改脚本顶部 OPENCC_CONFIG 指向本机实际的 JSON 配置。

4. 合并 Nginx / OpenResty 配置

  • 将 zero.zhongxiaojie.cn.conf 中的 server 块纳入主配置(include 或粘贴到 nginx.conf 的 http {} 下)。
  • 修改 证书路径日志路径上游域名 zhongxiaojie.cn、以及 body_filter_by_lua_file 的路径,使其与当前环境一致。
  • header_filter_by_lua 中去除 Content-Encoding,便于对明文 HTML 做处理;若上游与解压逻辑不匹配,需自行观察是否需要调整。

5. 校验并重载

sudo /usr/local/openresty/nginx/sbin/nginx -t 
sudo /usr/local/openresty/nginx/sbin/nginx -s reload 
# 或 systemctl reload openresty

 

6. 验证

  • 浏览器访问你的站点,查看页面简繁是否符合预期。
  • 检查 图片与站内链接是否正常(尤其含中文或 % 编码的路径)。
  • error.log 中不应再出现 OpenCC 库加载失败或大量 IPv6 unreachable(在无 IPv6 环境)。

配置项速查

项目 位置
OpenCC 配置 JSON opencc-filter.lua → OPENCC_CONFIG
Lua 脚本路径 zero.zhongxiaojie.cn.conf → body_filter_by_lua_file
上游站点 set $upstream_host … 与 proxy_pass https://$upstream_host$request_uri
DNS / 仅 IPv4 resolver 223.5.5.5 8.8.8.8 valid=300s ipv6=off
不参与转换的静态文件 `location ~* .(gif

故障排查

现象 可能原因
libopencc.so 找不到 未安装包、ldconfig 未包含库目录,或需 LD_LIBRARY_PATH
body_filter 报错、栈指向 ffi.load 同上;或架构不一致(如 32/64 位混用)
上游连接 IPv6 失败 已用 ipv6=off + 变量 proxy_pass;仍失败则检查防火墙与 DNS
图片 404 历史上多为 OpenCC 改了 URL 内汉字;当前脚本对常见属性已做保护,若仍有个别,检查是否来自 CSS url() 或 JS 动态拼接

如需改为其他域名、证书路径或 t2s 转换方向,只需改配置文件与 OPENCC_CONFIG,无需改 OpenResty 核心。

实际效果:

开源项目地址:https://gitee.com/obaby/baby-website-mirroring-tool

参考链接:https://blog.csdn.net/wzj_110/article/details/127758020

https://blog.rexskz.info/support-traditional-chinese-using-openresty-and-opencc.html

局部有雨

作者 obaby
2026年3月30日 11:00

春天到了,又到了万物复苏的季节。小草也从土里钻出了头,露出了点点的绿色。树上的花也开始绽放,虽然没有绿叶的衬托,倒是也别有一番韵味。

每个周末,都大同小异。跟上班一样,也没有什么特别的,与渐渐生机勃勃的春天比起来,似乎有些过于平淡了。每周依然是带着宝子上课,上课,剩下偶尔有那么一点时间,也不能走的太远,只能周边小范围的溜达溜达。

宝子现在上课不太需要自己去帮忙了,至少能隔网之后,不用自己跟着去抛球了。这样就有点时间可以继续跑步了,围着网球场一圈一圈的跑。

不过相比操场的400米跑道,这一圈一百多米跑起来感觉更累,感觉跟拉磨的驴一样。

周日上午的网球课推迟到了十一点,早上早点起来,宝子嚷嚷着去公园,我嚷嚷着去轮渡。当然,公园是上午去,轮渡是下午去。对象再次征求宝子的意见,得到的答复是两个都去。

急忙吃完东西,开车去公园。上车之后发现前挡风玻璃上面有点雨点,想着是不是要下雨,跟宝子说,下雨了可能。她表示怀疑,没感觉到雨点啊。不过既然没感觉到,那就去公园吧,一路上虽然天依然阴沉,刚停好车,拉开车门的瞬间,一阵大雨落了下来,赶忙重新钻进车里。前后不到五秒中的时间,就让这场公园之旅化为了泡影。

回家之后,宝子在家换上轮滑鞋,来回溜达。想着,既然下雨了,应该网球课也停了吧,然而等到十点半依然没消息。既然如此,那可能市北没有下雨吧。一路上天变得越来越晴朗,到了学校之后依然是一篇艳阳天。

下课去学校餐厅吃点东西,下午就可以去轮渡了。

虽然是中午的饭店,想找个空位置还是蛮方便的,买了两碗麻辣烫,没有小碗。去隔壁的卤肉饭要了个小碗。餐厅门口从外面能感受到吹进来的真真的凉风。吃完饭,到了室外反而变得更加的温暖,与餐厅的温度完全不同。

一路小道辗转到环湾大道,路况还是比较畅通的,半个小时就到了轮渡停车场。怕找不到停车位,在入口找了个空塞了进入。往里走,大约100米发现内部竟然有一个大的停车场,并且还有大量的空位。

候船厅入口,写了一些方言的标语。

虽然购票的时候显示有场次,但是实际买的时候,就是看人数,人够了就发船。感觉到了县城做镇上的客车的感觉,流水车,流水船。检票的时候会给一个油条,这个油条并不是给游客吃的,是给海鸥吃的。

这个码头,在海底隧道和跨海大桥开通之后就已经停了好几年,这两年才又重新开放,用来做海上观光。成人票两个人90,宝子2块,价格还是蛮合适的,海上50分钟的观光,说可以看到跨海大桥。

检票上船,刚找地方坐下,就有工作人员开始喊,大家可以去喂海鸥了。喂海鸥只能在一楼,二楼是观光区域,油条投喂完之后可以上二楼。带着宝子来到船尾,大家都在那里观望,也不知道在等什么,我扔了一块油条出去之后,马山就有无数的海鸥飞过来开始争抢。大家也跟着开始往海里投喂。

发船之后,海鸥依然跟着游船,甚至能感受到海鸥翅膀煽动的时候,甩出来的水滴。

还有跟着游船一直来回盘旋,不断的争抢从游客手里抛出的油条。

当然啦,如果足够大胆,也可以直接用手拿着让海鸥去吃。

不过,这天气稍微微差点了,大约半小时不到,就开始掉头返航了。说好的看跨海大桥呢?海面的能见度并不算高。只能隐约看到一个轮廓,远处的货轮也一样,飘渺。

回港之后,依然能看到很多人在等着检票。如果五点多应该能看到海上的日落,很多人也在等那个最好的时间。

海上的冷风吹来,单穿一条牛仔裤还是冷了点。

好在没有因为家里的一场雨而不出门,毕竟其他的地方是一片艳阳天,不过局部有雨。

m3u8 downloader v26.03.28

作者 obaby
2026年3月29日 21:58

基于 ffmpeg 的 m3u8 / 归档页解析下载工具,支持单链接、批量 CSV/文本、自定义输出目录与 ffmpeg 路径。

参数 说明
-i 输入:直链 m3u8、含 archives 的归档详情页、或带 cms_player 的播放页;也支持 .mp4 / .avi / .mov / .mpeg 直链(走 HTTP 分段下载,非 ffmpeg)
-o 输出文件名(自动补 .mp4);对部分模式用于覆盖默认标题名
-p 输出目录;可为本机路径或 UNC(如 \\服务器\共享\目录
-f 批量输入文件:.csv 或纯文本(每行一个链接)
-m 指定 ffmpeg 可执行文件路径

注意: 必须提供 -i 或 -f 之一。

单条示例

python m3u8_downloader.py -i "https://example.com/video.m3u8?token=xxx" -o myvideo -p D:\Videos

 

批量 CSV

 

  • 编码建议 UTF-8(含 BOM 亦可),表头需包含列 link;可选列 name(用于在部分逻辑里拼接输出名后缀,空单元格按空字符串处理,不会出现 nan 文件名)。
  • 会跳过空的 link 或内容为 link 的占位行。
  • 按行 顺序执行:上一任务结束(含 ffmpeg)后才会处理下一行。
python m3u8_downloader.py -f list.csv -p "\\192.168.1.12\media\Videos"

 

纯文本列表

 

每行一个 URL,行为与逐次 -i 类似;可通过 -o 传入统一输出名(视链接类型而定)。

功能说明

 

链接类型与行为

 

  1. .m3u8 直链
    使用 ffmpeg 拉流并 remux 为 .mp4-c copy)。
  2. URL 中含 archives(归档站 / bl05 类页面)
    请求页面,解析多个 div.dplayer 的 data-config,得到多路 m3u8;按路依次下载,文件名一般为 页面标题 + 序号,并经 safe_mp4_filename 净化。
  3. 其它播放页(非 archives)
    解析 var cms_player = {...} 的 JSON,取 url 作为 m3u8,再走 ffmpeg。
  4. .mp4 等直链
    使用 requests 分段下载(file_download.download_from_url),带 tqdm 进度条,不经过 ffmpeg

页面编码

 

抓取 HTML 时依次尝试 UTF-8 → UTF-8-sig → gb18030,减轻标题乱码。

输出文件名

 

  • 去除 Windows 非法字符,过长截断并可带 crc 后缀兜底。
  • 对「UTF-8 被误当成 latin-1」类乱码标题做启发式纠正(日志中可能出现 [F] 标题疑似 UTF-8/latin-1 乱码,已尝试纠正)。

覆盖策略

 

  • 若目标 .mp4 已存在os.path.isfile),跳过该次下载。
  • 调用 ffmpeg 时带 -n:不在终端交互询问覆盖;若因路径编码等导致检测不一致,ffmpeg 侧也会拒绝覆盖而非静默覆盖。

本地 m3u8 文件

 

-f 指向 .m3u8 / .m3u 时,当前代码分支为占位(pass),尚未实现。

目录说明

 

路径(相对程序目录) 用途
bin/ffmpeg.exe Windows 打包/放置 ffmpeg 的常见位置
download/m3u8_files 内部与 make_dir 相关的子目录逻辑
download/mp4 未指定 -p 时的默认输出目录

下载地址:

https://www.123912.com/s/ucY7Vv-njQAA?pwd=HwGK#

简单的快乐

作者 obaby
2026年3月27日 16:03

过年的时候买了两条牛仔裤,虽然是过年买的,但是有一条却并没怎么穿。主要是因为那是一条洞洞裤,穿洞洞裤里面穿的太厚了总是感觉有些奇怪。

洞洞裤最好的搭配可能就是薄裤袜或者光腿了,有时候总是看到什么各种短视频,见男朋友的最高礼仪-裤里丝。

不过可能更多男生喜欢的是黑丝吧。老妈看着自己的裤子,还以为是穿破了。用手指扣进去扣了一下,转头问我:『裤子坏了,你还不扔了?买不起裤子了吗?要不我给你缝一下?』

她总是说,你俩挣得也不少啊,买个东西扣扣搜搜的。多买几条裤子不行啊,别买那些几十块钱的鞋,太烂了。

有时候不得不承认,我的快乐就是来源于那些便宜的小玩意儿,哪怕穿几天坏了,扔了都不心疼。不过买东西的时候,宝子和她姥姥的眼光的确毒辣,给她们买衣服买鞋子,总是一眼就能相中那个最贵的。至于自己的日常穿搭,一百块钱的牛仔裤,500的鞋子,两百的衬衣,600的外套。内搭就不算了,这几本就是全部了,但是这些东西能穿好久,尤其是外套已经穿了好几年了。

有时候去逛街其实也不知道要买什么,就是纯粹瞎逛,基本也不会买什么衣服。等到换季的时候,偶尔买那么一两件。或者干脆从网上买,两百块钱买好几件,喜欢的留下,不喜欢的退回去。

周末带宝子去吃米线的时候看到的那家牛杂店,终于前天没忍住还是去了。看了一眼没地方停车,把车停到了对面充电停车场,于是在一片电车中间,混进了一辆纯油车。进入店里,虽然是午饭时间,店里只有两个大哥。

盯着菜单看了好久,才看到纯牛杂,小份35。

又要了一个肉夹馍,一瓶可乐,一共51块。等了半天,终于上了,不得不说,这小份真的不大的样子。刚开始害怕吃多了,影响自己减肥,后来才发现是自己想多了。

而至于味道,也没吃出小时候的味道。吃完出门给对象发消息说,『我去吃周末看到的那个店里的牛杂了,味道没有自己预想的那么好。』

发完消息,等红灯,过马路,这时候看到停车场的告示牌上写着:『中午11点-下午2点,充电高峰期禁止燃油车入内……』

这我往里进的时候自动放行了,也确实没注意,赶紧上车准备开走。这时候手机响了一声,拿起来看到一条微信消息:『那你以后还会再去吗?』

迟疑了一下,我回复:『不会去了,还不如我自己从二手东买处理好的,我自己煮。』

下午,从二手东买了之前看的牛杂,127三斤。

到了晚上依然是少吃,运动。白天走的少了,晚上就多跳几个,白天步数多的时候就可以少跳几个,目标就是凑够一万步。

跳习惯了之后,其实至于现在三千,五千,还是七千,感觉区别都不大,唯一的区别也就在于时长了。有时候跳完感觉还是有点紧绷的感觉。

偶尔也想能全身再按一下,上次按摩应该还是去年十月份左右吧。转眼快半年了,早上从高德上扒拉了半天,找到一个相对来说看着还算可以的。

最起码那个名字看起来还满顺眼的。

鉴于第一次去,没有选择太复杂的项目。不过也有不靠谱的地方,那个巨幕是个啥,确实没看到。刚开始趴在床上,技师按的挺舒服的,感觉都快睡着了。等开始按小腿的时候,一下就给按醒了,还是挺疼得。两个腿都是如此,聊了会儿,建议经常运动还是要多按一下。

为了按摩,中午吃饭的时间就没了。70分钟,甚至已经超了下午上班的点了。

喝点水,顺便看了下价目表,嗯,下次可以做个全身精油按摩,价格貌似跟之前经常去的那家店差不多。

按完还完衣服赶紧往公司赶,店外的停车场也挺让人崩溃的,一遍有台阶,整体过道也比较窄,好在车不多,来回倒腾十来次才从停车场开出来。

路上等红灯的时候,遇到一辆typeR,至于是不是真的,其实我也不大认识。后来着急从左侧超了过去,等上立交桥的时候发现那辆typeR紧贴在后面,不是,姐姐我不是想飙车啊,我是真的到上班点了。

直到自己从从主路转到辅路他还紧贴在后面,但是,姐姐我到啦。

停好车往电梯口走,发现小腿依然有点疼,可能是长时间不按,不习惯了吧。

哼,还是得多按几次。

绽放

作者 obaby
2026年3月25日 17:19

我要变成野花开在你身边
将我摘走吧在枯萎之前  — 戴娆 《绽放》

周末带宝子去上课的时候,竟然又听到了这首歌。上次听的时候应该是十年前了,眨眼间十几年竟然就过去了。那种轻松欢快的旋律,总是能让人心情变得更加的雀跃。

对我来说,绽放的年龄似乎依然是过去了,剩下的只有残花败柳,跟着时间一起变老。在老去的过程中也看到有的人的凋零,一个接着一个。上周的时候,同事神秘兮兮的说,公司有个哥们没了。说着拿手机给我看,我接过来,在他的朋友圈看到一条消息:『***因为心脏问题,医治无效去世,将在**举行告别仪式……』

看到这条消息的时候,内心并没有太大的波动。这几年,已经看着好多的人凋零,同村的邻居,自己的亲人,自己的同事,还有网上的名人。

昨天晚上对象说,下午看说什么张雪峰的头像变灰了,后面好像又给换回来了。我上网搜了一下,最开始看到的是张的助理说不清楚,合伙人说无可奉告。后来去微博搜的时候,就看到了张的微博账号发的讣告,的确是心脏问题去世了。

张还说自己跑马拉松什么的,身体没问题,网友都不见得能跑过他。然而,这他也没跑赢心脏病。看完消息之后,我一度都想直接买个aed放在家里了。不过对象说不至于,没啥必要。

张雪峰比我大一些,另外一个去世的同事比我大的更多一些。如果身体不好,看来真的不适合当牛马。而至于运动,真的能提高寿命吗?可能也没什么必然的相关性,有的疾病可能没有什么症状,而等到发病的时候,身体素质好的人可能存活的机会更少。身体素质不好的,可能会被拖垮但是不至于要命。当然,也有人说,锻炼是为了更体面的离开。身体拖垮了,可能就只剩下苟延残喘,没有质量的生活。

总是觉得来生还长,似乎有的是时间。现在看来,很多的人时间并没有那么多。或许正在绽放的时候,就被意外给消灭了。相比生命来说,其他的也就没那么重要了。钱多钱少,只要能活的开心就好了。体验生活,享受生活,才是关键,总是让自己活的不后悔才是对的。

很久之前刷短视频,看到一款个别假的手链。前几天又刷到了,去淘宝下了单买了四条,原价12块钱,最后实际花了3块钱。快递到了之后,带回家,对象看到说:『你真无聊,这有什么好玩的啊』

『没什么好玩的啊,我就是喜欢那种很假的感觉。』我答到。

我喜欢的是ta的视觉效果 ,有个角度看过去特别假。感觉都不想这个世界的东西。有时候买这些乱七八糟的小玩意儿,也会上瘾。可能买来不会戴,但是买的时候还是挺开心的。有时候也会买特别廉价的衣服神马的,并且买的衣服也真的不会穿出去。

有时候也挺矛盾的,这一身加起来不到100块钱,然而,周一去加油,大半箱油就花掉了300。加几次一辆电动车出来了,这还是里程数不多的情况下。粉皮每年花的钱,比我花的钱多多了。这么一向,感觉也挺无语。拍写真都够好多套了,不过下次拍,争取把十斤体重先减下来。跳绳的时候想到这些天天运动还猝死的,有感觉有那么一丝丝凉意。运动并不会使人长寿,健康也不会使人长寿,但是,我还是喜欢健康苗条。

最近事情的确有点多,一点点的堆积着都没来得及处理。工行发短信,说新信用卡已经到了营业厅了,但是却一直没去取。偶然想起来小时候吃的牛杂汤,忽然就很想吃。然而,美团、高德搜了一圈都没找到能吃牛杂的地方。想从二手东买,买回来自己加工,对象表示这个东西可能就你自己吃,买那么多也不合适,还是找个地方出去吃罢。

只是,到现在还没找到一个能吃牛杂的地方。周日带宝子去吃罐罐米线的时候,看到万达边上有一个牛杂店,好几天了,却还是没有来的及去。每天中午回家去消灭宝子姥姥在周末包的水饺,荠菜的,韭菜的。每次怕不够吃都会包很多,虽然很好吃,但是为了控制体重,剩下的水饺到现在已经吃了三天了,还有一些在冷冻上,明天不能再吃了,想去吃牛杂了。有的时候,那种记忆真的是刻在了骨子里。熟悉的味道,总是能让人感慨良多。而闺蜜圈的新版本发布,总是一波三折。华为的商户资质,三星的各种材料提交,总是有无数的关卡卡在前面,有时候也在想,不如放弃吧,反正也不挣钱,自己用也够了。

原本,今天似乎能有点空闲时间。然而,早上到公司拉代码的时候,发现移动硬盘坏了,mac 能识别磁盘,但是挂载不了,尝试急救失败。

Checking if the parent's minkey can be updated...
error: btn: oid (854310), xid (36994), type (0x40000003), subtype (0xb), flags (0x4) level (1)
error: btn: unable to repair minkey
   Object map is invalid.
** The volume /dev/rdisk5s1 with UUID 381DE7AB-BD78-4F75-879C-0E618699D213 was found to be corrupt and cannot be repaired.
** Verifying allocated space.
warning: found spaceman free queue tree entry (0xcfef0+17, xid 36987) which overlaps with existing range (0xcfef1+1)
warning: found spaceman free queue tree entry (0xcfef0+17, xid 36987) which overlaps with existing range (0xcfefb+1)
warning: found spaceman free queue tree entry (0xcff16+9, xid 36987) which overlaps with existing range (0xcff16+1)
warning: found spaceman free queue tree entry (0xcff16+9, xid 36987) which overlaps with existing range (0xcff1a+1)
warning: found spaceman free queue tree entry (0xcff16+9, xid 36987) which overlaps with existing range (0xcff1c+1)
warning: found spaceman free queue tree entry (0xcff22+1, xid 36987) which overlaps with existing range (0xcff22+1)
warning: found spaceman free queue tree entry (0xcff2c+3, xid 36987) which overlaps with existing range (0xcff2d+1)
warning: found spaceman free queue tree entry (0xcff30+3, xid 36987) which overlaps with existing range (0xcff31+1)
warning: found spaceman free queue tree entry (0xcff45+2, xid 36987) which overlaps with existing range (0xcff45+1)
warning: found spaceman free queue tree entry (0xcff4c+1, xid 36987) which overlaps with existing range (0xcff4c+1)
warning: found spaceman free queue tree entry (0xcffcc+3, xid 36987) which overlaps with existing range (0xcffcd+1)
warning: found spaceman free queue tree entry (0xcffcc+3, xid 36987) which overlaps with existing range (0xcffce+1)
warning: found spaceman free queue tree entry (0xcfe0f+11, xid 36988) which overlaps with existing range (0xcfe13+1)
warning: found spaceman free queue tree entry (0xcfe22+12, xid 36988) which overlaps with existing range (0xcfe24+1)
warning: found spaceman free queue tree entry (0xcfe22+12, xid 36988) which overlaps with existing range (0xcfe25+1)
warning: found spaceman free queue tree entry (0xcfe60+1, xid 36988) which overlaps with existing range (0xcfe60+1)
warning: found spaceman free queue tree entry (0xcfe85+1, xid 36988) which overlaps with existing range (0xcfe85+1)
warning: found spaceman free queue tree entry (0xcfe96+3, xid 36988) which overlaps with existing range (0xcfe98+1)
warning: found spaceman free queue tree entry (0xcff12+2, xid 36988) which overlaps with existing range (0xcff13+1)
warning: found spaceman free queue tree entry (0xcfe1c+1, xid 36989) which overlaps with existing range (0xcfe1c+1)
warning: found spaceman free queue tree entry (0xcfe36+1, xid 36989) which overlaps with existing range (0xcfe36+1)
warning: found spaceman free queue tree entry (0xcfe3e+1, xid 36989) which overlaps with existing range (0xcfe3e+1)
warning: found spaceman free queue tree entry (0xcfe57+2, xid 36989) which overlaps with existing range (0xcfe57+1)
warning: found spaceman free queue tree entry (0xcfea9+3, xid 36989) which overlaps with existing range (0xcfeab+1)
warning: found spaceman free queue tree entry (0xcfebe+1, xid 36989) which overlaps with existing range (0xcfebe+1)
warning: found spaceman free queue tree entry (0xcfee3+1, xid 36989) which overlaps with existing range (0xcfee3+1)
warning: found spaceman free queue tree entry (0xcff2f+1, xid 36989) which overlaps with existing range (0xcff2f+1)
warning: found spaceman free queue tree entry (0xcffd8+3, xid 36989) which overlaps with existing range (0xcffd9+1)
warning: found spaceman free queue tree entry (0xcfd9e+3, xid 36990) which overlaps with existing range (0xcfd9f+1)
warning: found spaceman free queue tree entry (0xcfd9e+3, xid 36990) which overlaps with existing range (0xcfda0+2)
warning: found spaceman free queue tree entry (0xcfe6e+1, xid 36990) which overlaps with existing range (0xcfe6e+1)
warning: found spaceman free queue tree entry (0xcfee6+1, xid 36990) which overlaps with existing range (0xcfee6+1)
warning: found spaceman free queue tree entry (0xcff29+1, xid 36990) which overlaps with existing range (0xcff29+1)
warning: found spaceman free queue tree entry (0xcfdc9+1, xid 36991) which overlaps with existing range (0xcfdc9+1)
warning: found spaceman free queue tree entry (0xcfe11+1, xid 36991) which overlaps with existing range (0xcfe0f+11)
warning: found spaceman free queue tree entry (0xcfda1+1, xid 36992) which overlaps with existing range (0xcfd9e+4)
warning: found spaceman free queue tree entry (0xcfdb5+1, xid 36992) which overlaps with existing range (0xcfdb5+1)
warning: found spaceman free queue tree entry (0xcfe4e+1, xid 36992) which overlaps with existing range (0xcfe4e+1)
warning: found spaceman free queue tree entry (0xcfe66+1, xid 36992) which overlaps with existing range (0xcfe66+1)
warning: found spaceman free queue tree entry (0xcfe88+1, xid 36992) which overlaps with existing range (0xcfe88+1)
warning: found spaceman free queue tree entry (0xcff41+1, xid 36992) which overlaps with existing range (0xcff41+1)
warning: found spaceman free queue tree entry (0xcfe40+1, xid 36993) which overlaps with existing range (0xcfe40+1)
warning: found spaceman free queue tree entry (0xcfeda+1, xid 36993) which overlaps with existing range (0xcfeda+1)
warning: found spaceman free queue tree entry (0xcff6c+1, xid 36993) which overlaps with existing range (0xcff6c+1)
warning: found spaceman free queue tree entry (0xcff7b+1, xid 36993) which overlaps with existing range (0xcff7a+2)
** Performing deferred repairs.
error: Unable to perform deferred repairs without full space verification
** The container /dev/rdisk5 could not be verified completely.

尝试各种修复方式,最终都以失败告终,apfs看来也不咋靠谱。只能格式化硬盘,重新拉代码,重新配置运行环境,拉代码大半天时间竟然就没了。

只是,退堂鼓一向,内心又觉得不甘。应该让她绽放的,辛辛苦苦的折腾到现在,总是不想功亏一篑。好好的东西,夭折了,也的确不是我的风格。

如果按照所谓的平均寿命,现在看来,正是如花似玉的年龄。该绽放就绽放吧,哪怕花开的有些晚了,也是总要开的。

🦞龙虾养殖小技巧

作者 obaby
2026年3月25日 15:21

在深入体验这个东西之前,其实我并未对龙虾抱有太高的期望。不过这几天实际使用下来,感觉还算可以,最起码没那么智障,一些简单的事情也能给处理好。

然而,龙虾虽好,但是养殖却还是稍显麻烦。尤其是系统配置不高的情况下,最开始的时候一切都配置好了,结果在某天晚上gateway就再也启动不了了,启动的的时候就报内存溢出。cpu和内存都直接跑满,等降下来之后,龙虾也跟着死了。刚开始以为是配置问题,改错东西导致启动失败了,结果在回滚镜像之后依然报错,这个镜像是刚安装好的时候创建的镜像。那么此时就有另外一个问题了,同样的镜像为什么系统重启之后就启动不了了?

当时没想这么多,解决办法是备份memory文件等进行重装,好在重装之后接本的功能和代码都在,让龙虾从新加载配置文件,也恢复到了之前的状态。不过,在重装的时候npm源也是个问题,可以考虑直接修改系统的npm源:

npm config set registry https://registry.npmmirror.com

检查修改是否生效:

npm config get registry

另外一个,那就是我给龙虾外层套了一层认证,当然这个做法有点傻,但是呢。这些乱七八糟的东西直接暴露出来总是多少感觉有点问题,于是还是套了一层nginx的认证。

毕竟,前几天周鸿祎还吆喝发现了龙虾的oday漏洞。

相关登录界面实现参考:https://cnb.cool/oba.by/baby-claw

然而,安装之后,昨天尝试更新龙虾,结果更新之后重启又开始报内存溢出,这就有点尴尬了,直接运行doctor:

ubuntu@VM-0-11-ubuntu:~$ openclaw doctor

🦞 OpenClaw 2026.3.23-2 (77e4) — I'm like tmux: confusing at first, then suddenly you can't live without me.

▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
██░▄▄▄░██░▄▄░██░▄▄▄██░▀██░██░▄▄▀██░████░▄▄▀██░███░██
██░███░██░▀▀░██░▄▄▄██░█░█░██░█████░████░▀▀░██░█░█░██
██░▀▀▀░██░█████░▀▀▀██░██▄░██░▀▀▄██░▀▀░█░██░██▄▀▄▀▄██
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
                  🦞 OPENCLAW 🦞                    
 
┌  OpenClaw doctor
│
◇  Update ──────────────────────────────────────────────────────────────────────────────────╮
│                                                                                           │
│  This install is not a git checkout.                                                      │
│  Run `openclaw update` to update via your package manager (npm/pnpm), then rerun doctor.  │
│                                                                                           │
├───────────────────────────────────────────────────────────────────────────────────────────╯
│
◇  Startup optimization ─────────────────────────────────────────────────────────────────────╮
│                                                                                            │
│  - NODE_COMPILE_CACHE is not set; repeated CLI runs can be slower on small hosts (Pi/VM).  │
│  - OPENCLAW_NO_RESPAWN is not set to 1; set it to avoid extra startup overhead from        │
│    self-respawn.                                                                           │
│  - Suggested env for low-power hosts:                                                      │
│    export NODE_COMPILE_CACHE=/var/tmp/openclaw-compile-cache                               │
│    mkdir -p /var/tmp/openclaw-compile-cache                                                │
│    export OPENCLAW_NO_RESPAWN=1                                                            │
│                                                                                            │
├────────────────────────────────────────────────────────────────────────────────────────────╯
13:24:32 [plugins] plugins.allow is empty; discovered non-bundled plugins may auto-load: openclaw-qqbot (/home/ubuntu/.openclaw/extensions/openclaw-qqbot/index.ts). Set plugins.allow to explicit trusted ids.
[qqbot-channel-api] Registered QQ channel API proxy tool
[qqbot-remind] Registered QQBot remind tool
│
◇  Archive 1 orphan transcript file in ~/.openclaw/agents/main/sessions? This only renames them to *.deleted.<timestamp>.
│  No
│
◇  State integrity ─────────────────────────────────────────────────────────────────────────╮
│                                                                                           │
│  - OAuth dir not present (~/.openclaw/credentials). Skipping create because no            │
│    WhatsApp/pairing channel config is active.                                             │
│  - Found 1 orphan transcript file in ~/.openclaw/agents/main/sessions.                    │
│    These .jsonl files are no longer referenced by sessions.json, so they are not part of  │
│    any active session history.                                                            │
│    Doctor can archive them safely by renaming each file to *.deleted.<timestamp>.         │
│    Examples: 9ede0dd4-5344-4156-a156-a9035538b1cb0d.jsonl                                   │
│                                                                                           │
├───────────────────────────────────────────────────────────────────────────────────────────╯
│
◇  Security ─────────────────────────────────╮
│                                            │
│  - No channel security warnings detected.  │
│  - Run: openclaw security audit --deep     │
│                                            │
├────────────────────────────────────────────╯
│
◇  Systemd ───────────────────────────────────────────────────────────────────────────╮
│                                                                                     │
│  Gateway runs as a systemd user service. Without lingering, systemd stops the user  │
│  session on logout/idle and kills the Gateway.                                      │
│  We can enable lingering now (may require sudo; writes /var/lib/systemd/linger).    │
│                                                                                     │
├─────────────────────────────────────────────────────────────────────────────────────╯
│
◇  Enable systemd lingering for ubuntu?
│  Yes
│
◇  Systemd ───────────────────────────────╮
│                                         │
│  Enabled systemd lingering for ubuntu.  │
│                                         │
├─────────────────────────────────────────╯

这时候才发现关键性的几行:

◇  Startup optimization ─────────────────────────────────────────────────────────────────────╮
│                                                                                            │
│  - NODE_COMPILE_CACHE is not set; repeated CLI runs can be slower on small hosts (Pi/VM).  │
│  - OPENCLAW_NO_RESPAWN is not set to 1; set it to avoid extra startup overhead from        │
│    self-respawn.                                                                           │
│  - Suggested env for low-power hosts:                                                      │
│    export NODE_COMPILE_CACHE=/var/tmp/openclaw-compile-cache                               │
│    mkdir -p /var/tmp/openclaw-compile-cache                                                │
│    export OPENCLAW_NO_RESPAWN=1                                                            │
│                                                                                            │
├────────────────────────────────────────────────────────────────────────────────────────────╯

这个startup 优化,不知道是不是针对gateway的启动也有效。不过在升级到最新版之后,感觉启动稍微顺畅了一点。似乎没那么卡了。

新版本貌似也同时修复了用量显示问题,上个版本,不管怎么查询用量都只显示今天的,新版貌似是没问题了:

服务器作为一个比较干净的环境,如果要实现一些其他的功能,就得能够进行文件快速交互,所以文件上传下载也就是一个必备的技能,推荐filebrower:

至此对我来说,一个简单的勉强能用的龙虾就实现了。可以尝试各种工作了:

1. 手搓elf可执行文件

2.给我发布闺蜜圈wiki的文章:

3.批量创建停机申请表:

skill这个东西,可以有,但是也不是必须的。

 

野心家

作者 obaby
2026年3月24日 08:58

时间总是太少,感觉有太多的事情想要做,太多的东西想要学。总是觉得时间太少,仿佛永远都没有够用的时候。总也赶不上发展的步伐,被时代狠狠的甩到了后面。

跟着滚滚洪流,跟在后面跑的精疲力尽,却总是感觉和这个世界脱节越来越严重,渐行渐远。有时候很容易有一种错觉,就是我上我也行,看着简单的东西,实际操作起来从来都不简单。并且没有什么捷径。

学了很多的技术,也许了很多的东西,然而,却似乎从来没有经通过,也不曾足够深入一项技术。总是感慨人与人之间的差距,不单纯是智商的差距,而是物种的差距,似乎完全不能理解别人是如何做到的。什么99%的汗水+1%的灵感,却鲜有人告诉你,那1%才是最重要的。

这几天体验养龙虾,几天下来感觉一些复杂的东西也能处理,至于能处理多复杂的东西就看用的模型多先进了。

以前自己做家里的家居控制系统的时候,只能用简单的语音识别加ifttt的指令简单识别。包括前几天给闺蜜圈做得快捷指令也是通过指令匹配的方式做得,现在看来是真的落伍。不过要体验新的东西,总是要付出代价的,巨量的token消耗。新技术的发展和迭代必然会改变一些事情,也会改变一些人的生存方式,这个是无解的。当然,也催生了一堆新的职业,例如那群生产孵化各种龙虾的,虽然现在看来各种虾是真的瞎。

以前花费好长时间才能掌握的汇编、java、oc等等,现在各种工具连一分钟的时间都不用就能生成可以运行的代码。或许,这依然不是极限,以前在windows上曾经手写十六进制,构造可执行文件。或许已经有ai能够实现不需要高级语言直接生成可执行文件了,不管是windows的pe文件,还是linux的efl文件,遵循特定的文件结构,剩下的就是生成导入导出表,编译机器码。如果这样,还要什么高级语言,你告诉ai需要什么格式,到时候可能就直接给出exe、so、apk、ipa文件了。最终的发展趋势就是从机器码->低级语言->高级语言->机器码。以前从命令行到了gui,现在又有很多人从gui回到了命令行,现在的ide编辑器似乎也没那么重要了,全部ai驱动,只需要一个输入输出窗口就行了。广义来说,龙虾也是此类进化方式,不再需要关注具体的代码,也不用修改,全部ai驱动。 

文件:hello_obaby

上午让workbuddy给整理汇总图片里的运维日志,结果卡住了之后,一天都没进展。

最后还是切到了龙虾上,虽然费功夫,但是确实做到了。

ai甚至说,人工识别了这些图片。可能ai才是野心家,哪天想把人类取而代之吧。

周末给龙虾装了个看板,结果在折腾到晚上的时候,因为qqbot的一个问题,把系统整挂了,通过快照还愿之后,cpu一直卡在100,最后也没能再起来。相比之下,自己的确不如ai,然而,ai却无法自救。

之前看《机器学习》的时候,总觉得自己也能写个模型出来,然而,前几天尝试用机器学习计算月经的预期时间都没弄好,想着改进算法却一直没时间。时间也不知道都干嘛去了,当然,与最近工作的事情也有关系,乱七八糟的事情实在是多,每天都有解决不完的问题。

那广泛的涉猎,到现在已经没有太大的意义,因为连自己的空闲时间都挤不出来。

也许,等哪天自己不眠不休,或者能活500年的时候,才能成为真正的野心家吧。

❌
❌