2026年1月

贝叶斯算法的智慧之旅:为什么我们选择了它

在数字世界的浩瀚星空中,有一颗智慧的星辰始终闪耀着温和而坚定的光芒,它就是贝叶斯算法。当我们决定将这位智慧的老者请进我们的项目时,并不是因为它最新最炫,而是因为它的智慧经得起时间的考验,如同一位历经沧桑的智者,总能在喧嚣中给出最稳妥的建议。

遇见贝叶斯:那一刻的心动

还记得第一次遇见贝叶斯定理时的那份惊喜吗?它如此简洁而优雅:

P(A|B) = P(B|A) * P(A) / P(B)

这短短的公式,却蕴含着深刻的哲理:在不确定的世界中,我们如何基于已知的证据,不断更新对未知的认知。这不正是我们在处理垃圾信息时所面临的挑战吗?

在我们的项目中,每一条消息都是一个未知的世界,而贝叶斯算法就像一位耐心的侦探,根据消息中的每一个词语(证据),不断调整它对这条消息性质的判断。

为什么是贝叶斯:那些让我们心动的理由

1. 智慧的增量学习

贝叶斯算法最让我们心动的地方,就是它的学习能力。它不是一位固执的学者,而是一位谦逊的学生,愿意在每一次判断后,根据结果不断调整自己的认知。

// 每一次训练,都是一次学习的机会
trainMessage(message: TrainedMessage): void {
  const tokens = this.tokenize(message.message);
  this.trainTokens(tokens, message.messageType === 'spam');
}

在我们的系统中,每一条被正确识别的垃圾信息,每一条被误判的正常消息,都会成为贝叶斯模型的营养,让它变得更加智慧。这种增量学习的能力,让我们的系统能够随着时间不断进化,而不是停滞不前。

2. 稳健的概率思维

在信息安全的世界中,我们面临的最大挑战之一就是"误判"。一条重要的消息被误判为垃圾,可能会造成严重的后果。而贝叶斯算法以其概率思维,给了我们一个更加稳健的解决方案。

它不会武断地下结论,而是给出一个概率:

return {
  isSpam: spamProbability >= probabilityThreshold,
  confidence: Math.round(spamProbabilityPercent),
  spamProbability: spamProbabilityPercent,
  hamProbability: hamProbabilityPercent,
  // ...
};

这种概率化的输出,让我们能够根据不同的场景设置不同的阈值,在严格性和宽容性之间找到最佳的平衡点。

3. 透明的决策过程

与那些"黑盒"机器学习模型不同,贝叶斯算法的决策过程是透明的。我们可以清楚地看到,是哪些词语影响了最终的判断:

console.log(`显著词: [${significantTokens.join(', ')}]`);
console.log(`垃圾概率: ${result.spamProbability.toFixed(2)}%`);
console.log(`正常概率: ${result.hamProbability.toFixed(2)}%`);

这种透明性让我们能够更好地理解模型的行为,也让我们能够更有针对性地改进训练数据。

4. 对中文的深刻理解

在我们的项目中,中文处理是一个关键挑战。而贝叶斯算法结合结巴分词( jieba ),展现出了对中文语言深刻的理解:

// 智能的中文分词
if (this.jieba) {
  const jiebaTokens = this.jieba.cut(token);
  processedTokens.push(...jiebaTokens);
}

这种对语言细节的关注,让我们的系统能够更好地理解中文消息的含义,而不是仅仅依赖于简单的关键词匹配。

参考的项目:

https://github.com/ramsayleung/bayes_spam_sniper

贝叶斯在行动:我们的实践故事

在我们的项目中,贝叶斯算法主要承担着垃圾信息检测的重任。让我们来看看它是如何工作的:

1. 训练:智慧的积累

我们的系统会从历史数据中提取高质量的训练样本:

// 只选择高置信度的样本进行训练
const spamWhere = {
  confidence: { gt: 70 }, // 高置信度垃圾
  detectionMethod: { in: ['ollama', 'bayes_ollama'] }
};

const hamWhere = {
  confidence: { lt: 30 }, // 低置信度
  action: 'pass', // 且被放行的消息
};

这种严格的筛选机制,确保了我们的训练数据的高质量。

2. 检测:智慧的应用

当一条新消息到来时,贝叶斯算法会:

  1. 清理文本:处理各种反垃圾技巧,如"合-约"→"合约"
  2. 智能分词:使用结巴分词处理中文
  3. 选择显著词:只关注最具区分度的词语
  4. 计算概率:基于历史数据计算垃圾概率
  5. 做出判断:根据阈值决定是否为垃圾
// 智能的文本清理
cleaned = cleaned.replace(/([一-龯])(\s+)([一-龯])/g, '$1$3'); // "想 赚 钱" → "想赚钱"

// 显著词的选择
const significantTokens = this.getSignificantTokens(tokens, probSpamPrior, probHamPrior);

3. 进化:智慧的成长

最美妙的地方在于,贝叶斯算法不是静态的。它会随着每一次判断不断学习:

// 增量训练
async trainIncremental(message: TrainedMessage): Promise<void> {
  this.trainMessage(message);
  if (this.autoSaveEnabled) {
    await this.saveState(); // 持久化学习成果
  }
}

这种持续学习的能力,让我们的系统能够适应不断变化的垃圾信息模式。

贝叶斯的智慧哲学

贝叶斯算法带给我们的,不仅仅是一个技术解决方案,更是一种智慧的哲学:

  1. 谦逊的态度:永远不要认为自己无所不知
  2. 开放的心态:愿意根据新的证据更新自己的认知
  3. 平衡的智慧:在确定性和不确定性之间找到平衡
  4. 透明的原则:让决策过程可解释、可理解

在信息安全的世界中,我们面临的不是一个静态的敌人,而是一个不断进化的挑战。贝叶斯算法以其独特的智慧,给了我们一个强大而灵活的工具,让我们能够在这个不断变化的世界中,始终保持警觉,又不失宽容。

结语:与智慧同行

选择贝叶斯算法,不是因为它最新最炫,而是因为它的智慧经得起时间的考验。在我们的项目中,它已经成为一个不可或缺的智慧伙伴,帮助我们在信息的海洋中,分辨真伪,守护安全。

而最美妙的地方在于,这段旅程还在继续。每一条新的消息,每一次新的判断,都是贝叶斯算法学习和成长的机会。我们与智慧同行,在不断变化的数字世界中,共同书写着安全与信任的新篇章。

欢迎测试,我们 bot 在 telegram 群组中反垃圾的实际效果:

https://t.me/SageGuardBot?startgroup=start

"在不确定的世界中,贝叶斯算法是我们的智慧灯塔,照亮前行的道路。"

本文为记录阅读 App 的资源接口,长期不定时更新接口

由于接口的特殊性,随时可能会失效

请各位收藏页面,以防止接口丢失

所有接口来源于公开网络,本文只是做收集和记录

感谢制作接口大神,让我们可以拥有更好的阅读体验~

软件下载:蓝奏云

书源接口

名称:八零小说
接口: https://www.yck2026.top/yuedu/shuyuan/json/id/6887.json
更新:2026 年 1 月

名称:爱去小说
接口: https://www.yck2026.top/yuedu/shuyuan/json/id/6886.json
更新:2026 年 1 月

名称:团夕小说
接口: https://www.yck2026.top/yuedu/shuyuan/json/id/6884.json
更新:2026 年 1 月

名称:八叉书库
接口: https://www.yck2026.top/yuedu/shuyuan/json/id/6882.json
更新:2026 年 1 月

名称:Lofter
接口: https://www.yck2026.top/yuedu/shuyuan/json/id/6873.json
更新:2026 年 1 月

名称:顶点小说
接口: https://www.yck2026.top/yuedu/shuyuan/json/id/6875.json
更新:2026 年 1 月

名称:必去读书库
接口: https://www.yck2026.top/yuedu/shuyuan/json/id/6785.json
更新:2026 年 1 月

名称:幻梦轻小说
接口: https://www.yck2026.top/yuedu/shuyuan/json/id/6280.json
更新:2026 年 1 月

聚合书源

名称:灰灰
接口: https://www.yck2026.top/yuedu/shuyuans/json/id/1026.json
更新:2026 年 1 月

名称:自用
接口: https://www.yck2026.top/yuedu/shuyuans/json/id/1024.json
更新:2026 年 1 月

名称:刺猬喵
接口: https://www.yck2026.top/yuedu/shuyuans/json/id/957.json
更新:2026 年 1 月

名称:沐鸢
接口: https://www.yck2026.top/yuedu/shuyuans/json/id/950.json
更新:2026 年 1 月

纯净规则

名称:烏雲净化
文件:规则.txt
更新:2026 年 1 月

openai 以及 claude 的 ai 内部脚本运行环境都是基于 node.js 运行时。codex 、claude code 等官方助手都是独家支持 npm 安装。
和 ai 最好的连接语言就是 js 、ts 。

以后各类设备底层都会内置 node.js 运行时,为了完成 ai 交互。

2025 一人公司年度复盘:从 0.1 到 1 的创业路

这一年花了很多时间在 UChart 的开发上面,不断迭代产品,从年初到年末,功能从少到多,从多到精。慢慢理解了为啥公司中产品经理总是改来改去,因为我自己在做产品的时候也是变来变去,年初把一种,年初把一种功能调整为另外一种形式。嗯,到年末又会把它重新改回来,这可能是产品经理经理常态总在不停不停的变化。
产品形态完成度约 20%,但核心功能已经能跑通
付费模式从单一$9.9 终身,调整到$16 终身,再升级到$30 终身+月付/年付多种模式

产品成果
✅ 每天稳定新增用户 100 人左右
✅ 付费转化率达到 5‰,虽然不高但已验证产品可行性
✅ 产品在同类竞品中具备竞争力,只要能获得流量就能留住用户

收入成果
✅ 2025 年付费总收入达到 900 美元
✅ 付费模式逐步完善,从单一到多元,商业化路径清晰

社媒成果
✅ 小红书:200+粉丝(从个位数起步)
✅ 推特:60+粉丝
✅ 积累了多个平台的运营经验,为 2026 年爆发做准备

2026 年我要做什么

战略调整
核心优先级:市场营销 > SEO 优化 > 产品迭代
从"开发驱动"转向"增长驱动"
优先解决获客问题,其次才是继续打磨产品

市场营销
提升小红书运营质量,从 200 粉丝突破到 1000+
持续运营推特,扩大影响力
尝试更多获客渠道,如 Reddit ,Product hunt

SEO 优化
优化网站 SEO ,提升自然搜索流量
产出高质量内容,建立行业权威
深入研究关键词,抓住精准流量

产品迭代
继续优化产品,但控制在合理的迭代节奏
基于用户反馈打磨核心功能
在保持产品竞争力的前提下,逐步提升完成度

闲着无聊让 codex 糊了个小工具,顺便混点赞(小白开发,各位佬轻喷~)

主要作用:
1、自动批量获取 Gemini Business 账号相关信息
2、将获取的信息整理转换,同步给 Business Gemini Pool
3、当同步数量少于设定数量时,自动创建新的账户补齐

ps:由于 Business token 有效期只有 12 小时,系统会每 6 小时自动执行 token 刷新、同步等,达到 token 保活的目的

使用到的项目(感谢~!):
Gemini Business2api:

Gemini Business 账号管理系统(使用 cf 邮件系统自动创建)

邮件系统搭建参考教程:

最后是中间件同步工具:
sync.zip
启动方式:
填写好.env 后
pip install -r requirements.txt
python app.py


📌 转载信息
转载时间:
2026/1/24 10:34:57

项目地址:

背景

楼主是学生党,去年为了帮忙开发毕业设计代码,也搭上了谷歌大善人的便车,照着论坛里佬们的思路,搭建了 Claude Code + Claude Code Router + Gemini Api (主要是 GCP 赠金和 done-hub)的框架。但是,在使用的时候,经常发生一些始料未及的报错(毕竟不是 Claude 自家模型)和缺陷(尤其是提示词的遗忘问题),因此在写毕设代码的同时,我也一直在迭代提示词组件,在这里小小分享一下,希望各位佬友能多多提出意见,交流 Claude Code 的提示词构建技巧和使用方法~


具体而言,在 Claude Code 中使用 Gemini 的优点和缺点都很明显:

优点:

  1. 科研思维与创新性:模型具备较强的跨领域知识理解能力,能够提供具有启发性的架构思路与创新方案。
  2. 百万级上下文窗口:1M Token 的上下文容量允许模型全量阅读大型代码库。模型能够维护函数签名、变量定义在长调用链与数据流中的一致性。
  3. 成本效益:基于调用次数的计费模式降低了大规模上下文注入的成本,适合科研场景下的大量文献与代码阅读。最重要的是,注入大批量的提示词一点也不心疼

缺点:

  1. Agent 调度迟缓:模型在使用 Subagent 时(尤其是 Explore)存在延迟与卡死现象。
  2. 遗忘风险:受限于滑窗注意力机制,模型在长对话中容易遗忘初始指令,特别是特定的格式约束;这导致模型一段时间后就会回归其初始的状态。
  3. 回复风格偏差:原生模型倾向于使用夸张、绝对化的词汇,并表现出过度的自信与讨好,不符合科研严谨性要求。(Gemini:你发现了问题的核心!
  4. 非专用编程模型缺陷:作为通用模型,其在 Bash 环境操作与文件系统细节(如文件名字符混淆、编码处理)上存在不稳定性。

此外,在科研与架构验证场景中,我更希望代码生成工具具备 “白盒性” 与 “可控性”。但是 Claude Code 自带的提示词天然倾向于自动化流程,例如使用 SubAgent 等等,但这往往是我不太期望的 (因为我需要提出具体的要求,并与 AI 共同讨论完整的设计方案和风险)。

针对上述局限,我设计了一套基于分层提示词系统与运行时钩子的小组件,以强化模型的工程约束(不过提示词是按照我自己的开发偏好来的,可能因人而异)。这套组件有以下特性:

运行时钩子体系

系统利用 Claude Code 的 Hooks 机制,部署了三个核心钩子以实现全生命周期的工程管控:

  1. 协议执行器 (env_enforcer.py)

    • 触发时机UserPromptSubmit(用户提问提交时)。
    • 功能:在上下文末端强制注入简短的提示词摘要和易遗忘的约束。这有效对抗了 Gemini 模型的长窗口遗忘问题,确保每一轮对话都重置回受控状态。
  2. 上下文管理器 (context_manager.py)

    • 触发时机PreCompact(压缩前)与 SessionStart(会话开始)。
    • 功能:自动扫描 Git 状态、目录结构及 skills/ 下的可用工具,生成 .compact_args.md 快照。在新会话启动时注入该快照,使模型能迅速恢复项目全貌认知,并感知可用的高级能力(Skills)。
  3. 工具安全检查 (pre_tool_guard.py)

    • 触发时机PreToolUse(工具调用前)。
    • 功能:实施多维度的即时修正与拦截。
      • 路径安全:将绝对路径自动修正为相对路径,拦截项目外的非授权访问。
      • 环境修正:自动向 Bash 命令注入 PYTHONIOENCODING 及环境激活脚本,解决 Windows 平台下的编码与环境一致性问题。

行为与风格约束

通过 CLAUDE.mdstyle.md 定义静态规范,修正模型的回复风格。

  • 语言规范:强制全中文回复,禁用营销性术语(如 “赋能”、“痛点”)。为此提供了完整的禁止词列表和 few-shot 替换示例。
  • 句式约束:要求使用无修饰的一般陈述句,禁止使用夸张的形容词与副词。
  • 操作规范
    • 静默执行:减少不必要的口语化确认。
    • 验证后执行:针对 Bash 操作与文件系统,强制先验证后执行。
    • 相对路径优先:规范文件路径的引用方式。

除此之外,output-styles\python-architect.md 中还插入了一些著名开发者的工程哲学(大概算 “请神” 吧…不过不会继承某些人的坏脾气),例如:

  • Linus Torvalds:以数据为中心, 优先考虑内存布局、清晰的数据结构
  • Rich Hickey:简单 容易,拒绝为了贪图方便而引入耦合。
  • Kent Beck: 执行严格的测试驱动开发(TDD),以及尽早识别并处理 “代码异味”。
    . . .

递归上下文完整性

针对代码生成,引入 “递归上下文完整性” 协议。该协议作为核心防幻觉机制,要求模型在生成或修改代码前严格遵循以下步骤:

  • 源头溯源:必须溯源至任何引用变量或函数的原始定义处,禁止仅凭调用处的上下文推断接口签名。
  • 继承链验证:若涉及类继承或接口实现,必须检索父类定义以验证完整的类型契约。
  • 上下文饱和:在消除所有类型歧义前,不得进行代码生成。


希望各位如果用的满意的话可以随手点个 stars ,也欢迎各位提出修改意见~(我没有系统的学过提示词工程,所以某些设计可能比较拙劣)


📌 转载信息
原作者:
ModestMouse
转载时间:
2026/1/24 10:34:21

浏览了社区和 GitHub 上众多的 Surge 配置仓库,我发现一个普遍痛点:配置项冗余且混乱。很多早已废弃的参数、默认即可的开关、甚至相互冲突的规则被盲目复制粘贴,对于追求极致整洁的强迫症患者来说,阅读这些配置文件简直是一种折磨。

为此,我决定重构一份 “反熵增” 的配置清单。

核心原则:如无必要,勿增实体。

  1. 剔除默认值:如果 Surge 的默认行为已经合理,绝不显式写入配置文件。
  2. 拒绝无效参数:清理死代码和过时配置(如 VIF 模式下的 skip-proxy)。
  3. 仅保留关键:只体现需要根据国情额外配置的,或社区公认的最佳实践(Best Practice)。

基于以上原则,我整理了以下配置。希望它能成为一份逻辑自洽、甚至赏心悦目的基准配置。欢迎大家从原理层面进行 “代码审查” 和讨论,我们将持续迭代更新。

[General] 核心设置

[General]
# --- 连接稳定性与测试 ---
# 开启 Wi-Fi 助理:当 Wi-Fi 信号极差或无法联网时,自动使用蜂窝数据
wifi-assist = true
# 连通性测试:用于检测是否具备互联网访问能力
internet-test-url = http://wifi.vivo.com.cn/generate_204
# 代理测速:用于测试代理节点的延迟基准
proxy-test-url = http://cp.cloudflare.com/generate_204

# --- 物理网络旁路 (核心优化) ---
# 作用:流量不经过 Surge 虚拟网卡,直接由物理网卡处理 (VIF 模式下唯一有效的绕过方式)
# 1. 192/10/172: 解决局域网传输 (NAS) 发热、跑不满带宽的问题;确保公共 Wi-Fi 认证页面正常弹出
# 2. 100.64: 解决 Tailscale/ZeroTier 组网连接失败、运营商内网服务异常
# 3. 224/239/255: 解决 AirPlay 投屏找不到设备、智能家居 SSDP 发现问题
tun-excluded-routes = 192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12, 100.64.0.0/10, 239.255.255.250/32, 224.0.0.0/24, 255.255.255.255/32

# --- DNS 解析 ---
# 策略:阿里(223) + 腾讯(119) 提供高可用解析,System 作兜底
# 注意:保留 system 是为了在公共 Wi-Fi 未认证(外网不通)时,能获取路由器下发的内网 IP 以弹出登录页
dns-server = 223.5.5.5, 119.29.29.29, system
# 劫持转发:强制接管设备内所有发往 53 端口的 DNS 查询 (防止 App 自定义 DNS 导致分流失效)
hijack-dns = *:53

按需选配:

# ============================================
# Optional / Advanced Settings (按需启用)
# ============================================

# --- 远程控制器 (HTTP API) ---
# 允许通过 HTTP API 控制 Surge (如 Yacd 面板 / 快捷指令)
# http-api = pass@127.0.0.1:6171
# http-api-tls = false
# http-api-web-dashboard = false

# --- 局域网访问 (Wi-Fi/热点共享) ---
# 允许局域网内其他设备连接本机的代理服务
# allow-wifi-access = false
# allow-hotspot-access = true
# 代理服务监听端口 (默认 HTTP:6152, SOCKS5:6153)
# wifi-access-http-port = 6152
# wifi-access-socks5-port = 6153

# --- 高级网络行为 ---
# 隐藏状态栏 VPN 图标 
# hide-vpn-icon = false
# 排除简单主机名 (不包含点的域名) 走代理,通常用于让内部域名强制直连
# exclude-simple-hostnames = true
# IPv6 支持 (默认关闭,国内环境建议保持关闭以提升兼容性)
# ipv6 = false
# 当遇到 REJECT 策略时,是否显示网页报错页面 (默认为直接断开)
# show-error-page-for-reject = true
# UDP 转发失败时的回退行为 (默认为 REJECT,可选 DIRECT)
# udp-policy-not-supported-behaviour = REJECT

# --- DNS 与数据源 ---
# 加密 DNS (DoH/DoQ): 除非有抗污染强需求,否则不建议开启,会增加延迟
# encrypted-dns-server = https://223.5.5.5/dns-query, quic://dns.alidns.com
# 自定义 GeoIP 数据库源 (仅当默认数据库不准时使用)
# geoip-maxmind-url = https://raw.githubusercontent.com/Loyalsoldier/geoip/release/Country.mmdb
# 从 /etc/hosts 读取 DNS 记录 (仅 macOS)
# read-etc-hosts = true

# --- 遗留与替代项 (不推荐使用) ---
# [VIF Mode 无效] 即使配置也被忽略,物理直连请使用 tun-excluded-routes
# skip-proxy = 192.168.0.0/24, 10.0.0.0/8, 172.16.0.0/12, *.local, localhost

# [建议使用模块] 强制返回真实 IP (解决游戏 NAT 类型或去 IP 校验)
# 推荐使用 Surge 模块或 Rule 进行管理,保持主配置简洁
# always-real-ip = *.srv.nintendo.net, *.stun.playstation.net, xbox.*.microsoft.com

未完待续…


📌 转载信息
转载时间:
2026/1/24 10:34:11

随着自建节点的流量增长,节点管理员们可能会遇到的需要处理的情况也会增多。

所以,除了主题删除之外,今天的功能部署为节点管理员们增加了又一个新的管理工具:

屏蔽列表

你可以在自己的每个节点里添加最多 100 个屏蔽用户名。被屏蔽的用户将无法在你的节点里创建新主题或者回复。

如果节点管理员遇到一些更极端的恶意破坏或者 spam 行为,节点管理员觉得有必要通过 V2EX 全站对指定用户名进行彻底 ban ,那么可以在 /go/chamber 节点告知。


自从 node/create 功能在 2026 年 1 月 10 日上线,我们希望的是这能够成为一个可以用来创建自己的独特社区的工具。所以大部分情况下,我们想做的事情是为节点创建者提供更多更好的工具,而不是去审核所有新内容。

每个节点里的内容,是节点创建者及节点参与者自己的。

下面代码中 f()会被重复执行吗?

package main

import (
	"fmt"
	"sync"
)

type Once struct {
	m    sync.Mutex
	done uint32
}

func (o *Once) Do(f func()) {
	if o.done == 1 {
		return
	}

	o.m.Lock()
	defer o.m.Unlock()

	fmt.Println("bing: ", o.done)

	if o.done == 0 {
		o.done = 1
		f()
	}
}

func main() {
	var once Once
	wg := sync.WaitGroup{}

	wg.Add(100)
	for i := 0; i < 100; i++ {
		go func() {
			defer wg.Done()
			once.Do(func() {
				println("executed---------》 ")
			})
		}()
	}
	wg.Wait()
}


接:雨云无限白嫖 FRP 服务器攻略(无 aff)

@fatekey 大佬的方案是单独开一个容器,我想着反正我有青龙面板在跑脚本,站在巨人的肩膀上顺手写了一个,把多账户也写进来了

一、运行效果

先看实际运行的效果吧
【前两个账号运行已签到,仅测试多账户切换功能,临时注册的第三个号用于测试签到功能】

## 开始执行... 2026-01-23 16:57:53

2026-01-23 16:57:54.116950139 [W:onnxruntime:Default, cpuid_info.cc:91 LogEarlyWarning] Unknown CPU vendor. cpuinfo_vendor value: 16
2026-01-23 16:57:54,191 - INFO - --------------------------------------------------------------------------------
2026-01-23 16:57:54,191 - INFO - 雨云签到工具 by SerendipityR ~
2026-01-23 16:57:54,191 - INFO - Github发布页: https://github.com/SerendipityR-2022/Rainyun-Qiandao
2026-01-23 16:57:54,192 - INFO - --------------------------------------------------------------------------------
2026-01-23 16:57:54,192 - INFO - 雨云签到工具容器版 by fatekey ~
2026-01-23 16:57:54,192 - INFO - Github发布页: https://github.com/fatekey/Rainyun-Qiandao
2026-01-23 16:57:54,192 - INFO - --------------------------------------------------------------------------------
2026-01-23 16:57:54,192 - INFO -                    项目为二次开发青龙脚本化运行
2026-01-23 16:57:54,192 - INFO -                      本项目基于上述项目开发
2026-01-23 16:57:54,192 - INFO -                 本项目仅作为学习参考,请勿用于其他用途
2026-01-23 16:57:54,192 - INFO - --------------------------------------------------------------------------------
2026-01-23 16:57:54,192 - INFO - ✅ 成功解析3个账号
2026-01-23 16:57:54,192 - INFO - 
================= 处理第1个账号 ==================
2026-01-23 16:57:54,192 - INFO - 
========== 开始处理账号:TACGN ==========
2026-01-23 16:57:54,192 - INFO - ⏳ 随机延时 0 分钟 35 秒
2026-01-23 16:58:29,794 - INFO - ✅ Selenium驱动初始化成功,路径:/usr/bin/chromedriver
2026-01-23 16:58:29,809 - INFO - ✅ 已注入stealth.min.js反检测脚本
2026-01-23 16:58:29,809 - INFO - ⏳ 发起登录请求
2026-01-23 16:58:29,810 - INFO - 🌐 访问雨云登录页
2026-01-23 16:58:31,258 - INFO - 页面标题:登录 | 雨云
2026-01-23 16:58:31,258 - INFO - ⏳ 等待登录表单元素加载...
2026-01-23 16:58:31,376 - INFO - 📝 输入账号密码
2026-01-23 16:58:31,775 - INFO - ⏳ 正在登录中,耗时较长请稍等……
2026-01-23 16:58:54,899 - INFO - ✅ 未触发登录验证码
2026-01-23 16:58:59,906 - INFO - 当前页面: https://app.rainyun.com/dashboard
2026-01-23 16:58:59,910 - INFO - 页面标题: 总览 | 雨云
2026-01-23 16:58:59,940 - INFO - ✅ 账号登录成功:TACGN
2026-01-23 16:58:59,940 - INFO - 🌐 访问赚取积分页
2026-01-23 16:59:00,958 - INFO - 当前页面: https://app.rainyun.com/account/reward/earn
2026-01-23 16:59:00,962 - INFO - 页面标题: 赚取积分 | 雨云
2026-01-23 16:59:00,962 - INFO - 🔍 查找每日签到按钮
2026-01-23 16:59:01,016 - INFO - 📌 签到状态:已完成,无需重复签到
2026-01-23 16:59:01,047 - INFO - 💰 当前积分:700(约0.35元)
2026-01-23 16:59:01,131 - INFO - ✅ 账号TACGN浏览器已关闭
2026-01-23 16:59:01,131 - INFO - ✅ 临时文件清理完成
2026-01-23 16:59:01,131 - INFO - 
========== 账号TACGN处理完成 ==========

2026-01-23 16:59:05,996 - INFO - 
================= 处理第2个账号 ==================
2026-01-23 16:59:05,996 - INFO - 
========== 开始处理账号:ACGN_T ==========
2026-01-23 16:59:05,996 - INFO - ⏳ 随机延时 4 分钟 3 秒
2026-01-23 17:03:09,549 - INFO - ✅ Selenium驱动初始化成功,路径:/usr/bin/chromedriver
2026-01-23 17:03:09,564 - INFO - ✅ 已注入stealth.min.js反检测脚本
2026-01-23 17:03:09,564 - INFO - ⏳ 发起登录请求
2026-01-23 17:03:09,564 - INFO - 🌐 访问雨云登录页
2026-01-23 17:03:11,015 - INFO - 页面标题:登录 | 雨云
2026-01-23 17:03:11,016 - INFO - ⏳ 等待登录表单元素加载...
2026-01-23 17:03:11,155 - INFO - 📝 输入账号密码
2026-01-23 17:03:11,584 - INFO - ⏳ 正在登录中,耗时较长请稍等……
2026-01-23 17:03:34,692 - INFO - ✅ 未触发登录验证码
2026-01-23 17:03:39,699 - INFO - 当前页面: https://app.rainyun.com/dashboard
2026-01-23 17:03:39,703 - INFO - 页面标题: 总览 | 雨云
2026-01-23 17:03:39,733 - INFO - ✅ 账号登录成功:ACGN_T
2026-01-23 17:03:39,733 - INFO - 🌐 访问赚取积分页
2026-01-23 17:03:40,777 - INFO - 当前页面: https://app.rainyun.com/account/reward/earn
2026-01-23 17:03:40,782 - INFO - 页面标题: 赚取积分 | 雨云
2026-01-23 17:03:40,783 - INFO - 🔍 查找每日签到按钮
2026-01-23 17:03:40,858 - INFO - 📌 签到状态:已完成,无需重复签到
2026-01-23 17:03:40,881 - INFO - 💰 当前积分:4684(约2.34元)
2026-01-23 17:03:40,966 - INFO - ✅ 账号ACGN_T浏览器已关闭
2026-01-23 17:03:40,966 - INFO - ✅ 临时文件清理完成
2026-01-23 17:03:40,966 - INFO - 
========== 账号ACGN_T处理完成 ==========

2026-01-23 17:03:43,883 - INFO - 
================= 处理第3个账号 ==================
2026-01-23 17:03:43,883 - INFO - 
========== 开始处理账号:ACGN ==========
2026-01-23 17:03:43,883 - INFO - ⏳ 随机延时 4 分钟 50 秒
2026-01-23 17:08:34,684 - INFO - ✅ Selenium驱动初始化成功,路径:/usr/bin/chromedriver
2026-01-23 17:08:34,698 - INFO - ✅ 已注入stealth.min.js反检测脚本
2026-01-23 17:08:34,699 - INFO - ⏳ 发起登录请求
2026-01-23 17:08:34,699 - INFO - 🌐 访问雨云登录页
2026-01-23 17:08:36,143 - INFO - 页面标题:登录 | 雨云
2026-01-23 17:08:36,143 - INFO - ⏳ 等待登录表单元素加载...
2026-01-23 17:08:36,285 - INFO - 📝 输入账号密码
2026-01-23 17:08:36,657 - INFO - ⏳ 正在登录中,耗时较长请稍等……
2026-01-23 17:08:59,802 - INFO - ✅ 未触发登录验证码
2026-01-23 17:09:04,809 - INFO - 当前页面: https://app.rainyun.com/dashboard
2026-01-23 17:09:04,813 - INFO - 页面标题: 总览 | 雨云
2026-01-23 17:09:04,842 - INFO - ✅ 账号登录成功:ACGN
2026-01-23 17:09:04,842 - INFO - 🌐 访问赚取积分页
2026-01-23 17:09:05,841 - INFO - 当前页面: https://app.rainyun.com/account/reward/earn
2026-01-23 17:09:05,871 - INFO - 页面标题: 赚取积分 | 雨云
2026-01-23 17:09:05,871 - INFO - 🔍 查找每日签到按钮
2026-01-23 17:09:05,940 - INFO - 📌 签到状态:领取奖励,开始领取
2026-01-23 17:09:06,072 - INFO - ⚠️ 触发签到验证码
2026-01-23 17:09:06,253 - INFO - 🔄 验证码处理第1次尝试(最大10次)
2026-01-23 17:09:06,810 - INFO - 开始下载验证码图片(1):https://turing.captcha.qcloud.com/cap_union_new_getcapbysig?img_index=1&image=02680900003d283800000015123b75d53fed&sess=s0HtD0kpY6pcWGI6FzFpt6ZiszTr2EhH-VfdHwPwxIdqv34Z-7I44K0-_RhKCQ_D1pczn56AhHTy7TzWXqVjayAnecALMlUWYf152tXUBM_URxYIPDxvoDD7jXbk7mwSIeJKDAUtmTTnuuaRcoqdw3DlBpEXv3Xc4RbCewuRGJZUAkZPrzzB8njktvXIOPrqAhs4UafKm96GgAUPJExW9_2PkkRGBKSTS43H1uLzB9el3g70xLMDYSd2TywoxM5Ps2idtfMPBn_aMw93gVXYLGwpX0Iztn4QG1vFv9VJj6NgCvOU2YSfCmTrGyEXxdzPnGglGJAKJFB0FfuxP4bM3-0O4DQt4l-5NsT52KR_8WcG7rvohxQXZy1sRw9MY84c31oFqKfUyPsa49v1VdtmranaOtiaDLX6SjgI6rJPvt2_kSelSHRNUWtA**
2026-01-23 17:09:07,077 - INFO - 开始下载验证码图片(2):https://turing.captcha.qcloud.com/cap_union_new_getcapbysig?img_index=0&image=02680900003d283800000015123b75d53fed&sess=s0HtD0kpY6pcWGI6FzFpt6ZiszTr2EhH-VfdHwPwxIdqv34Z-7I44K0-_RhKCQ_D1pczn56AhHTy7TzWXqVjayAnecALMlUWYf152tXUBM_URxYIPDxvoDD7jXbk7mwSIeJKDAUtmTTnuuaRcoqdw3DlBpEXv3Xc4RbCewuRGJZUAkZPrzzB8njktvXIOPrqAhs4UafKm96GgAUPJExW9_2PkkRGBKSTS43H1uLzB9el3g70xLMDYSd2TywoxM5Ps2idtfMPBn_aMw93gVXYLGwpX0Iztn4QG1vFv9VJj6NgCvOU2YSfCmTrGyEXxdzPnGglGJAKJFB0FfuxP4bM3-0O4DQt4l-5NsT52KR_8WcG7rvohxQXZy1sRw9MY84c31oFqKfUyPsa49v1VdtmranaOtiaDLX6SjgI6rJPvt2_kSelSHRNUWtA**
2026-01-23 17:09:07,375 - ERROR - ⚠️ 图案2识别率0.0000低于阈值0.4
2026-01-23 17:09:07,376 - ERROR - ❌ 验证码坐标重复,答案无效
2026-01-23 17:09:07,376 - ERROR - ❌ 验证码处理失败:验证码答案无效
2026-01-23 17:09:07,376 - ERROR - ⏳ 刷新验证码中,稍后重试……
2026-01-23 17:09:13,495 - INFO - 🔄 验证码处理第2次尝试(最大10次)
2026-01-23 17:09:13,518 - INFO - 开始下载验证码图片(1):https://turing.captcha.qcloud.com/cap_union_new_getcapbysig?img_index=1&image=0268090000946c2b0000000bb5e61fd63312&sess=s0_hrS7I5bVMRdivCWNVX_5xijZd5qBztok8b_H7bwMciiNFNIe3KMmj4IPktJO-cbs-8dl7upCI40ZosuxWWRjpXlIbF-P3ZWNoFjjg5G9dMFSybpTUgmgQO1lGEy1QSjGIghi44ITJTpCGcF4ym8wD4iU0xLCVakXfJvTvPiJbxl055LMVFM8W1FM1TtThPXpkg5h9JgYXRHols_wYhIgOI_dRxdgl3r_h-dSKI109RxypesTYee-w0m-Lw_41AM1etin4G_Iamp3lveRUaOtNV1JT4ssYxJ3DR1NZ8SEfN3yxvn9Z-_dxfifqGBxz8hkBmv4vsmx4M9imY60mxrr32HJt0K1ODVgIkzXKA0mgcq1DsSXM0AlcE765_pI_-NP9BgOPXEivjsEDpnxrS-nUFA1DJEz6urpWBwjgZN80OGgAAIs1XL1A**
2026-01-23 17:09:13,731 - INFO - 开始下载验证码图片(2):https://turing.captcha.qcloud.com/cap_union_new_getcapbysig?img_index=0&image=0268090000946c2b0000000bb5e61fd63312&sess=s0_hrS7I5bVMRdivCWNVX_5xijZd5qBztok8b_H7bwMciiNFNIe3KMmj4IPktJO-cbs-8dl7upCI40ZosuxWWRjpXlIbF-P3ZWNoFjjg5G9dMFSybpTUgmgQO1lGEy1QSjGIghi44ITJTpCGcF4ym8wD4iU0xLCVakXfJvTvPiJbxl055LMVFM8W1FM1TtThPXpkg5h9JgYXRHols_wYhIgOI_dRxdgl3r_h-dSKI109RxypesTYee-w0m-Lw_41AM1etin4G_Iamp3lveRUaOtNV1JT4ssYxJ3DR1NZ8SEfN3yxvn9Z-_dxfifqGBxz8hkBmv4vsmx4M9imY60mxrr32HJt0K1ODVgIkzXKA0mgcq1DsSXM0AlcE765_pI_-NP9BgOPXEivjsEDpnxrS-nUFA1DJEz6urpWBwjgZN80OGgAAIs1XL1A**
2026-01-23 17:09:14,002 - ERROR - ⚠️ 图案2识别率0.1515低于阈值0.4
2026-01-23 17:09:14,003 - ERROR - ❌ 验证码坐标重复,答案无效
2026-01-23 17:09:14,003 - ERROR - ❌ 验证码处理失败:验证码答案无效
2026-01-23 17:09:14,003 - ERROR - ⏳ 刷新验证码中,稍后重试……
2026-01-23 17:09:23,108 - INFO - 🔄 验证码处理第3次尝试(最大10次)
2026-01-23 17:09:23,131 - INFO - 开始下载验证码图片(1):https://turing.captcha.qcloud.com/cap_union_new_getcapbysig?img_index=1&image=0268090000f13d2300000015123b75d53f2f&sess=s02qPcN6ye2H2TPQfQ9ghy_0L3jB722YFRMCmx-rWnjm4UgxUo3F4WLoUzz5JczVgNJMtQwRWLFRo4OvXls1zjaajvPXch4RMoo6YZOavScFvdGaB-9B-ecxWvfcPx7ZTEb03-5MTmG-P2LipAwhLGAYKO0JOK6Rb6z3KYkAy9pxHIXYP9FaLlwdRvLsEDbqKWJZKCP4IHJ9mav4XH2EoTFfWGYMR-sA53gKcavXkSbzg2J_3ntSL6rszaLREZi9ZCSn1bPIDt16NYUXhHhlPFCJmBzIh41fG-nFTtpB-A8_i_vPaNo3mwlxJ9KojhSP37q7CfeASWq8-DTtI3OnT-mZbyVzoDHvBgQOiiQu5o0_VxQtxzWD9vNmVbErvVsP1VxQEVv0GCFywapI0H-R2DimaJI87vvVkVIzVce2MZQJ_lxWICubZ-RA**
2026-01-23 17:09:23,352 - INFO - 开始下载验证码图片(2):https://turing.captcha.qcloud.com/cap_union_new_getcapbysig?img_index=0&image=0268090000f13d2300000015123b75d53f2f&sess=s02qPcN6ye2H2TPQfQ9ghy_0L3jB722YFRMCmx-rWnjm4UgxUo3F4WLoUzz5JczVgNJMtQwRWLFRo4OvXls1zjaajvPXch4RMoo6YZOavScFvdGaB-9B-ecxWvfcPx7ZTEb03-5MTmG-P2LipAwhLGAYKO0JOK6Rb6z3KYkAy9pxHIXYP9FaLlwdRvLsEDbqKWJZKCP4IHJ9mav4XH2EoTFfWGYMR-sA53gKcavXkSbzg2J_3ntSL6rszaLREZi9ZCSn1bPIDt16NYUXhHhlPFCJmBzIh41fG-nFTtpB-A8_i_vPaNo3mwlxJ9KojhSP37q7CfeASWq8-DTtI3OnT-mZbyVzoDHvBgQOiiQu5o0_VxQtxzWD9vNmVbErvVsP1VxQEVv0GCFywapI0H-R2DimaJI87vvVkVIzVce2MZQJ_lxWICubZ-RA**
2026-01-23 17:09:23,677 - INFO - 🎯 图案 1 坐标(37,277),匹配率:0.6552
2026-01-23 17:09:23,677 - INFO - 🎯 图案 2 坐标(598,114),匹配率:0.5641
2026-01-23 17:09:23,677 - INFO - 🎯 图案 3 坐标(437,197),匹配率:0.6207
2026-01-23 17:09:26,517 - INFO - 📤 提交验证码
2026-01-23 17:09:31,670 - INFO - ✅ 验证码验证通过
2026-01-23 17:09:36,676 - INFO - ✅ 签到奖励领取成功
2026-01-23 17:09:36,699 - INFO - 💰 当前积分:700(约0.35元)
2026-01-23 17:09:36,788 - INFO - ✅ 账号ACGN浏览器已关闭
2026-01-23 17:09:36,788 - INFO - ✅ 临时文件清理完成
2026-01-23 17:09:36,788 - INFO - 
========== 账号ACGN处理完成 ==========

2026-01-23 17:09:40,305 - INFO - 
🎉 所有账号处理完成!

## 执行结束... 2026-01-23 17:09:40  耗时 707 秒   

二、前置条件

青龙面板:我是直接用的 1panel 应用商店里的青龙面板
雨云账号密码:自个注册去

三、准备工作

青龙面板安装依赖:安装不上的,不会的请自行搜索教程或者请教 AI 了哦
—— NodeJs:chromium
—— Python3:selenium
—— Linux:chromium-driver
青龙面板配置环境变量:想设置多少自己设置就好了
—— RAINYUN_ACCOUNT
—— [[“账号 1”,“账号 1 密码”],[“账号 2”,“账号 2 密码”]]

四、创建文件

同目录下创建以下两个文件

stealth.min.js
stealth.min.js.txt

rainyun.py
rainyun.py.txt

删掉 [.txt] 后缀上传就好了,顺便贴出 rainyun.py 的代码如下

import json
import logging
import os
import random
import re
import sys
import time
from typing import Tuple, Optional, List

import cv2
import ddddocr
import requests
from selenium import webdriver
from selenium.common import TimeoutException, WebDriverException, NoSuchElementException
from selenium.webdriver import ActionChains
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.webdriver import WebDriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait

# ===================== 青龙面板专属配置(常量不抽离) =====================
CONFIG = {
    "timeout": 20,  # 青龙面板网络可能不稳定,延长超时时间
    "max_delay": 5,  # 最大随机等待分钟数
    "max_captcha_retry": 10,  # 验证码最大重试次数(防止递归栈溢出)
    "similarity_threshold": 0.4,  # 降低阈值提升识别率
    "script_path": os.path.dirname(os.path.abspath(__file__)),  # 青龙脚本所在目录
    "temp_path": os.path.join(os.path.dirname(os.path.abspath(__file__)), "temp"),  # 临时文件路径
    "rainyun_login_url": "https://app.rainyun.com/auth/login",
    "rainyun_earn_url": "https://app.rainyun.com/account/reward/earn"
}

# 全局日志对象(仅日志全局化,核心变量均函数内初始化)
logger = logging.getLogger(__name__)

def init_logger():
    """初始化青龙面板日志格式(增强版)"""
    logging.basicConfig(
        level=logging.INFO,
        format='%(asctime)s - %(levelname)s - %(message)s',
        handlers=[logging.StreamHandler(sys.stdout)]
    )
    # 打印项目信息
    logger.info("-"*80)
    logger.info("雨云签到工具 by SerendipityR ~")
    logger.info("Github发布页: https://github.com/SerendipityR-2022/Rainyun-Qiandao")
    logger.info("-"*80)
    logger.info("雨云签到工具容器版 by fatekey ~")
    logger.info("Github发布页: https://github.com/fatekey/Rainyun-Qiandao")
    logger.info("-"*80)
    logger.info("                   项目为二次开发青龙脚本化运行")
    logger.info("                     本项目基于上述项目开发")
    logger.info("                本项目仅作为学习参考,请勿用于其他用途")
    logger.info("-"*80)

def init_selenium() -> WebDriver:
    """初始化青龙面板专用Selenium驱动(每次调用新建实例,避免缓存污染)"""
    ops = Options()
    # 容器环境必需配置
    ops.add_argument("--no-sandbox")
    ops.add_argument("--disable-dev-shm-usage")
    ops.add_argument("--headless=new")
    ops.add_argument("--disable-gpu")
    ops.add_argument("--window-size=1920,1080")
    ops.add_argument("--user-agent=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36")
    # 反爬配置
    ops.add_experimental_option("excludeSwitches", ["enable-automation"])
    ops.add_experimental_option('useAutomationExtension', False)
    ops.add_argument("--disable-blink-features=AutomationControlled")
    
    # 青龙面板固定驱动路径校验
    driver_path = "/usr/bin/chromedriver"
    if not os.path.exists(driver_path) or not os.access(driver_path, os.X_OK):
        raise FileNotFoundError(
            f"青龙面板未安装chromium-driver!\n"
            f"请在青龙终端执行:apt update && apt install -y chromium-driver"
        )

    try:
        service = Service(executable_path=driver_path)
        driver = webdriver.Chrome(service=service, options=ops)
        # 清空缓存(双重保障)
        driver.delete_all_cookies()
        logger.info(f"✅ Selenium驱动初始化成功,路径:{driver_path}")
        return driver
    except WebDriverException as e:
        logger.error(f"❌ 驱动启动失败:{str(e)}")
        raise

def check_stealth_js() -> str:
    """检查青龙脚本目录下的stealth.min.js"""
    js_path = os.path.join(CONFIG["script_path"], "stealth.min.js")
    if not os.path.exists(js_path):
        logger.error(f"❌ 未找到stealth.min.js!请将文件上传到青龙脚本目录:{CONFIG['script_path']}")
        logger.info("📥 下载地址:https://raw.githubusercontent.com/berstend/puppeteer-extra/master/packages/puppeteer-extra-plugin-stealth/evasions/stealth.min.js")
        sys.exit(1)
    return js_path

def inject_stealth_js(driver: WebDriver):
    """注入反检测脚本(传入driver实例,解耦全局变量)"""
    js_path = check_stealth_js()
    with open(js_path, "r", encoding="utf-8") as f:
        js = f.read()
    driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {"source": js})
    logger.info("✅ 已注入stealth.min.js反检测脚本")

def download_image(url: str, filename: str, img_index: int) -> bool:
    """下载图片(带URL日志+青龙面板加请求头防拦截)"""
    os.makedirs(CONFIG["temp_path"], exist_ok=True)
    try:
        logger.info(f"开始下载验证码图片({img_index}):{url}")
        headers = {
            "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
            "Referer": "https://app.rainyun.com/"
        }
        response = requests.get(url, headers=headers, timeout=10)
        response.raise_for_status()
        path = os.path.join(CONFIG["temp_path"], filename)
        with open(path, "wb") as f:
            f.write(response.content)
        return True
    except Exception as e:
        logger.error(f"❌ 下载图片失败 {url}:{str(e)}")
        return False

# ========== 工具函数(精简+健壮性优化) ==========
def get_url_from_style(style: str) -> Optional[str]:
    """从style属性提取URL"""
    try:
        match = re.search(r'url\(["\']?(.*?)["\']?\)', style)
        return match.group(1) if match else None
    except Exception:
        return None

def get_width_from_style(style: str) -> str:
    """从style属性提取宽度"""
    match = re.search(r'width:\s*([\d.]+)px', style)
    return match.group(1) if match else "300"

def get_height_from_style(style: str) -> str:
    """从style属性提取高度"""
    match = re.search(r'height:\s*([\d.]+)px', style)
    return match.group(1) if match else "150"

def compute_similarity(img1_path: str, img2_path: str) -> Tuple[float, int]:
    """青龙面板适配:SIFT不可用时用ORB(增加异常兜底)"""
    try:
        img1 = cv2.imread(img1_path, cv2.IMREAD_GRAYSCALE)
        img2 = cv2.imread(img2_path, cv2.IMREAD_GRAYSCALE)
        if img1 is None or img2 is None:
            logger.warning(f"❌ 图片读取失败:{img1_path} 或 {img2_path}")
            return 0.0, 0

        # 优先SIFT,降级ORB
        try:
            sift = cv2.SIFT_create()
            norm = cv2.NORM_L2
        except AttributeError:
            sift = cv2.ORB_create()
            norm = cv2.NORM_HAMMING
            logger.warning("⚠️ SIFT不可用,使用ORB匹配")

        kp1, des1 = sift.detectAndCompute(img1, None)
        kp2, des2 = sift.detectAndCompute(img2, None)
        if des1 is None or des2 is None:
            return 0.0, 0

        bf = cv2.BFMatcher(norm, crossCheck=False)
        matches = bf.knnMatch(des1, des2, k=2)
        good = [m for m, n in matches if m.distance < 0.8 * n.distance]
        similarity = len(good) / len(matches) if matches else 0.0
        return similarity, len(good)
    except Exception as e:
        logger.error(f"❌ 相似度计算失败:{str(e)}")
        return 0.0, 0

def download_captcha_img(driver: WebDriver, wait: WebDriverWait) -> bool:
    """下载并分割验证码图片(解耦全局变量)"""
    try:
        # 清空旧临时文件
        if os.path.exists(CONFIG["temp_path"]):
            for f in os.listdir(CONFIG["temp_path"]):
                os.remove(os.path.join(CONFIG["temp_path"], f))

        # 定位验证码背景图
        slideBg = wait.until(EC.visibility_of_element_located((By.XPATH, '//*[@id="slideBg"]')))
        img1_url = get_url_from_style(slideBg.get_attribute("style"))
        if not img1_url or not download_image(img1_url, "captcha.jpg", 1):
            return False

        # 定位验证码碎片图
        sprite = wait.until(EC.visibility_of_element_located((By.XPATH, '//*[@id="instruction"]/div/img')))
        if not download_image(sprite.get_attribute("src"), "sprite.jpg", 2):
            return False

        # 分割碎片图
        raw = cv2.imread(os.path.join(CONFIG["temp_path"], "sprite.jpg"))
        if raw is None:
            logger.error("❌ 验证码碎片图读取失败")
            return False
        w = raw.shape[1]
        for i in range(3):
            cv2.imwrite(
                os.path.join(CONFIG["temp_path"], f"sprite_{i+1}.jpg"),
                raw[:, w//3*i : w//3*(i+1)]
            )
        return True
    except TimeoutException:
        logger.error("❌ 验证码图片加载超时")
        return False
    except Exception as e:
        logger.error(f"❌ 验证码图片处理失败:{str(e)}")
        return False

def check_answer(result: dict) -> bool:
    """检查验证码答案有效性(带识别率日志)"""
    valid = True
    for i in range(3):
        sim = float(result.get(f"sprite_{i+1}.similarity", 0))
        if sim < CONFIG["similarity_threshold"]:
            logger.error(f"⚠️ 图案{i+1}识别率{sim:.4f}低于阈值{CONFIG['similarity_threshold']}")
            valid = False
            break
    # 检查坐标唯一性
    positions = [result.get(f"sprite_{i+1}.position") for i in range(3)]
    if len(set(positions)) != 3:
        logger.error("❌ 验证码坐标重复,答案无效")
        valid = False
    return valid

def process_captcha(driver: WebDriver, wait: WebDriverWait) -> bool:
    """处理验证码(改递归为循环,提升健壮性,解耦全局变量)"""
    captcha_retry_count = 0
    ocr = ddddocr.DdddOcr(ocr=True, show_ad=False)
    det = ddddocr.DdddOcr(det=True, show_ad=False)

    while captcha_retry_count < CONFIG["max_captcha_retry"]:
        captcha_retry_count += 1
        logger.info(f"🔄 验证码处理第{captcha_retry_count}次尝试(最大{CONFIG['max_captcha_retry']}次)")
        try:
            # 下载验证码图片
            if not download_captcha_img(driver, wait):
                raise Exception("验证码图片下载失败")

            # 校验验证码有效性
            valid = True
            for i in range(3):
                sprite_path = os.path.join(CONFIG["temp_path"], f"sprite_{i+1}.jpg")
                with open(sprite_path, "rb") as f:
                    if ocr.classification(f.read()) in ["0", "1"]:
                        valid = False
                        break
            if not valid:
                raise Exception("验证码碎片无效")

            # 识别验证码
            captcha = cv2.imread(os.path.join(CONFIG["temp_path"], "captcha.jpg"))
            if captcha is None:
                raise Exception("验证码背景图读取失败")
            with open(os.path.join(CONFIG["temp_path"], "captcha.jpg"), "rb") as f:
                bboxes = det.detection(f.read())
            if not bboxes:
                raise Exception("未检测到验证码图案")

            # 匹配碎片与背景图
            result = {}
            for i, (x1, y1, x2, y2) in enumerate(bboxes):
                # 裁剪背景图中的图案
                cv2.imwrite(os.path.join(CONFIG["temp_path"], f"spec_{i+1}.jpg"), captcha[y1:y2, x1:x2])
                # 计算与每个碎片的相似度
                for j in range(3):
                    sim, _ = compute_similarity(
                        os.path.join(CONFIG["temp_path"], f"sprite_{j+1}.jpg"),
                        os.path.join(CONFIG["temp_path"], f"spec_{i+1}.jpg")
                    )
                    key_sim = f"sprite_{j+1}.similarity"
                    key_pos = f"sprite_{j+1}.position"
                    if sim > float(result.get(key_sim, 0)):
                        result[key_sim] = sim
                        result[key_pos] = f"{int((x1+x2)/2)},{int((y1+y2)/2)}"

            # 校验答案
            if not check_answer(result):
                raise Exception("验证码答案无效")

            # 打印匹配结果
            for i in range(3):
                pos = result[f"sprite_{i+1}.position"]
                sim = result[f"sprite_{i+1}.similarity"]
                x, y = pos.split(",")
                logger.info(f"🎯 图案 {i+1} 坐标({x},{y}),匹配率:{sim:.4f}")

            # 点击验证码图案
            slideBg = wait.until(EC.visibility_of_element_located((By.XPATH, '//*[@id="slideBg"]')))
            style = slideBg.get_attribute("style")
            width, height = float(get_width_from_style(style)), float(get_height_from_style(style))
            width_raw, height_raw = captcha.shape[1], captcha.shape[0]

            for i in range(3):
                pos = result[f"sprite_{i+1}.position"]
                x, y = map(int, pos.split(","))
                # 计算实际点击坐标(适配页面缩放)
                final_x = int(-width/2 + x/width_raw * width) + random.randint(-1, 1)
                final_y = int(-height/2 + y/height_raw * height) + random.randint(-1, 1)
                ActionChains(driver).move_to_element_with_offset(slideBg, final_x, final_y).click().perform()
                time.sleep(random.uniform(0.5, 1))

            # 提交验证码
            logger.info("📤 提交验证码")
            confirm = wait.until(EC.element_to_be_clickable((By.XPATH, '//*[@id="tcStatus"]/div[2]/div[2]/div/div')))
            confirm.click()
            time.sleep(5)

            # 校验验证码结果
            tc_operation = wait.until(EC.visibility_of_element_located((By.XPATH, '//*[@id="tcOperation"]')))
            if tc_operation.get_attribute("class") == "tc-opera pointer show-success":
                logger.info("✅ 验证码验证通过")
                return True
            else:
                raise Exception("验证码验证失败")

        except Exception as e:
            logger.error(f"❌ 验证码处理失败:{str(e)}")
            # 刷新验证码重试
            try:
                logger.error("⏳ 刷新验证码中,稍后重试……")
                reload = driver.find_element(By.XPATH, '//*[@id="reload"]')
                time.sleep(3)
                reload.click()
                time.sleep(min(3 * (2 ** (captcha_retry_count - 1)), 30))  # 指数退避重试间隔,上限30秒
            except NoSuchElementException:
                logger.error("❌ 验证码刷新按钮未找到,重试失败")
                return False

    logger.error(f"❌ 验证码重试{CONFIG['max_captcha_retry']}次仍失败,放弃")
    return False

def clean_temp():
    """清理青龙面板临时文件(增加容错)"""
    try:
        if os.path.exists(CONFIG["temp_path"]):
            for f in os.listdir(CONFIG["temp_path"]):
                file_path = os.path.join(CONFIG["temp_path"], f)
                try:
                    os.remove(file_path)
                except Exception as e:
                    logger.warning(f"⚠️ 删除临时文件{file_path}失败:{str(e)}")
            os.rmdir(CONFIG["temp_path"])
        logger.info("✅ 临时文件清理完成")
    except Exception as e:
        logger.warning(f"⚠️ 清理临时文件失败:{str(e)}")

def parse_accounts() -> List[List[str]]:
    """解析青龙面板RAINYUN_ACCOUNT环境变量"""
    account_str = os.getenv("RAINYUN_ACCOUNT")
    if not account_str:
        logger.error("❌ 未配置RAINYUN_ACCOUNT环境变量!格式应为[[账号1,密码1],[账号2,密码2]]")
        sys.exit(1)
    
    try:
        # 解析JSON格式的账号列表(兼容单引号/双引号)
        account_str = account_str.replace("'", "\"")  # 统一为双引号
        accounts = json.loads(account_str)
        # 校验格式
        if not isinstance(accounts, list):
            raise ValueError("环境变量值不是列表类型")
        for idx, account in enumerate(accounts):
            if not isinstance(account, list) or len(account) != 2:
                raise ValueError(f"第{idx+1}个账号格式错误,应为[账号,密码]")
            if not account[0] or not account[1]:
                raise ValueError(f"第{idx+1}个账号/密码为空")
        logger.info(f"✅ 成功解析{len(accounts)}个账号")
        return accounts
    except json.JSONDecodeError as e:
        logger.error(f"❌ RAINYUN_ACCOUNT格式解析失败:{str(e)},请检查格式是否为合法JSON")
        sys.exit(1)
    except ValueError as e:
        logger.error(f"❌ RAINYUN_ACCOUNT格式错误:{str(e)}")
        sys.exit(1)

def sign_in_rainyun(username: str, password: str):
    """单账号签到核心逻辑(独立封装,支持多账号循环调用)"""
    driver = None
    try:
        logger.info(f"\n========== 开始处理账号:{username} ==========")
        # 随机延时(可选)
        delay = random.randint(0, CONFIG["max_delay"])
        delay_sec = random.randint(0, 60)
        logger.info(f"⏳ 随机延时 {delay} 分钟 {delay_sec} 秒")
        time.sleep(delay * 60 + delay_sec)

        # 初始化Selenium(每次新建实例,清空缓存)
        driver = init_selenium()
        inject_stealth_js(driver)
        wait = WebDriverWait(driver, CONFIG["timeout"])

        # 访问登录页
        logger.info("⏳ 发起登录请求")
        logger.info("🌐 访问雨云登录页")
        driver.get(CONFIG["rainyun_login_url"])
        logger.info(f"页面标题:{driver.title}")

        # 输入账号密码
        logger.info("⏳ 等待登录表单元素加载...")
        username_elem = wait.until(EC.visibility_of_element_located((By.NAME, "login-field")))
        password_elem = wait.until(EC.visibility_of_element_located((By.NAME, "login-password")))
        login_btn = wait.until(EC.element_to_be_clickable((By.XPATH, '//*[@id="app"]/div[1]/div[1]/div/div[2]/fade/div/div/span/form/button')))
        logger.info("📝 输入账号密码")
        username_elem.send_keys(username)
        password_elem.send_keys(password)
        login_btn.click()
        logger.info("⏳ 正在登录中,耗时较长请稍等……")
        time.sleep(3)

        # 处理登录验证码
        try:
            wait.until(EC.visibility_of_element_located((By.ID, "tcaptcha_iframe_dy")))
            logger.warning("⚠️ 触发登录验证码")
            driver.switch_to.frame("tcaptcha_iframe_dy")
            if not process_captcha(driver, wait):
                raise Exception("登录验证码验证失败")
        except TimeoutException:
            logger.info("✅ 未触发登录验证码")

        # 校验登录状态
        time.sleep(5)
        driver.switch_to.default_content()
        logger.info(f"当前页面: {driver.current_url}")
        logger.info(f"页面标题: {driver.title}")
        if driver.current_url != "https://app.rainyun.com/dashboard":
            raise Exception("登录失败!请检查账号密码或网络")
        user_name = driver.find_element(By.XPATH, '//*[@id="app"]/div[1]/nav/div[1]/ul/div[6]/li/a/div/div/p').text.strip()
        logger.info(f"✅ 账号登录成功:{user_name}")
        
        # 访问签到页
        logger.info("🌐 访问赚取积分页")
        driver.get(CONFIG["rainyun_earn_url"])
        driver.implicitly_wait(5)
        logger.info(f"当前页面: {driver.current_url}")
        logger.info(f"页面标题: {driver.title}")

        # 查找并点击签到按钮
        logger.info("🔍 查找每日签到按钮")
        earn_btn_qddiv = driver.find_element(By.XPATH, '//*[@id="app"]/div[1]/div[3]/div[2]/div/div/div[2]/div[2]/div/div/div/div[1]/div')
        earn_btn_qd = earn_btn_qddiv.find_element(By.XPATH, './/span[contains(text(),"每日签到")]')
        status_elem = earn_btn_qd.find_element(By.XPATH, './following-sibling::span[1]')
        status_text = status_elem.text.strip()

        if status_text == "领取奖励":
            earn_btn = status_elem.find_element(By.XPATH, './a')
            logger.info(f"📌 签到状态:{status_text},开始领取")
            earn_btn.click()

            # 处理签到验证码
            logger.info("⚠️ 触发签到验证码")
            driver.switch_to.frame("tcaptcha_iframe_dy")
            if not process_captcha(driver, wait):
                raise Exception("签到验证码验证失败")
            driver.switch_to.default_content()

            # 校验签到结果
            time.sleep(5)
            logger.info("✅ 签到奖励领取成功")
        else:
            logger.info(f"📌 签到状态:{status_text},无需重复签到")

        # 获取当前积分
        try:
            points_elem = driver.find_element(By.XPATH, '//*[@id="app"]/div[1]/div[3]/div[2]/div/div/div[2]/div[1]/div[1]/div/p/div/h3')
            current_points = int(''.join(re.findall(r'\d+', points_elem.text)))
            logger.info(f"💰 当前积分:{current_points}(约{current_points/2000:.2f}元)")
        except Exception as e:
            logger.warning(f"⚠️ 积分获取失败:{str(e)}")

        

    except Exception as e:
        logger.error(f"❌ 账号{username}处理失败:{str(e)}", exc_info=True)
    finally:
        # 关闭浏览器,彻底清空缓存
        if driver:
            try:
                driver.quit()
                logger.info(f"✅ 账号{username}浏览器已关闭")
            except Exception as e:
                logger.warning(f"⚠️ 关闭浏览器失败:{str(e)}")
        # 清理临时文件
        clean_temp()
    logger.info(f"\n========== 账号{username}处理完成 ==========\n")

def main():
    """主函数:解析多账号,依次执行签到"""
    init_logger()
    # 解析账号列表
    accounts = parse_accounts()
    # 依次处理每个账号
    for idx, (username, password) in enumerate(accounts, 1):
        logger.info(f"\n================= 处理第{idx}个账号 ==================")
        sign_in_rainyun(username, password)
        # 账号间间隔(可选)
        time.sleep(random.uniform(2, 5))
    
    logger.info("\n🎉 所有账号处理完成!")

if __name__ == "__main__":
    main()

五、最后

后面的设置定时任务之类的就懒得写啦,顺便我也懒得写通知之类的了,感谢前人的开发让我站于巨人肩膀上,要是有后来者优化了就更好了

哦,对了,懒得上传 github 了,就丢 linux.do 里好了


📌 转载信息
原作者:
T_ACGN
转载时间:
2026/1/24 07:01:47

浏览了社区和 GitHub 上众多的 Surge 配置仓库,我发现一个普遍痛点:配置项冗余且混乱。很多早已废弃的参数、默认即可的开关、甚至相互冲突的规则被盲目复制粘贴,对于追求极致整洁的强迫症患者来说,阅读这些配置文件简直是一种折磨。

为此,我决定重构一份 “反熵增” 的配置清单。

核心原则:如无必要,勿增实体。

  1. 剔除默认值:如果 Surge 的默认行为已经合理,绝不显式写入配置文件。
  2. 拒绝无效参数:清理死代码和过时配置(如 VIF 模式下的 skip-proxy)。
  3. 仅保留关键:只体现需要根据国情额外配置的,或社区公认的最佳实践(Best Practice)。

基于以上原则,我整理了以下配置。希望它能成为一份逻辑自洽、甚至赏心悦目的基准配置。欢迎大家从原理层面进行 “代码审查” 和讨论,我们将持续迭代更新。

[General] 核心设置

[General]
# --- 连接稳定性与测试 ---
# 开启 Wi-Fi 助理:当 Wi-Fi 信号极差或无法联网时,自动使用蜂窝数据
wifi-assist = true
# 连通性测试:用于检测是否具备互联网访问能力
internet-test-url = http://wifi.vivo.com.cn/generate_204
# 代理测速:用于测试代理节点的延迟基准
proxy-test-url = http://cp.cloudflare.com/generate_204

# --- 物理网络旁路 (核心优化) ---
# 作用:流量不经过 Surge 虚拟网卡,直接由物理网卡处理 (VIF 模式下唯一有效的绕过方式)
# 1. 192/10/172: 解决局域网传输 (NAS) 发热、跑不满带宽的问题;确保公共 Wi-Fi 认证页面正常弹出
# 2. 100.64: 解决 Tailscale/ZeroTier 组网连接失败、运营商内网服务异常
# 3. 224/239/255: 解决 AirPlay 投屏找不到设备、智能家居 SSDP 发现问题
tun-excluded-routes = 192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12, 100.64.0.0/10, 239.255.255.250/32, 224.0.0.0/24, 255.255.255.255/32

# --- DNS 解析 ---
# 策略:阿里(223) + 腾讯(119) 提供高可用解析,System 作兜底
# 注意:保留 system 是为了在公共 Wi-Fi 未认证(外网不通)时,能获取路由器下发的内网 IP 以弹出登录页
dns-server = 223.5.5.5, 119.29.29.29, system
# 劫持转发:强制接管设备内所有发往 53 端口的 DNS 查询 (防止 App 自定义 DNS 导致分流失效)
hijack-dns = *:53

按需选配:

# ============================================
# Optional / Advanced Settings (按需启用)
# ============================================

# --- 远程控制器 (HTTP API) ---
# 允许通过 HTTP API 控制 Surge (如 Yacd 面板 / 快捷指令)
# http-api = pass@127.0.0.1:6171
# http-api-tls = false
# http-api-web-dashboard = false

# --- 局域网访问 (Wi-Fi/热点共享) ---
# 允许局域网内其他设备连接本机的代理服务
# allow-wifi-access = false
# allow-hotspot-access = true
# 代理服务监听端口 (默认 HTTP:6152, SOCKS5:6153)
# wifi-access-http-port = 6152
# wifi-access-socks5-port = 6153

# --- 高级网络行为 ---
# 隐藏状态栏 VPN 图标 
# hide-vpn-icon = false
# 排除简单主机名 (不包含点的域名) 走代理,通常用于让内部域名强制直连
# exclude-simple-hostnames = true
# IPv6 支持 (默认关闭,国内环境建议保持关闭以提升兼容性)
# ipv6 = false
# 当遇到 REJECT 策略时,是否显示网页报错页面 (默认为直接断开)
# show-error-page-for-reject = true
# UDP 转发失败时的回退行为 (默认为 REJECT,可选 DIRECT)
# udp-policy-not-supported-behaviour = REJECT

# --- DNS 与数据源 ---
# 加密 DNS (DoH/DoQ): 除非有抗污染强需求,否则不建议开启,会增加延迟
# encrypted-dns-server = https://223.5.5.5/dns-query, quic://dns.alidns.com
# 自定义 GeoIP 数据库源 (仅当默认数据库不准时使用)
# geoip-maxmind-url = https://raw.githubusercontent.com/Loyalsoldier/geoip/release/Country.mmdb
# 从 /etc/hosts 读取 DNS 记录 (仅 macOS)
# read-etc-hosts = true

# --- 遗留与替代项 (不推荐使用) ---
# [VIF Mode 无效] 即使配置也被忽略,物理直连请使用 tun-excluded-routes
# skip-proxy = 192.168.0.0/24, 10.0.0.0/8, 172.16.0.0/12, *.local, localhost

# [建议使用模块] 强制返回真实 IP (解决游戏 NAT 类型或去 IP 校验)
# 推荐使用 Surge 模块或 Rule 进行管理,保持主配置简洁
# always-real-ip = *.srv.nintendo.net, *.stun.playstation.net, xbox.*.microsoft.com

未完待续…


📌 转载信息
转载时间:
2026/1/24 07:00:09

之前一直当作一个把网页保存到本地的剪辑工具
今天配置了一下里面的 AI 感觉用起来还不错推荐给大家

1 chrome 插件下载地址

2. 使用 AI 功能 对网页内容进行总结或者打标签

  1. 配置模型供应商 这里使用了薄荷佬的公益站

配置模型

  1. 新建一个模版

  2. 配置属性

tags 这里配置的是 可以自己写自己需要的提示词

{{"Analyze the article and create up to 5 tags in comma-separated format. Tags should be in Chinese unless necessary for company names, person names or abbreviations. One tag must be selected from: "资源, AI编程, AI画图 , 工具, 产品". Other tags should be based on the article's topic, mentioned people, companies or products."}}

描述

{{"One-sentence summary of the article content,translated to Chinese"}}
  1. 然后可以把模版进行导入导出

刚刚的模版复制出的 json 直接导入 就可以快速编辑了

{
  "schemaVersion": "0.1.0",
  "name": "剪藏模板",
  "behavior": "create",
  "noteContentFormat": "{{content}}",
  "properties": [
    {
      "name": "标题",
      "value": "{{title}}",
      "type": "text"
    },
    {
      "name": "来源",
      "value": "{{url}} ",
      "type": "text"
    },
    {
      "name": "创建时间",
      "value": "{{date}}",
      "type": "text"
    },
    {
      "name": "tags",
      "value": "{{\\\"Analyze the article and create up to 5 tags in comma-separated format. Tags should be in Chinese unless necessary for company names, person names or abbreviations. One tag must be selected from: \\\"UI设计, AI, 编程, 经济, 效率, 产品\\\". Other tags should be based on the article's topic, mentioned people, companies or products.\\\"}}",
      "type": "multitext"
    },
    {
      "name": "简单描述",
      "value": "{{\\\"One-sentence summary of the article content,translated to Chinese\\\"}}",
      "type": "text"
    }
  ],
  "triggers": [],
  "noteNameFormat": "{{title}}",
  "path": "para/Clippings"
}

  1. 其余属性自行探索,有小伙伴做出好用的模版 可以导出来进行交流

📌 转载信息
原作者:
mocheng
转载时间:
2026/1/24 06:57:22

最近把代理服务器 IP 换了下,从 192.168.0.13 换成了 192.168.0.170,在远端 Linux 上用 Codex CLI 走新服务器的 HTTP 代理,遇到报错:

stream disconnected before completion: error sending request for url (https://chatgpt.com/backend-api/codex/responses)

export RUST_LOG=trace 打开 trace 级别日志后,查看 ~/.codex/log/codex-tui.log,发现它竟然去连旧代理 192.168.0.13:7890,并报 Host is unreachable

最诡异的是:我检查运行中的 codex 进程环境变量,里面完全看不到旧代理。
这里我采用了原来帖子里处理 codex VSCode 插件的方法直接添加了代理所以看不到原来的旧代理:

#!/usr/bin/env bash set -euo pipefail

unset http_proxy https_proxy all_proxy no_proxy
unset HTTP_PROXY HTTPS_PROXY ALL_PROXY NO_PROXY

PROXY="http://192.168.0.170:7890" export http_proxy="$PROXY" export https_proxy="$PROXY" export all_proxy="$PROXY" export HTTP_PROXY="$PROXY" export HTTPS_PROXY="$PROXY" export ALL_PROXY="$PROXY" export NO_PROXY="localhost,127.0.0.1,::1" export no_proxy="$NO_PROXY" # 用于确认 VS Code/CLI 实际有没有跑到这个 wrapper env | grep -i _proxy > "/tmp/codex-proxy-env.$$.txt"

HERE="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" exec "$HERE/codex.real" "$@" 

1)我怎么确认 “进程 environ 里没有旧代理”

我用下面命令抓 codex 相关进程的环境变量:

查看 codex 进程对应的环境变量

for p in $(pgrep -af codex | awk '{print $1}'); do echo "=== PID $p exe=$(readlink -f /proc/$p/exe 2>/dev/null) ===" tr '\0' '\n' < /proc/$p/environ 2>/dev/null | grep -iE '(^|_)proxy=' || true done 

输出看起来代理都是统一的(只有新代理 192.168.0.170):

=== PID 1703340 exe=/home/user/.vscode-server/extensions/openai.chatgpt-0.4.62-linux-arm64/bin/linux-aarch64/codex.real ===
HTTPS_PROXY=http://192.168.0.170:7890
HTTP_PROXY=http://192.168.0.170:7890
=== PID 1713620 exe=/home/user/.local/node/bin/node ===
no_proxy=localhost,127.0.0.1,::1
https_proxy=http://192.168.0.170:7890
NO_PROXY=localhost,127.0.0.1,::1
HTTPS_PROXY=http://192.168.0.170:7890
HTTP_PROXY=http://192.168.0.170:7890
http_proxy=http://192.168.0.170:7890
ALL_PROXY=http://192.168.0.170:7890
all_proxy=http://192.168.0.170:7890
=== PID 1713631 exe=/home/user/.local/node/lib/node_modules/@openai/codex/vendor/aarch64-unknown-linux-musl/codex/codex ===
no_proxy=localhost,127.0.0.1,::1
https_proxy=http://192.168.0.170:7890
NO_PROXY=localhost,127.0.0.1,::1
HTTPS_PROXY=http://192.168.0.170:7890
HTTP_PROXY=http://192.168.0.170:7890
http_proxy=http://192.168.0.170:7890
ALL_PROXY=http://192.168.0.170:7890
all_proxy=http://192.168.0.170:7890

2) 但 trace 日志里仍然出现旧代理

我开启 trace:

追踪 codex 执行的日志

export RUST_LOG=trace
codex

然后在~/.codex/log/codex-tui.log 里看到它尝试连接旧代理 192.168.0.13:7890(典型是 reqwest /hyper 的 connect error)。

3) 最终根因:~/.codex/.env 里残留了旧代理

看了下常规的~/.bashrc , /etc/bash.bashrc 都没有老代理的影子,使用才最残暴的 sudo grep -nri 192.168.0.13 /* 暴力搜索所有文件夹下包含老代理的 IP。最后发现我以前在~/.codex/.env 里写过旧的 http_proxy/https_proxy,后来忘了删。。。。删除后,问题解决。

4) 经验总结

只看 /proc/$PID/environ 可能会被误导:即便进程环境里已经是新代理,codex-tui.log 仍可能暴露 “.env 残留的旧代理”。这时候一定要善用 export RUST_LOG=trace 来观察 codex 运行日志。


📌 转载信息
原作者:
LeBronGanDalf
转载时间:
2026/1/24 06:56:01

由于我有读 PDF 的需求,所以做了一个 AI 定位器,可以聊天和定位 PDF 内容

配置自己的 api key 到这个网站

免费使用打开这个网站

GitHub (求个 Star):


📌 转载信息
原作者:
yeahhe
转载时间:
2026/1/24 06:55:23

之前一直当作一个把网页保存到本地的剪辑工具
今天配置了一下里面的 AI 感觉用起来还不错推荐给大家

1 chrome 插件下载地址

2. 使用 AI 功能 对网页内容进行总结或者打标签

  1. 配置模型供应商 这里使用了薄荷佬的公益站

配置模型

  1. 新建一个模版

  2. 配置属性

tags 这里配置的是 可以自己写自己需要的提示词

{{"Analyze the article and create up to 5 tags in comma-separated format. Tags should be in Chinese unless necessary for company names, person names or abbreviations. One tag must be selected from: "资源, AI编程, AI画图 , 工具, 产品". Other tags should be based on the article's topic, mentioned people, companies or products."}}

描述

{{"One-sentence summary of the article content,translated to Chinese"}}
  1. 然后可以把模版进行导入导出

刚刚的模版复制出的 json 直接导入 就可以快速编辑了

{
  "schemaVersion": "0.1.0",
  "name": "剪藏模板",
  "behavior": "create",
  "noteContentFormat": "{{content}}",
  "properties": [
    {
      "name": "标题",
      "value": "{{title}}",
      "type": "text"
    },
    {
      "name": "来源",
      "value": "{{url}} ",
      "type": "text"
    },
    {
      "name": "创建时间",
      "value": "{{date}}",
      "type": "text"
    },
    {
      "name": "tags",
      "value": "{{\\\"Analyze the article and create up to 5 tags in comma-separated format. Tags should be in Chinese unless necessary for company names, person names or abbreviations. One tag must be selected from: \\\"UI设计, AI, 编程, 经济, 效率, 产品\\\". Other tags should be based on the article's topic, mentioned people, companies or products.\\\"}}",
      "type": "multitext"
    },
    {
      "name": "简单描述",
      "value": "{{\\\"One-sentence summary of the article content,translated to Chinese\\\"}}",
      "type": "text"
    }
  ],
  "triggers": [],
  "noteNameFormat": "{{title}}",
  "path": "para/Clippings"
}

  1. 其余属性自行探索,有小伙伴做出好用的模版 可以导出来进行交流

📌 转载信息
原作者:
mocheng
转载时间:
2026/1/24 06:54:31

头回自己碰见,哥们你知道吗。
用着用着 gemini 抽风了,这你受得了吗。
抽风了大概 10 分钟,消息被吞了。
抽风的原因疑是是聊天记录太多了。
抽风前聊天记录全给我加载了然后每次切换窗口都给我跳到第一个对话,不过没想到最后这么癫。

为了不漏信息,所以只放抽风的截图。
image

某网站的求助帖为例:

提问

Comparing IFRS Accounting Standards and U.S. GAAP: Bridging the Differences (September 2025) | DART – Deloitte Accounting Research Tool

网页内嵌的 pdf,请帮忙下载,因为该网站有好多类似的文章,还请介绍如何突破限制,下载该网站的其他类似内容

下载链接

f12,定位到 pdf 渲染层(页面嵌套)


执行 PDFViewerApplication.download() 即可(基本上通用)

以下是 pdf 文件链接的下载链接

https://dart.deloitte.com/USDART/ov-resource/057be505-289e-11e9-818d-67c85cbcc7fa#pdfjs.action=download

(下是应该能下,但是太慢啦)

pdf.js 应该是通用的,除非服务器做了限制(也有办法绕过然后保存)

找到密码

使用特殊(对于 L 站为准入门槛)网咯下载后,发现有密码。
先说该网站结论:

  1. 找到 id 为 r0inab 的 script 标签的内容,
    例如:Business Combinations (November 2025) | DART – Deloitte Accounting Research Tool ,(没错我另起炉灶了)
    pdf.js 通用 pdf 下载教程3
    搜索得到加密后的密钥:2s1e1k2p1d1j2t2p1e1g1d1h172u1g1j2p171k2t1d1d172s1h2p2u171f2u2q1c1g1f2u1j

  2. 计算密钥

    function decode(encodedString) {
        let result = '';
        for (let o = 1; o < encodedString.length; o++) {
            if (o % 2 === 1) {
                const charCode = parseInt(encodedString[o-1] + encodedString[o], 36);
                result += String.fromCharCode(charCode);
            }
        }
        return result;
    }
    
    const encoded = "2s1e1k2p1d1j2t2p1e1g1d1h172u1g1j2p171k2t1d1d172s1h2p2u171f2u2q1c1g1f2u1j";
    console.log(decode(encoded));
    

    得到密码:d28a17ea2415+f47a+8e11+d5af+3fb043f7

  3. 即可解开啦

PS: 如果希望去除密码,最简单的方法就是在线 pdf 解锁网站,比如 https://www.ilovepdf.com/zh-cn/unlock_pdf

还有就是要多换几个软件试试,可能是软件 bug,chrome 和 2345pdf 阅读器说密码不对,但是 edge、福昕 pdf 编辑器、上面那个 pdf 解锁网站则可以正确打开。。。

追码过程(通用)

pdf.js 中搜索 .onPassword


如果手速不够快可能需要刷新,
如果存在密码的话可能会在此处断下,并且报错 No password given
如图中所示,此处的 e.onPassword 则是自动传输密码的必经之路。
如果偷懒,可以在 e.onPassword 的第一回调参数(也就是上面的 s 函数)下断点,

即可直接偷鸡。

当然,本例追过去,发现

onPassword = (e, t) => {
    this.isViewerEmbedded && this._unblockDocumentLoadEvent(),
    window.addEventListener('message', function t(i) {
        window.removeEventListener('message', t)
        for (var n = i.data, s = '', o = 1; o < n.length; o++)
            o % 2 && (s += String.fromCharCode(parseInt(n[o - 1] + n[o], 36)))
        e(s)
    }),
    window.parent.postMessage('s', location.href)
}

然后追到 s 的监听换事件

//外层 function onMessage(e) {
e.origin === location.protocol + "//" + location.host && (e.data === String.fromCharCode(115) ? e.source.postMessage(JSON.parse(document.getElementById("r0inab").innerText), location.href) : e.data === String.fromCharCode(103) && e.source.postMessage(JSON.parse(document.getElementById("r0inyk").innerText), location.href));
}

最终算法参考本文 [找到密码] 部分

↓能不能别点赞,点点别的, 不想吃 LDC 低保


📌 转载信息
原作者:
taozhiyu
转载时间:
2026/1/24 06:54:06

花费了半年时间开发的 AI 量化平台正式开源了,采用了 Apache2.0 协议,可商用。
全平台支持、美股、A 股、港股、加密、外汇、期货
TV 的平替,能够 AI 写策略、运行指标到 K 线。同时支持多用户版本,本地 Docker 一键部署
Dome 演示地址:https://ai.quantdinger.com
github 地址:GitHub - brokermr810/QuantDinger: AI-driven, local-first quantitative trading platform for research, backtesting and live execution. Python-native, privacy-first, open source.
如果觉得好用,请大佬赏赐一个 star, 您的支持是我们进步的动力。同时招募志同道合的项目管理员,管理仓库,共建开源社区!


📌 转载信息
转载时间:
2026/1/24 06:46:07

简介

使用 Nuxt 4.2.2 + tauri 2 开发,通过 API 接口获取寻访记录,仓库新建,功能还在逐步完善中。

目前暂时仅支持 Windows 端,数据都在程序同级目录下的 userData 目录中。

欢迎佬友们试试看~也欢迎大家晒晒自己的抽卡记录(当然能给仓库点个 Star 就更好了 hhh

附一张自己的:(还有比我更非的吗)

仓库链接:


📌 转载信息
原作者:
Sakikoo
转载时间:
2026/1/24 06:46:04