春有百花秋有月

春有百花秋有月

夏有凉风冬有雪

通过tg发送图片自动上传R2图床同时发布博客

为了方便不在电脑前时也能发博客,配置了 Telegram → R2 图床 → 博客引用 的自动上传流程。

上传耗时:约 200ms,R2 返回 200 OK,公网可直接访问。

测试截图:

TG测试截图

后续在 TG 中发图给机器人,自动完成上传 — 返回 `` 格式,直接复制到博客编辑器即可。

阅读剩余部分 →

使用Cloudflare R2存储桶搭配PicList中S3配置图床.

选择 Cloudflare R2:在同一个 Cloudflare 账号里创建 R2 存储桶,然后用 PicList 中S3配置好,这就是能安稳用上十几年的国际化标准图床。一定要开启 WebP 压缩:在 PicList 里或者博客后台,配置好图片上传时自动转为 WebP 或 AVIF 格式。只要图片体积小,10GB 的免费额度你可能直到博客停更都用不完。

[!TIP]提前准备好域名和双币信用卡

下载地址参考:https://piclist.cn/app.html#%E7%9B%B4%E9%93%BE%E4%B8%8B%E8%BD%BD%E5%9C%B0%E5%9D%80



之后配置格式转换压缩:

阅读剩余部分 →

博客优化记录(2026年7月)

这次对博客进行了一次比较系统的优化整理,涉及首页排版、相册集成、细节体验等多个方面,记录一下。

之前首页的文章列表左对齐,和浏览器边缘贴得很近,观感不好。调整为正确居中,两栏布局(文章列表 + 侧边栏标签云),并对大屏幕做了边距优化。

之前的标签云可能藏在某个角落或者显示不完整,这次把它作为侧边栏独立模块展示在首页右侧,最多显示 30 个标签,按文章数量降序排列,字体大小根据热度动态调整。

首页大标题「春有百花秋有月」使用渐变色字体,视觉上更有质感:

.home-title h1 {
    font-family: 'ZCOOL XiaoWei', 'Noto Serif SC', serif;
    font-size: 2.2em;
    font-weight: 400;
    background: linear-gradient(135deg, #6e8aad 0%, #9fcff7 40%, #c4a882 70%, #b8896e 100%);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
}

4. 足迹页面全屏 iframe

足迹页面(地图)原来是窄幅展示,这次优化为全屏宽度,使用 CSS 让 iframe 填满视口:

.page-map iframe {
    width: 100% !important;
    max-width: 100% !important;
    height: calc(100vh - 3.2em) !important;
}

5. 随拍相册集成到主站

随拍页面原本是新开标签页跳转,体验不连贯。这次把它改造为和足迹页面一样的内嵌 iframe 形式,直接在站内全屏展示,不需要跳出。导航链接的 target="_blank" 也同步移除。

阅读剩余部分 →

X-UI + Reality + WARP 全栈代理配置指南

本文详细记录利用 X-UI 面板搭建 VLESS-Reality 节点,配合 Cloudflare WARP SOCKS5 分流解锁 AI 服务的完整方案。适用于具备一定 Linux 操作基础的用户。

在跨境网络访问场景中,IDC 机房 IP 容易被目标网站(OpenAI、Claude、Gemini、X 等)识别并限制(1020 错误或直接拒绝访问)。同时,移动端(尤其是 iOS)依赖 Apple APNs 推送服务,代理路由配置不当会导致 Telegram 等 App 推送失效。

本文方案:

                        ┌─────────────────────┐
  Client                │   Linux Server       │
  ┌──────┐              │  ┌───────────────┐   │
  │ QX / │───HTTPS─────▶│  │  X-UI Panel   │   │
  │ SR   │              │  │  (TLS + 443)  │   │
  └──────┘              │  └───────┬───────┘   │
                         │          │           │
                         │  ┌───────▼───────┐  │
                         │  │ Xray-core      │  │
                         │  │ VLESS Reality  │  │
                         │  └───────┬───────┘  │
                         │          │           │
                         │   ┌──────▼──────┐   │
                         │   │ warp-cli     │   │
                         │   │ SOCKS5 :1234 │   │
                         │   └──────┬──────┘   │
                         │          │           │
                         └──────────┼───────────┘
                                    │
                              CF WARP Edge IP
                               (解锁 AI 服务)

核心协议选型说明:

阅读剩余部分 →

OpenClaw Cron 任务 EmbeddedAttemptSessionTakeoverError 排查与修复记录

背景:近期服务器每日备份(02:00)和每日 Dashboard 早报(06:01)等多个定时任务频繁报错 EmbeddedAttemptSessionTakeoverError,导致任务无法正常执行。经过详细排查,发现这是一个配置冲突导致的问题。

OpenClaw 日志中反复出现:

EmbeddedAttemptSessionTakeoverError

多个 cron 任务随机失败,有时备份能跑但早报失败,有时反过来。看起来像是资源竞争的问题。

检查所有 cron 任务的 jobs.json 配置后发现一个致命的矛盾配置

{
  "sessionTarget": "isolated",      // ← 说:请用独立会话运行
  "sessionKey": "agent:main:telegram:default:direct:5595946736"  // ← 又说:强制绑定到同一个 TG 会话
}

这两个配置同时存在时,OpenClaw 优先使用 sessionKey,导致所有 10 个定时任务都在抢同一个 Telegram DM 会话锁

sessionKey 是「绑定到指定会话」的硬链接,所有任务指向同一个 Telegram DM 会话。并发时只有一个任务能持有该会话,其他全部失败。

二选一,不能混用:

方案 A(我们使用的):isolated + announce

阅读剩余部分 →

OpenClaw 故障排查:EmbeddedAttemptSessionTakeoverError 解决方案

OpenClaw 故障排查:EmbeddedAttemptSessionTakeoverError 解决方案

cron 任务(每日Dashboard早报、服务器每日备份)反复失败,错误信息如下:

EmbeddedAttemptSessionTakeoverError: session file changed while embedded prompt lock was released: 
/home/openclaw/.openclaw/agents/main/sessions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.jsonl

FailoverError: session file changed while embedded prompt lock was released

问题原因

技术原理

OpenClaw 的 Pi embedded runner 在执行 cron 任务时采用双锁机制:

当锁在步骤2释放期间,如果有其他进程修改了同一个 session 文件(fingerprint 指纹变化),重新获取锁时会检测到文件被篡改,抛出 EmbeddedAttemptSessionTakeoverError。

cp ~/.openclaw/agents/main/sessions/sessions.json ~/.openclaw/agents/main/sessions/sessions.json.bak.$(date +%Y%m%d)

步骤 2:识别故障 cron job 的 sessionKey

从 cron runs 日志中找到 sessionId,例如:

/home/openclaw/.openclaw/agents/main/sessions/6d127bc5-cdf9-4c03-97fd-761bb7dd55d7.jsonl

对应的 sessionKey 为:

阅读剩余部分 →

跑步自动归档 Watcher 两次 Debug 实录:从轮询到 inotify 的本地 OCR 优化之路

🤖 本文由 OpenClaw + DeepSeek 自动生成

一次从远程 OCR 降级到本地 Tesseract 的架构痛史,顺便干掉三个隐蔽 Bug。

我平时跑步用 Nike Run Club(NRC),跑完后截个图发到自己的 Telegram Bot,Bot 会自动把图片存入服务器。我有一个后台 Watcher 脚本,专门监控这个图片目录,一旦发现新图片就 OCR 提取运动数据(距离、时长、配速、卡路里),然后自动归档到 Notion 的 Sports 数据库,实现"跑完截个图,一切自动归档"的极简体验。

本来这套流程跑得好好的,直到前天,突然崩了。

截图发过去,Bot 这边显示图片已保存,但 Notion 里就是没有新记录。手动跑脚本测试,发现 OCR 一直超时,偶尔跑出来也是空结果。

开始排查。

起初 Watcher 的 OCR 链路是这样的:图片 → image 工具调用外部视觉 API → 返回文本。这套链路的核心问题在于:

一张截图 OCR 要等 30 秒以上,频繁超时。

阅读剩余部分 →

OpenClaw + 私人中台优化实录

OpenClaw + 私人中台优化实录(2026-06-04 ~ 2026-06-05)

最近对个人 OpenClaw 中台做了一次系统性优化,解决了几个长期痛点,记录一下。

原来的早报只有天气、待办、股票、新闻四大块,这两天新增了两个模块:

1. 昨日复盘修复
之前的「已完成任务」里混入了历史积压任务,只要在 Notion 里标记过就算进来,不管实际完成了多久。现在改为以 last_edited_time 为唯一判定锚,只显示昨天实际完成的任务。

2. 情感温度计
新增「💓 活跃度」区块,扫描最近 7 天对话记录中敏感关键词出现频率,映射为 🔥 / 📊 / 💤 三档输出,完全不暴露具体内容。阈值:≥15 次 → 🔥,≥5 次 → 📊,<5 次 → 💤。

之前 9:30 检测未触发预警时还会发一条「无声模式退出」消息,纯粹噪音。改造了两点:

改造后只有触发 6% 股息率预警时才推送,其余时间完全静默。

两个待办 cron(9:00 早间提醒 + 17:30 下班前催办)同样改为:脚本内部无内容时 return 静默退出,cron delivery 全部改为 none。零噪音原则:正常状态不发任何消息,有事才推。

阅读剩余部分 →

榨干甲骨文VPS:从全局卡顿到满血4K

Xray + Cloudflare WARP 完美智能分流 · 解锁 ChatGPT/Gemini 保姆级教程

自建节点的小伙伴常常会遇到一个两难抉择:为了解锁 ChatGPT、Gemini 或 Claude 等对机房 IP 封锁极严的 AI 服务,我们通常会在 VPS 上套一层 Cloudflare WARP。

然而,如果使用传统的"全局 WARP(WGCF模式)",整台服务器的所有出站流量都会被强制拉进 WARP 的虚拟网卡。由于 Cloudflare 免费公用节点的带宽处于全球网民"公地悲剧"的状态,晚高峰或网络抽风时,原本可以跑满几百兆的甲骨文原生网络,会被活生生限速到 1 Mbps(连 YouTube 1080P 都卡)

本文将分享一套企业级/生产标准的分流架构:让服务器恢复纯净原生公网(YouTube 直连秒开 4K 几万 Kbps),同时让 Xray 像智能交通警察一样,只把去往 AI 网站的流量精准踢给 WARP 转发,完美兼顾极致网速流媒体/AI完美解锁

我们要做的第一步,就是把 WARP 从"接管全局网卡的流氓"变成"只在后台默默监听的备用通道"。

cf

2.卸载全局 WARP:在弹出的菜单中,先输入 4 彻底卸载当前运行的 WGCF-WARP(防止新旧模式冲突)。

3.安装 Socks5-WARP:重新输入 cf 进入菜单,选择 2. 方案二:安装 Socks5-WARP

4.记录本地端口:一路回车保持默认安装。安装成功后,脚本会在屏幕上提示一个关键的本地监听端口,通常默认为 40000(本地 IP 为 127.0.0.1)。

阅读剩余部分 →

服务器备份体系搭建全记录

本文记录于 2026-05-28,基于 Oracle ARM Ubuntu 20.04 LTS 环境,折腾了一整个下午才搞定。

服务器上跑着一堆服务:两个 Typecho 博客、一个 OpenClaw AI 中台、各种 Docker 容器(PhotoPrism、BitWarden、alist 等),还有 Java/Python 微服务。每天凌晨把这些东西自动备份到 OneDrive,并且要求:

/mnt/onedrive/bak/
├── Mon/          ← 周一备份(7天循环)
├── Tue/          ← 周二备份
├── Wed/          ← 周三备份
├── Thu/          ← 周四备份 ← 今天生成的
├── Fri/          ← 周五备份
├── Sat/          ← 周六备份
└── Sun/          ← 周日备份

每个工作日目录内,包含以下备份文件:

Thu/
├── backup_server.sh      ← 备份脚本本身(核心!)
├── crontab.txt          ← Cron 任务配置导出
├── docker_volumes.tar.gz ← Docker 数据卷(待 Docker 可用后生效)
├── dockers.tar.gz       ← /opt/dockers 容器配置
├── install_software.sh   ← 快速安装脚本(可在新机跑)
├── nginx_conf.tar.gz    ← Nginx conf.d 配置
├── nginx_ssl.tar.gz     ← SSL 证书目录
├── openclaw.tar.gz      ← OpenClaw 整个主目录
├── packages.txt         ← 系统已装软件完整清单
├── service.tar.gz       ← /opt/service 微服务
├── typecho.tar.gz       ← 主博客(SQLite 安全打包)
└── typecho_sex.tar.gz   ← 第二个博客

单轮次总大小约 786M,7 天循环总共约 5.5G,对 OneDrive 来说毫无压力。

阅读剩余部分 →

从windows拷贝到Mac上的堡垒机私钥无法使用问题解决方案

输入口令后报错信息:Keys(Pkcs1(Pkcs8(Asn1(Error

 SSH  Connecting to portal-pro
 SSH  Host key fingerprint:
 SSH   ssh-rsa  SHA256:K2YjDYXsD5ehqZvvBfxvW0voThaKT9nRqXtXVQxu1yQ 
 SSH   !  Failed to load private key file:///Users/zgz/Documents/dev/server_file/0.197_luojugang: Error: Keys(Pkcs1(Pkcs8(Asn1(Error { kind: Length { tag: Tag(0x6e: APPLICATION [14] (constructed)) }, position: None }))))

查看私钥文件:

cat /Users/zgz/Documents/dev/
<!--more-->
server_file/0.197_luojugang

如下信息:

-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,557572BEACB4BC74

这说明:
• 这是一个 PEM 格式、RSA 私钥
• 加密过,密码保护的
• 加密方式是 DES-EDE3-CBC
→ 新版 OpenSSH 和许多 Rust SSH 库默认不再支持这种旧式加密 PEM 私钥格式了

📌 解决方法:

✅ 方法 1:解密私钥并转成不加密的 PEM 格式

在 Mac 上执行:

阅读剩余部分 →

从 MacBook Air M1 迁移到 M4:完整迁移助手记录

最近入手了 MacBook Air M4,顺利把原来的 MacBook Air M1 上的数据迁移过来,记录一下整个过程和遇到的小坑,给后来者参考。

📦 设备配置

设备 配置
旧设备 MacBook Air M1 2020, macOS Sequoia 15.0.1
新设备 MacBook Air M4 2024, macOS Sequoia 15.3
迁移方式 迁移助理(Migration Assistant)
连接方式 Thunderbolt 4 数据线直连
数据量 约 180 GB

📃 迁移前准备

.5
👉 避免版本不一致导致迁移助手卡顿/断连的问题。

📦 迁移步骤

1️⃣ 打开迁移助手
• 应用程序 → 实用工具 → 迁移助理
• 新机选择「从 Mac、Time Machine 或启动磁盘转移数据」

2️⃣ 连接设备
• 插上雷雳4线,或连同一 Wi-Fi
• 等待两台设备互相识别

阅读剩余部分 →

MySQL Client 8.0.42 安装过程记录(内网服务器)

在可访问外网的服务器,前往 MySQL官网 下载以下 RPM 包:

保存到目录:

/home/appadmin/2025/

2️⃣ 上传 RPM 包到内网服务器

⚠️ 注意:

使用 rz 上传发现 上传后 MD5 值与源文件不一致,导致安装异常。

📌 解决方法:

改用 scp 方式上传,确保文件完整。

示例命令:

阅读剩余部分 →

配置uWSGI

应用背景

本配置文档适用于基于 Flask 的 file_upload 应用。应用所在目录为 /opt/service/python_prj/fileUpload/upload/ver3,并使用 uWSGI 提供服务。通过系统服务管理器 systemd 启动和管理 uWSGI。

在虚拟环境 pictoHub.env 中安装 uWSGI:

source /opt/service/python_prj/pictoHub.env/bin/activate
pip install uwsgi
deactivate

2. 创建 uWSGI 用户

为 uWSGI 服务创建独立的运行用户 uwsgi:

sudo useradd -r -s /bin/false uwsgi

3. 授权相关目录

确保 uwsgi 用户对必要目录有权限:

sudo mkdir -p /var/log/uwsgi
sudo mkdir -p /run/uwsgi

sudo chown -R uwsgi:uwsgi /opt/service/python_prj/fileUpload/upload/ver3
sudo chown -R uwsgi:uwsgi /var/log/uwsgi
sudo chown -R uwsgi:uwsgi /run/uwsgi

4. 创建 uWSGI 配置文件

在 /opt/service/python_prj/fileUpload/upload/ver3/ 中创建 uwsgi.ini 文件,内容如下:

[uwsgi]

<!--more-->

# uWSGI 运行用户和组
uid = uwsgi
gid = uwsgi

# 启动服务监听的地址和端口
http-socket = 0.0.0.0:8066

# 指定虚拟环境路径
virtualenv = /opt/service/python_prj/pictoHub.env

# 指定 Flask 应用文件的路径
wsgi-file = /opt/service/python_prj/fileUpload/upload/ver3/app.py

# 设置 Flask 的应用实例
callable = app

# 设置静态文件目录映射
static-map = /static=/opt/service/python_prj/fileUpload/upload/ver3/static

# 日志文件路径
logto = /var/log/uwsgi/uwsgi.log

# 设置进程数
processes = 4

# 确保应用正常启动
touch-reload = /opt/service/python_prj/fileUpload/upload/ver3/app.py

5. 创建 Systemd 服务文件

在 /etc/systemd/system/ 目录下创建 file_upload-uwsgi.service,内容如下:

阅读剩余部分 →

Mac install Erlang and RabbitMQ

以下是如何在 Mac 上安装 Erlang 和 RabbitMQ,并启用 RabbitMQ 可视化监控插件的详细步骤。

阅读剩余部分 →

推送(git push)操作在向 GitHub 发送数据时遇到了问题

您遇到的 Git 错误日志表明,推送(git push)操作在向 GitHub 发送数据时遇到了问题。具体错误信息如下:

error: RPC failed; HTTP 400 curl 22 The requested URL returned error: 400
send-pack: unexpected disconnect while reading sideband packet
fatal: the remote end hung up unexpectedly

这些错误通常与以下几个原因相关:

这部分显示了您提交的文件大小(54.45 MB)。Git 默认推送限制较小的文件,如果您上传的文件太大,可能会导致超时或断开连接。

解决办法:
• 您可以通过调整 Git 的 http.postBuffer 设置来增加上传限制。运行以下命令来增加缓冲区大小:

git config --global http.postBuffer 524288000

这将把上传缓冲区的大小设置为 500MB。

解决办法:
• 如果您的文件太大,您可能需要使用 Git LFS(Git Large File Storage)来管理大文件。您可以按照 GitHub 官方文档 设置 Git LFS 来解决这个问题。

网络问题:
• 有时网络中断或不稳定也会导致上传过程中断。您可以尝试重新提交或者在网络更稳定的环境下再试一次。

分支名称问题:
• 您的命令是 git push -u master master,看起来像是想将本地的 master 分支推送到远程的 master 分支,但 master 分支在 GitHub 上可能已经更名为 main。

阅读剩余部分 →

使用jVectorMap工具为typecho增加足迹功能

本文部署后的足迹地图如下:
https://313390.xyz/index.php/51.html

github参考地址:
https://github.com/HelloWuJiaYi/jVectorMap-Footprint

注意事项:

    # Map info
    location /map {
        index index.html;
        #try_files $uri $uri/ =404;
        add_header X-Frame-Options "ALLOWALL";
    }
<!--more-->
  • 2.修改index.html,可根据注释修改使用中国地图或者世界地图,然后增加自已的足迹点,足迹坐标可参考:
    https://jingweidu.bmcx.com/

    3.新建独立页面,引入如下代码:

    !!!
    <iframe style="max-width: 100%" 
                frameborder="no" 
                border="0" 
                marginwidth="0" 
                marginheight="0" 
                width="100%" 
                height="600px" 
                src="https://313390.xyz/map/index.html">                                        
          </iframe>
    !!!
  • 4.收工,访问新建的独立页面即可。

    阅读剩余部分 →

调整Mac book中Launchpad启动台的行、列数量

具体办法:

打开终端,复制并执行以下4行命令:

defaults write com.apple.dock springboard-columns -int 12
defaults write com.apple.dock springboard-rows -int 8
defaults write com.apple.dock ResetLaunchPad -bool TRUE
killall Dock
  • 说明:

    以上每行的含义:
    1、调整每一列显示图标数量,9表示每一列显示9个,数字部分可根据个人喜好进行设置。
    2、调整多少行显示图标数量,这里我用的是6,数字部分你也可以改成8或其他
    3、重置Launchpad
    4、重启Dock
  • 恢复默认设置的方法,在终端Terminal中执行以下4行命令(全部复制):

    阅读剩余部分 →
  • 控制typecho首页不显示全文,只显示摘要的方法

    虽然在Typecho的文章撰写里面可以使用

    <!--more-->

    来控制首页显示文章的内容,但有时候码完字后忘记添加了又需要进去编辑添加。为了能达到一劳永逸的效果,以下将采用修改Typecho模版来实现全局控制文章显示的内容:

    阅读剩余部分 →

    为typecho文中代码增加一键复制功能

    Update header.php,add codecopy,eg:

    vi /var/www/typecho/usr/themes/single/header.php

    在header之前增加如下js脚本:

    <script>
            // 在代码块右上角添加复制按钮
            document.addEventListener('DOMContentLoaded', initCodeCopyButton);
            function initCodeCopyButton() {
                function initCSS(callback) {
                    const css = `
                        .btn-code-copy {
                            position: absolute;
                            line-height: .6em;
                            top: .2em;
                            right: .2em;
                            color: rgb(87, 87, 87);
                        }
                        .btn-code-copy:hover {
                            color: rgb(145, 145, 145);
                            cursor: pointer;
                        }
                        `;
                    const styleId = btoa('btn-code-copy').replace(/[=+\/]/g, '');
                    const head = document.getElementsByTagName('head')[0];
                    if (!head.querySelector('#' + styleId)) {
                        const style = document.createElement('style');
                        style.id = styleId;
                        if (style.styleSheet) {
                            style.styleSheet.cssText = css;
                        } else {
                            style.appendChild(document.createTextNode(css));
                        }
                        head.appendChild(style);
                    }
                    callback();
                };
                function copyTextContent(source) {
                    let result = false;
                    const target = document.createElement('pre');
                    target.style.opacity = '0';
                    target.textContent = source.textContent;
                    document.body.appendChild(target);
                    try {
                        const range = document.createRange();
                        range.selectNode(target);
                        window.getSelection().removeAllRanges();
                        window.getSelection().addRange(range);
                        document.execCommand('copy');
                        window.getSelection().removeAllRanges();
                        result = true;
                    } catch (e) { console.log('copy failed.'); }
                    document.body.removeChild(target);
                    return result;
                };
                function initButton(pre) {
                    const code = pre.querySelector('code');
                    if (code) {
                        const preParent = pre.parentElement;
                        const newPreParent = document.createElement('div');
                        newPreParent.style = 'position: relative';
                        preParent.insertBefore(newPreParent, pre);
                        const copyBtn = document.createElement('div');
                        copyBtn.innerHTML = 'copy';
                        copyBtn.className = 'btn-code-copy';
                        copyBtn.addEventListener('click', () => {
                            copyBtn.innerHTML = copyTextContent(code) ? 'success' : 'failure';
                            setTimeout(() => copyBtn.innerHTML = 'copy', 250);
                        });
                        newPreParent.appendChild(copyBtn);
                        newPreParent.appendChild(pre);
                    }
                };
                const pres = document.querySelectorAll('pre');
                if (pres.length !== 0) {
                    initCSS(() => pres.forEach(pre => initButton(pre)));
                }
            };
    </script>
    阅读剩余部分 →