本文转载自我自己的个人博客文章,同时也欢迎参观我的个人网站
一字一句均手打,7k 字不易(去掉 md 语法之前统计出来快有 8k 字了)


首页链接

开始之前,先放链接

随着改名(一会会提到),链接有一点变动,但也顺便增加了一些域名,哪个都行

增加了 Cloudflare Pages,更改了 netlify 和 vercel 的域名(旧的也能访问,会重定向)

前言

长文警告!纯手打不易!一定要看到后面!后面更重要

距离上次写它的博客已经 10 个月左右(300 多天)了

(看了一眼博客,上一篇是 2025.2.22)

所以这次我想在开始之前先聊一聊,聊点东西

这次的重点是最后一个哦~请耐心看完~

关于这个东西

WebTooys(Quicktools)这个东西,算是我前端学习路上的一个记录了,从我初学前端的 TODO 到现在的 React,直接的说就是我的成长历程

可以看到,我刚学没多久就解除了 Web Component 组件库了,这也深深影响着我

我对它的定义是,一个纯前端静态网页工具集,不需要打包构建的那种,不过后面这一祖宗之法从现在开始…… 动摇了,后面你就知道了

它还会继续更新的!

关于第__弹

每到发博客的时候,都会写上第几弹这样的东西

其实对于项目本身来说并不存在这种说法,这只是博客的说法罢了

每到一个大项目(开了单独的 Github 仓库的)我就会发一弹

由于上次值更新了两个项目,就不 + 1 了,+0.1 了(其实一开始是 + 0.5 的,但是我后来改了)

改名

这次有一个很大的变动 —— 改名!

Quicktools → WebTooys

原因

原本这个名字是我随便取的,简单易懂

可正是因为如此,重名率 101.0000% × 114514 啊

我刚刚在 bing 搜了一下,第一页 quicktools.* 域名的就有 4 个,其他的也都是写着自己是 Quicktools 的

我觉得,这不行!我要一个新的名字

我回想了一下,所谓 Tools—— 工具,我做到了吗?最初我是这么想的,但是它逐渐变了

它现在还是有着不少 Tools 的,但是也有不少 Toys,实用性几乎没有,娱乐性倒是有

我一拍脑袋!就叫 Tooys 吧!融合了这两个单词,玩具工具都是具

感觉有点简单,稍微补一下

所以新的名字就是:

WebTooys

可是啊,我看了我还是放不下 Quicktools 这个称呼,也懒得改 Github 仓库名,这一改我还得跑到 Netlify 和 Vercel 那边去……

所以,全称就叫做 WebTooys Quicktools,简称 Tooys / WT

以前还简称 QT 呢

Netlify 域名

前面说了,改了名也改了域名

Vercel 好改,允许多个域名 *.vercel.app 同时存在(我设置了 301 重定向),可是 Netlify 就不行了

我想改,但是改不了,改了旧的就没了,可我最常用的就是旧的 Netlify 域名啊,一改就失效了

那就……

创建一个新的叫做 ftz-tools 的项目!

我直接利用 Netlify Drop 上传这个东西(Github Gist)

这里有一个 netlify.toml,直接 301 重定向到新站

可是 Drop 要求我必须要有一个 index.html,那我就快速写了一个多重自动重定向的网页(其实应该够不着这一步的)(用了 http-equiv="refresh"window.location.href<a>

完美解决!

AI

以前在这个项目中使用 AI 要么是 AI 辅助开发、要么是直接 vibe,但是就是没有 AI 为中心的

现在有了

前面的项目

开始说第三弹之前,我想说说第二弹和第 2.1 弹的两个大项目

Taple

这是我写了一个学期的玩意,那时还没有双休(现在也没有)

写完之后还有不少修修补补,增加了 AI 功能,增加了缩放功能,算是我坚持最久的一个项目了吧

UpDyn

一个批量查看 B 站 UP 主动态的工具,但是没过多久这个破站的风控加强了

用不了了!最后当我找到一个能用的第三方库的时候,我本地能跑通,部署到服务器上就寄了,大概是因为服务器在国外吧

总之 —— 蒙古上单.jpg(█ ███████)

首页重构

其实发了上一弹博客没多久,我就重构界面了

最初的版本长这样:(用的是 mdui1 组件库)(翻 git 第一次提交翻出来的)

其实 mdui2 的表格不作为一个组件存在

material web 进维护模式好几年了你怎么还不活啊

为啥新版本涟漪不放到 Material 3 官方文档上(mdui 遵循官方文档用的是旧涟漪)

后来我觉得太丑了,于是用 mdui2 的卡片重做了一个简单的网页,没有 js,增加项目直接改 html 就行了

(图片自己翻)

而在新的版本,长的差不多,但其实是重构了的,我使用 js 生成组件的方式显示项目

为了方便管理,我把项目配置独立到了 link.js 里面,不用 links.json 的原因是我不想要页面加载完了之后还得等 fetch,同时也不用被 json 严格的格式困扰

这么做的好处就是,我可以添加筛选分类排序功能了!

此外藏了一个小彩蛋(不要作弊哦,看源码全都看光光了,我可没藏多深,不看源码往这恶彩蛋才有意思)

sitemap 和 umami

我想让它上 Bing Webmaster 和 Google Search Console,所以我需要一个 sitemap,一开始我想要写一个脚本直接生成的,后来想了一下,换成手写了,这样可控性高

然后也添加了 umami,分享链接


介绍

已经写了好多字

该开始写每个项目的介绍了~

按时间顺序来吧

AB 追更器

我从小学就开始追更 Alan Becker 的动画了,几年以来技术一直在提升,一度期盼更新,可惜现在生活上有着各种事情,逐渐地在意得少了,当初的热情也消退了许多,但这并不妨碍我依旧热爱

没看过的赶紧给我去看

我学前端之前,我用 python 做了一个用 PythonAnywhere+Flask+API 的查看视频列表的网页,还用的是 MDBootstrap

而我光速重写

现在那个网页已经变成 api 转发了,现在是用 mdui 做的界面,请求 PythonAnywhere 后端,这样不用工具也能看

图片用了 wsrv.nl 反代 Youtube 图片,国内网络可以访问

直播和首播都会被算进视频列表 API 的,所以不用担心看不上首播 / 直播了

关于旧的那个,看我以前的一篇博客

asak viewer

asak 是我以前写的一个库,支持 py 和 js,写过一篇博客

就我自己而言,作用不怎么大

不过我通过这种方式扩展了 asak.json 的作用

把自己的各种 llm api 收集起来,导入到 viewer 里面,就能够搜索和复制了,方便快速选取自己的多个 api 多个模型

同时还提供了一个测试 api 的功能,直接把参数填进去就能测试这个 api 这个 key 这个模型是否可用,只要没有 CORS

其他没什么好说的,过

AI 记事本

这个东西我发过一个 L 站帖子和一个 B 站视频,就懒得介绍了

但是我想谈谈我的想法,我直接引用帖子里的话吧

原本就是想这样做一个记事本的,想着这样打字都不用考虑选词了,一顿狂敲 ai 自动帮我整理好,输入法什么的都免了
想象很美好现实很骨感
等以后小模型进步了,输出又快又好,这玩意绝对好用(

平常打字的时候,时不时会遇上打错字(拼音同音字),一旦打字打快了打爽了一不小心就漏掉了,说不定你能在这篇文章里面找到一点点错别字,那么我们可以从输入法动手,直接 ai 纠错,这就是我的想法

当然,技术还不够成熟

后面我会写一篇博客,专门来写我的一些想法,或是完成不了,或是我没技术,或是世界的技术还不够,或是我没时间写,或是我后来又觉得不行……

Copic

你只需要按下 PrtSc 键把截图复制到剪贴板,然后打开这个网页按下 ctrl+v,图片就保存了

这个网页不依赖任何第三方资源,你可以按 ctrl+s 把这单个 html 保存下来离线使用,我是用 GLM-4.6 临时搓的,顺手放上去了

其实我自己并不需要用这个东西,我之前写的 LifeLog 电脑端就顺手集成了截图功能,其实我是在整大活,短时间内可能没能整得完,留个悬念

你跟截图过不去了是吧?之前写的第一个 elecron 应用CapsTools 第一个集成的就是截图

FakeNET

重点来了!压轴项目!

访问链接(一会解释)

  • /builts/fakenet/index.html

  • /builts/fakenet/index_jd.html

  • /builts/fakenet/index_jdm.html

简单介绍一下吧

这个在 Github README 已经写了很多,再结合视频,我觉得我不需要讲什么了

先说一下,虽然我提到了 Sora,但是其实这个想法的产生是在 Sora2 发布之前的,在我被另一个项目的 bug 硬控到柳暗花明又一柳暗花明的时候 Sora2 发布了,当我放弃了那个项目之后我才开始制作这个东西

简单地说,这个东西就是让你填入 CloseAI 格式的 API,然后得到一个 伪・文 章 平 台

由 AI 生成文章推送,点击文章之后会由 AI 生成文章内容,等文章内容生成完毕之后生成评论,当你下一次点击刷新按钮的时候,AI 会先根据你看没看过的文章、点不点赞文章评论来分析,修改你的偏好列表,这个偏好列表会应用到全部请求

我觉得效果不算好,可能是模型的原因,也可能是提示词的原因,不过你可以改系统提示词,只要 AI 输出一样,如果可以的话,有没有大佬贡献一个更好的提示词

界面用了 Fluent UI React Component,好看吧,这是 WebTooys (Quicktools) 第一次上需要构建的项目,祖宗之法可变!后面还会有别的

开发历程

按照惯例,讲一讲开发历程吧,分享一下开发过程中的一些事情

久久久

开发这玩意用了我两个多月,靠着每周单休去写的,期间还有几次游戏更新,再加上每周各种各样的事,少的一周就写了大概半小时……

砍砍砍

一开始构想的时候,我想这是一个虚假的社交网络平台,分成三个分区:

  • 现在你看到的文章分区
  • 类似于推特的帖文分区
  • 类似于小红书的图文分区

而且一开始我的构想还有个用 AI 给内容生成图片的环节,这些我都懒得搞了

本来想要在模型配置里面做一个全局模型,然没每个模型配置可以选择是否使用全局模型,配置的时候会省事一点,但是开发起来略微有点复杂我懒得搞

bun

开始做之前刷到了 bun 这个玩意,感觉有点意思,那这次就用 bun 写一下吧

感觉比 vite 省事了一点点,但不多,不过我主管觉得它打包速度挺快的

当然,我这里没有用到后端,下次玩玩 bun 的哪些后端 api

提示词的引用方式

写了一堆提示词,一开始我用 md 文件存起来然后尝试用 require 引入这些文件的

但是 bun 在开发服务器里面得到的是_bun/xxx 这样子的字符串,没把它当成文本,它也不支持 require('xxx?raw')(直接报错),这至少对我调试很不利

我放弃了,于是创建了一个文件

export default {
    "zh-CN": {
        title: `...`,
        //...
    },
    "en": {
        //...
    }
};

直接解决,但是太不优雅了

没过多久,AppStore 代码泄露,我在 tg 找到了源码观摩观摩,用的是 Svelte,呃…… 我没玩过……

但是我发现有一点!我想要打开 svg 文件看看,结果预览不了,切换到文本查看我就恍然大悟了

export default "data:image/svg+xml,%3csvg%20viewBox='0%200%2070.76...ntColor'%3e%3c/path%3e%3c/svg%3e" 

你给我干哪来了?这还是 svg 吗

于是我就修改了一下,一个 ts 文件一个提示词,每个文件都是 module.export.default = ...

我可不会干这种在 md 文件写 js 的神秘事情

i18n

我想,这次玩一下语言切换吧,使用 react-i18n

可是我在编辑器没有提示没有预览有点难受,然后我找到了 i18n-ally这个 VSCode 插件,挺好用的

最后用 AI 把 zh-CN.json 翻译成了 en.json

但是在写的过程中,其实很多地方都用了三目运算符去处理,尤其是拼接用户提示词的时候

那我为什么不都用三目运算符呢,反正是 react 处理起来也省事

state 深度

我 React 写的不多,就犯了一个错误:一个组件的 state 更改之后用了 useEffect 把 state 同步到另一个组件的 state,这样就触发了 state 深度限制,React 直接崩掉

Markdown 方案更改

一开始写 Markdown 显示的部分我是用了我之前写的一个水项目LMCanvas 里面的代码的,还使用了 styled component 注入 css(整个项目没创建过一个 css 文件,因为我不想)

但是这个玩意似乎不够好,想了想算了,换了一个方案,支持更丰富的 Markdown 语法,让 AI 更能够发挥富文本能力,那就是 ——GFM (GitHub Flavored Markdown)

import 'github-markdown-css/github-markdown.css';
//... <ReactMarkdown remarkPlugins={[remarkGfm, remarkMath]}
rehypePlugins={[[rehypeKatex, { throwOnError: false }]]}
components={components}
>

但是这个背景色和文本颜色不符,其实很简单就能解决:{backgroundColor: 'transparent',color: 'inherit',}

顺手还加上了 Katexreact-syntax-highlighter(vscDarkPlus 主题)

九分甚至有十分完善地显示了 Markdown

CVE

就在我做着做着,突然! 两个 10 级的漏洞掉了下来

但其实我这个纯前端项目应该不受什么影响的~~,再怎么样顶多让 Vercel 抗一下~~,但我还是升级了,安心一点

闭包

一直以来闭包相关的事情我都是凭直觉写的,顶多怀疑一下也不会出什么错,但是写 React 就得注意一下,这个 State 跟我的直觉不太一样,得稍加注意一下

这次我偷偷利用了一下闭包,拿没刷新的闭包旧值和用 useEffectEvent 拿到的新值进行比较

结果因为太快了组件没刷新有的情况下 useEffectEvent 拿到的也是旧值

部署 cf pages

cf 更新了新的 ui,点击创建一看就不一样了

我直接点击了 Continue with Github,一路过去,失败了,提示构建失败

欸?我这样一不用构建啊怎么就失败了

我回来这个界面看了一下” 创建 Worker“?,左边怎么就只有 Worker 没有 Pages 这选项啊

再看看右边” 选择一种方法 “?这啥?

说实话这个界面挺好看的,但是我看了半天我才看到面板下面有一个小小的 "Looking to deploy Pages? Get started"

我寻思着这上一个界面叫 Workers 和 Pages,这两玩意算是同级了吧,怎么 Pages 按钮塞这么小,欺负我眼瞎?

点了一下,马上回到了旧 ui,体验有点割裂

不过也是顺利创建了 CF Pages 版本的部署

我的有点大,你要忍一下

其实有 CF Pages 在我完全不用担心带宽问题

Netlify 和 Vercel 各 100GB 也不用担心好吧大哥

其实根本没那么多人访问这个破站

但是我想出了一个馊主意 ——jsDeliver

这服务好哇,能狗直接拿 Github 上面的文件使用 cdn 引入

简单改一下就是 index_jd.htmlindex_jdm.html

何意味


尾声

不知道说什么,希望大家多多支持,我后面还会写的

不知不觉怎么写了怎么多字啊


📌 转载信息
转载时间:
2026/1/3 15:03:30

没把我累疯,从今天早上 7 点半一直分析到刚刚,终于把 Kiro 协议注册机给 done 了

看到 佬的帖子 我想自己试试看

不过还是有点小毛病的,指纹生成有大问题,由于时间紧张我是混合了 BrowserForge 和我自己的垃圾指纹,勉强可以过 send otp, 但是会被极速封号,下周再完善一下全部改为 BrowserForge

还有个小问题就是 initizaloAUTH 返回的是个跳转链接,但是它是通过网页动态跳转的而不是 302 到 workflowHandle, 跳转先用 playwright 跳了(反正不是啥重要的,也没有指纹)下周再分析一下 main sso 的跳转逻辑

加密方面,twmic 的算法不难,然后密码那一点还有个加密

元旦收获好大,写了 OpenRouter,Groq,Ollama,Gumloop,ZAI,Qwen 的协议注册机 (Aliyun Captcha 我给他旁路了,100% solve 滑块,类似 turnstile solver)

Cerebras 和 Zencoder 只能浏览器了,recaptcha score 达不到 我准备自己写一个本地的 V2 解决器


📌 转载信息
转载时间:
2026/1/3 15:02:46

起因是 CC 读大项目的时候,幻觉严重,特别是对于需要大量上下文的复杂流程理解错误,所以基于 AST 做了一个工具来提取函数签名,以及调用关系,能极大压缩上下文的情况下让 CC 完整理解调用链路。

欢迎佬友试用,轻喷,当然能给个 star 就更好了


📌 转载信息
原作者:
didnhdj
转载时间:
2026/1/3 15:02:30

转载自:10 张免费 eSIM 狠狠塞满你的卡槽! | 玖玖小博客

安装流程

iPhone:设置 → 蜂窝网络 → 添加 eSIM → 扫描二维码

Android:设置 → 网络与互联网 → 添加 eSIM → 扫描二维码

如果扫码有困难,也可以选择手动输入激活码的方式。安装完成后,建议先在 WiFi 环境下进行测试,确保 eSIM 能够正常激活。

以下直接分享按照的 LPA 码!你也可以转换为二维码安装,纯分享,无任何 AFF

  1. Speedtest Travel

    • 特点:由知名的 Speedtest 团队推出,性能和网速有保障

    • 覆盖:全球 140 + 个国家

    • 价格:免费体验,按天计费

    • 适用:对网速要求较高的用户

LPA:1$rsp.truphone.com$QRF-SPEEDTEST-PMRDGIR2EARDCIT5

  1. BetterRoaming

    • 特点:老牌服务商,用户口碑好

    • 覆盖:全球 150 + 个国家和地区

    • 价格:按流量计费,透明清晰

    • 适用:经常出国的商务人士

LPA:1$rsp.truphone.com$QRF-BETTERROAMING-PMRDGIR2EARDEIT5

  1. Heinemann

    • 特点:知名免税店集团推出的 eSIM 服务

    • 覆盖:欧洲、美洲、亚洲

    • 价格:具有竞争力的套餐价格

    • 适用:机场购物及旅游人士

LPA:1$rsp.truphone.com$QRF-HEINEMANN-PMRDGIR2EARDGIT5

  1. freenet Travel

    • 特点:德国知名电信运营商推出

    • 覆盖:欧洲和全球主要国家

    • 价格:多种套餐选择

    • 适用:欧洲旅游用户

LPA:1$rsp.truphone.com$QRF-FREENET-PMRDGIR2EARDCIT5

  1. Sparhandy

    • 特点:德国最大的移动虚拟网络运营商

    • 覆盖:全球 200 + 个国家

    • 价格:价格亲民

    • 适用:长期国外工作人士

LPA:1$rsp.truphone.com$QRF-SPARHANDY-PMRDGIR2EARDCIT5

  1. Xesim

    • 特点:快速增长的 eSIM 平台,服务周到

    • 覆盖:全球主要目的地

    • 价格:性价比高

    • 适用:自助旅游爱好者

LPA:1$rsp.truphone.com$QRF-XEAPPS-PMRDGIR2EARDGIT5

  1. Digital Repubilc

    • 特点:数字化服务平台,操作简单

    • 覆盖:全球范围

    • 价格:按需付费,无额外费用

    • 适用:初次使用 eSIM 的用户

LPA:1$rsp.truphone.com$QRF-DIGITALREPUBLIC-PMRDGIR2EARDCIT5

  1. High Mobile

    • 特点:提供本地化服务,客户支持好

    • 覆盖:全球 150 + 个国家

    • 价格:多档位套餐

    • 适用:需要客服支持的用户

LPA:1$rsp.truphone.com$QRF-HIGHMOBILE-PMRDGIR2EARDCIT5

  1. Clujairport

    • 特点:罗马尼亚机场推出,便利快捷

    • 覆盖:欧洲重点区域

    • 价格:机场特价

    • 适用:欧洲旅游用户

LPA:1$rsp.truphone.com$QRF-CLUJAIRPORT-PMRDGIR2EARDIIT5

  1. Esimfirst
  • 特点:新兴平台,功能完整,用户界面友好

  • 覆盖:全球 200 + 个国家

  • 价格:首次用户有优惠

  • 适用:所有类型的用户

LPA:1$rsp.truphone.com$QRF-ESIMFIRST-PMRDGIR2EARDIIT5

选择建议:

根据你的出行目的地和使用习惯选择即可,多数服务商支持多个 eSIM 同时激活,可以根据需要灵活切换。

使用小贴士:

• 出国前先下载好地图、翻译、订票等关键应用

• 激活前确保手机连接WiFi

• 保存好激活二维码和ICCID,以防需要重新激活

• 根据出行时长和使用习惯选择合适的套餐

• 到达目的地后切换至eSIM进行数据使用

• 定期查看流量使用情况

📌 转载信息
原作者:
tans0008
转载时间:
2026/1/3 14:59:26

背景

・Gemini: 相同 role 的消息不能连续出现
・Claude: 一个 tool call 必须跟随一个 tool result,tool call ID 必须对齐
・Gemini3: tool call 必须附带思维签名
……


在不知道第多少次被 Rikkahub 和 RooCode 的 Tool Call 报错折磨到之后 我选择开发了…

AnyToolCall

AnyToolCall 是一个基于提示词注入的 openai 兼容 LLM 中间件,是 LLM 以高兼容性进行工具调用,也可以让不支持工具调用的模型强兼工具调用

Info

Key Features:
・解析请求中的 tool call/tool result / 工具定义 并转化为提示词注入模式的工具调用
・流式输出支持 tool call
・强健的 tool 调用格式约束(基于随机罕见 Unicode 字符 详见 GitHub 说明) 降低解析失败的可能

部署 & 使用详见
仓库地址:GitHub - AliyahZombie/AnyToolCall: A llm api midware offering prompt-based function call ability.

QAQ 给孩子点个 star 呗

Danger

本项目是对 LLM api 的透明代理,这意味着部署者能看到 您的 APIkey,聊天上下文… 推荐自行部署以保证数据安全


📌 转载信息
原作者:
curaalizm
转载时间:
2026/1/3 14:59:23

前言:感谢 V 友开发的 rise ,我从他那里获得了 ”在日常生活中多基础外语“ 灵感。

我想了想,我每天用 Claude code 的时间也挺长的。于是我做了一个 CLAUDE​.md 的提示词,实现类似 rise 一样的在日常对话插入英文的核心功能。

使用方式:

  1. 下载 prompt 内容: https://gist.github.com/artshooter/99c7bb9078bc6868196b6179bc43340b
  2. 写入你本地的 ~/.claude/CLAUDE​.md 文件中。(如果没有这个文件就创建一个)

cc 对话示例:

我:两句话介绍 VLL 的氛围语言学习功能

cc:VLL 是一种 immersive(沉浸式) 语言学习方式,在日常对话中自动将部分词汇替换为你正在学习的 target language(目标语言),并用母语标注 annotations(注释)。
这种方法让你在正常交流中 naturally(自然地) 接触新词汇,比刻意背单词更 effortless(轻松) 且容易 retain(记住)。

近日有网友发现新隐形模型,并且经过测试风格类似 Grok,分别是 Vortexshade,quantumcrow (LMArena) 和 Obsidian (DesignArena)



𝕏 x.com

🚨Vortexshade is on lmarena

this upcoming grok 4.2 update this is snake game made by it

8:16 AM - 2 Jan 2026 230🔁 12

📌 转载信息
原作者:
BunnHack
转载时间:
2026/1/3 14:59:11

最近头发两边很炸,导致头看起来很大,很不美观,所以去剪掉,发现了每次发型师(无论专家还是总监)给我剪发的模板就是剪短推光两边,上面不动

后面通过 ai 和小红书研究了一番,发现其实这是行业不成文的规矩,主要是为了提高顾客的复购率,并且技术要求最低最简单,而且效果明显,这种剪法就是 3-4 周内必炸!

最后给大家的建议就是,如果条件允许懒得打理,每 2 周剪一次这种模板是最好的,否则就留长,然后用服帖药水自己烫两边.

前两天看到 Manus 被收购,今天就看到字节了 AnyGen 。
初步体验了一下,算得上是对标的竞品,
用来生成文档和 PPT 还不错,
不用考虑太多的提示词,
生成的速度和效果也都可以。
用来生成简单的页面,也看得过去。

不知道之后会发展怎么样,
至少现在够用了,
邀请用户还能领 Pro 版本还不错。
可以先用起来。

iOS 生成的默认是 HEIC+一个小视频 MOV 。在 Android Google Photos 无法播放。所以我用 Export Unmodified Originals ,导出 MOV+JPG 文件。
然后用[https://github.com/PetrVys/MotionPhoto2]( https://github.com/PetrVys/MotionPhoto2)转换成 Motion Photo 。
但是最近发现直接转换后 Android 放不了,于是我把 mov 转换成 MP4 再合并,但现在 Google Photos 打开的时候会卡,想知道应该怎么处理?另外我有 pixel 一代,可以上传原画的 live photo 吗?
ffmpeg -i IMG_3963.MOV -c:v copy -c:a copy IMG_3963.MP4

基本完全靠 claude code 帮助搭建的出海站点
支持 text to image ,text to video, music 等
设计了多 provider 负载,完整的支付
一周左右的时间,开始上线测试,希望大家多提提意见 https://ourl.cn/naXGsy
主打 sora2 sora2pro veo3.1 nano banana ,
人民币支付还没有接通,现在注册有 credit 可以免费测试

活动入口:中国银行 app→我的→我的权益→立减金

点开能发现微信和支付宝各有三张满 10.01 减 10 的立减金,点进立减金就能兑换到微信/支付宝,然后在微信/支付宝支付的时候选择中行储蓄卡就能用这个立减金了。

最优传输问题

考虑这么一个问题,假设佬友 A、B 分别有 100 刀、400 刀 api,佬友 C、D 分别有 200 刀、300 刀的 api 需求,那么怎么生成一个合理的分配方案

我们可以很容易列出这么一个表格

供给 \ 需求C(200)D(300)
A(100)
B(400)

由于供给需求平衡,分配方案是非常多的,比如使用对角线定 1 法,立马就能像填字游戏一样给出一个结果:

  • 对角线定 1:
    供给 \ 需求C(200)D(300)
    A(100)100
    B(400)
  • 填充空格:
    供给 \ 需求C(200)D(300)
    A(100)1000
    B(400)100300

非常好,我们已经得到一个可行方案了。把上述问题归纳抽象成数学语言,就是一个最优传输 ( Optimal Transport) 问题:

在给定源分布、目标分布和运输代价矩阵的情况下,求一个非负矩阵,使得它的行和、列和分别等于给定分布,并且总运输成本最小。

已知供给 μ、需求 ν 、以及每条路的单位成本 Cij
目标运输矩阵 Π:

  • Πij0
  • 行和 = 供给: Π1=μ
  • 列和 = 需求: Π1=ν

目标:总成本最小

minΠU(μ,ν)C,Π=i,jCijΠij
数学语言与引入问题的对应
源分布供给端(100,400)
目标分布需求端(200,300)
传输代价未显式体现,默认都是单位代价
非负矩阵传输非负性,不能从需求端分配资源给供给端

听起来这个问题也不难嘛,填填空也就把方案做出来了,那么这个分配方式有什么问题吗?

  • 规模复杂性:如果供给方和需求方数量增加,问题也将复杂起来,由于要求传输非负性,定 1 法可能导致其他位置计算出现负数而需要进行调整
  • 成本敏感性:给定供给方和需求方之间的传输代价,问题复杂度也立马上升
  • 解的稀疏性:定 1 法容易产生大量 0 元素,在应对分布改变和成本改变时不稳定,比如某个供给方断供,其对应的需求方将立刻卡脖子

为了不把鸡蛋放在一个篮子里(解的稀疏性)和解决其他问题,我们需要把分配方式软化,比如这样的分配方式就被认为比之前的方案更加 “软”:

供给 \ 需求C(200)D(300)
A(100)5050
B(400)150250

那么,怎么数学化地计算这种软化方案呢,数学家引入了一种叫熵正则项,用来惩罚传输方案里的极端项,就是让你不要把鸡蛋放在一个篮子里:就算你供给量少,也要拆开卖,万一买家跑了你也不至于全亏损;就算你需求少,也分开买,卖家不稳定了你还有缓冲。于是熵正则最优传输的目标变成:

minΠU(μ,ν)C,ΠεH(Π)

其中,

H(Π)=i,jΠijlogΠij

ε 代表了分散程度,该值如果取 0,则问题等价于原始的最优传输问题,该值增大,传输方案越软。
增加熵正则项后,目标函数就从线性变成了强凸性的。在给定 ε 的情况下,熵正则最优传输问题的最优解是唯一的,且具有一个非常优雅的结构:

Π=diag(u)Kdiag(v),Kij=exp(Cij/ε)

计算出缩放系数 uv 就能给出最优解了,这里有个简单的 Sinkhorn 算法,通过交替地更新这两个缩放系数,让边际约束满足:

uμ/(Kv),vν/(Ku)

由于该问题变成强凸性,所以上述过程收敛很快(线性收敛),而且迭代过程只涉及逐行逐列的元素归一化(可规模化并行计算),是一个非常实用的方案。

以前述引入问题为例

ε=1, 计算 K 矩阵:

K=[1e1e11]=[]

第 1 轮迭代后的方案 X(1)

X(1)[]

此时行和误差约为 6.1891:

  • 行和 (,)
  • 列和 =(200,300)

第 6 轮迭代后的方案 X(6)

X(6)[]

・行和 (100.00034024, 399.99965976) (已经非常接近供给)
・列和 = (200, 300)


双随机矩阵

解决了熵正则最优问题,我们再来看看这个传输矩阵,如果我们把供给、需求归一化,那么原先的传输矩阵就变成了一个 “分配比例” 矩阵,每个格子不再代表具体分配的额度,而是分配比例。失去量纲让它变得更加通用了,从一个具体的 “分配方案” 变成了一个 “通用的分配规则”。

如果进一步把供给端和需求端设置为均匀分布(都是 1),那么这种 “分配比例” 矩阵会具备如下特点:

  • 所有元素非负
  • 各行元素之和为 1
  • 各列元素之和为 1

具备这些特点的矩阵,取名叫 “双随机矩阵”,因为它每行每列都像是一个随机概率分布。可以把双随机矩阵看成一个软路由表,它规定了供给和需求之间的分配比例,又可以通过软化系数 ε 避免极端分配情况(即不把鸡蛋放在一个篮子里)和快速调整(可学习性)。

容易观察到,它具备乘法封闭性:

任意两个 nxn 双随机矩阵相乘,结果仍然是双随机矩阵。

相当于可以累积这种路由表,而保持供给、需求的分配方案不崩溃 —— 你叠多少层 “软分配”,整体仍然是一个合法的软分配,不会出现 “某个需求方越分越多、某个供给方越分越空” 的诡异情况。如果需要对一个分布进行信息重分配,那双随机矩阵是一个非常好的选择。

诶,既需要避免累积导致模式崩溃,又期望促进不同分布之间进行信息分配,还有良好的可学习性性质,那不正是神经网络中常见的需求吗?这也正是 DeepSeek mHC 引入双随机矩阵(论文里的故事叫 “流形约束”)的重要原因。啊这鸡汤真是太好喝了,哦不是, 这矩阵真是太美妙了。

  • 字节的 HC 尝试引入 Mapping 来丰富跨层连接多样性,mHC 则通过双随机矩阵约束改良了 HC 的训练不稳定性(谱范数为 1,累乘封闭性)

(除引入双随机矩阵外,mHC 也做了很多扎实的优化工作,这里就不再赘述和解读了,仅从最优传输角度提供一种浅浅解读)


📌 转载信息
原作者:
zhong_little
转载时间:
2026/1/3 14:54:58

接上回:因为 PR 被拒,所以重写了整个逻辑
佬友们好喵
之前发的 AiNiee-CLI 一直只有 TUI,虽然黑框框对于生产力来说确实舒服,不过挂机久了总觉得少点什么,后面发现不做个好看的无脑的 UI 还是不太行,所以又用 react 弄了个 WebUI 喵

肝了一下,给 CLI 版内置了一个 Web Server
现在不需要一直盯着终端日志刷屏了,直接浏览器打开,体验大概是这样的喵:

看图喵

这次更新说大也不大喵

其实就一个核心点
Web Dashboard
之前 TUI 其实也还行啦,不过 WebUI 更高级点罢了(x,现在你可以直接看到:

  • 实时 RPM:看着曲线波动,能直观判断 API 是不是阻塞了喵!
  • 实时 TPM:计算成本和速度更直观喵!
  • 动态波形图:有一说一,看着那个线条在那画,其实我觉得还蛮解压的喵

1. 因为是 Web 服务,现在你可以把程序扔在 NAS 或者服务器上跑,再或者躺床上用手机浏览器(暂时不建议手机访问,我这边自己测试出了点毛病)看进度
不管是局域网还是做个内网穿透,随时随地都能监工,不用爬起来看电脑屏幕了喵!
这对懒猫娘来说还是很不错的功能喵!

2. 虽然加了 Web 端,但启动还是老样子喵
为了照顾不想敲命令的兄弟,仓库里依然保留了 一键启动脚本 (.bat / .sh)。
双击 → 自动拉起环境 (uv) -> 按 9 启动 WebServer → 自动弹窗 → 开跑


碎碎念:
目前的 WebUI 已经能满足绝大部分挂机监控需求,后续可能会把配置热修改也搬到 Web 上来喵!

现在 CLI 版本集成了原生的 TUI,也支持 WebServer,原版只有 GUI,占用还高,真没有 CLI 版本好用吧(又开始王婆了喵 x
仓库地址还是老地方喵:
Github: GitHub - ShadowLoveElysia/AiNiee-CLI: ⚡ An optimized, headless CLI for AiNiee powered by UV. Features intelligent format conversion, multi-profile config system, and stabilized TUI. ⚡ 基于 UV 驱动的 AiNiee 优化版 CLI 提供智能化格式输入 / 转换、多配置文件系统及高度稳定的 TUI 界面,专为高效批处理设计
如果可以的话,能点个 Star 喵?


📌 转载信息
转载时间:
2026/1/3 14:54:27

这两天写了一个博文在 L 站上的转载),但是有亿点长,我想在这个快节奏的社会应该也没有人看完这么长的玩意,所以我决定水个帖,单独说说这个压轴的项目 ——FakeNET(Github)


这是一个一个 伪・文 章 平 台,需要填入 CloseAI 格式的 API

我简单画个图吧:

是的,就是这么简单一个玩意

你曾想过?你是互联网上唯一的人类,其它所有账号都是机器人,你看着 AI 产生的内容、阅读 AI 生成的评论……


📌 转载信息
转载时间:
2026/1/3 14:54:06

https://huodong.10155.com/h5/hdact5/basketballGame/#/

签到三天,第三天每次刷新增加一次投篮机会,理论上可以刷无限次投篮次数,bug有一定的时效性,有可能过几天就修复了,注意:只有签到满三天,在第三天的时候这个bug才会触发,第四天会重置,就不会触发这个逻辑漏洞

// 全局变量:记录执行次数和所有返回结果
let executeCount = 0; // 执行次数计数器
const requestResults = []; // 存储每次请求的返回结果

// 封装请求头配置,减少重复代码
function setCommonHeaders(xhr) {

  xhr.setRequestHeader('Accept', 'application/json, text/plain, */*');
  xhr.setRequestHeader('Content-Type', 'application/json');
  xhr.setRequestHeader('Pragma', 'no-cache');
  xhr.setRequestHeader('Cache-Control', 'no-cache');
  xhr.setRequestHeader('accessToken', 'Bearer eb2cdded-2105-4833-86c6-6b27856c8dea');
  xhr.setRequestHeader('Accept-Language', 'zh-CN,zh;q=0.9');

}

// 第一个请求:开始挑战
function startChallenge() {
  // 每次执行计数器+1
  executeCount++;
  const currentExecId = executeCount; // 记录本次执行的序号
  console.log(`===== 第 ${currentExecId} 次执行 =====`);

  const requestData = JSON.stringify({
    "activityId": 1312,
    "mobile": "你的手机号"
  });

  const xhr = new XMLHttpRequest();
  xhr.withCredentials = true;

  // 初始化本次执行的结果记录
  const currentResult = {
    execId: currentExecId, // 执行序号
    time: new Date().toLocaleString(), // 执行时间
    firstRequest: null, // 第一个请求结果
    secondRequest: null // 第二个请求结果
  };

  xhr.addEventListener('readystatechange', function () {
    if (this.readyState === this.DONE) {
      // 处理第一个请求完成后的逻辑
      if (this.status >= 200 && this.status < 300) {
        try {
          const responseData = JSON.parse(this.responseText);
          console.log(`第 ${currentExecId} 次 - 第一个请求返回结果:`, responseData);
          currentResult.firstRequest = responseData; // 记录第一个请求结果

          // 确保获取到result后再执行第二个请求
          if (responseData.result) {
            challengeResultRecord(responseData.result, currentResult);
          } else {
            const errorMsg = `第 ${currentExecId} 次 - 第一个请求未返回result字段`;
            console.error(errorMsg, responseData);
            currentResult.firstRequest = { error: errorMsg, rawData: responseData };
            // 记录本次结果
            requestResults.push(currentResult);
          }
        } catch (e) {
          const errorMsg = `第 ${currentExecId} 次 - 解析第一个请求返回数据失败`;
          console.error(errorMsg, e);
          currentResult.firstRequest = { error: errorMsg, errorDetail: e };
          // 记录本次结果
          requestResults.push(currentResult);
        }
      } else {
        const errorMsg = `第 ${currentExecId} 次 - 第一个请求失败,状态码:${this.status}`;
        console.error(errorMsg);
        currentResult.firstRequest = { error: errorMsg, statusCode: this.status };
        // 记录本次结果
        requestResults.push(currentResult);
      }
    }
  });

  xhr.open('POST', 'https://huodong.10155.com/wo_activity/basketballCheck/startChallenge');
  setCommonHeaders(xhr);
  xhr.send(requestData);
}

// 第二个请求:提交挑战结果
function challengeResultRecord(challengeId, currentResult) {
  const currentExecId = currentResult.execId;
  const requestData = JSON.stringify({
    "id": challengeId,
    "hitCount": 8
  });

  const xhr = new XMLHttpRequest();
  xhr.withCredentials = true;

  xhr.addEventListener('readystatechange', function () {
    if (this.readyState === this.DONE) {
      // 处理第二个请求完成后的逻辑
      if (this.status >= 200 && this.status < 300) {
        try {
          const responseData = JSON.parse(this.responseText);
          console.log(`第 ${currentExecId} 次 - 第二个请求返回结果:`, responseData);
          currentResult.secondRequest = responseData; // 记录第二个请求结果
        } catch (e) {
          const errorMsg = `第 ${currentExecId} 次 - 解析第二个请求返回数据失败`;
          console.error(errorMsg, e);
          currentResult.secondRequest = { error: errorMsg, errorDetail: e };
        }
      } else {
        const errorMsg = `第 ${currentExecId} 次 - 第二个请求失败,状态码:${this.status}`;
        console.error(errorMsg);
        currentResult.secondRequest = { error: errorMsg, statusCode: this.status };
      }
      // 记录本次完整结果
      requestResults.push(currentResult);
      console.log(`===== 第 ${currentExecId} 次执行完成,累计执行 ${executeCount} 次 =====`);
    }
  });

  xhr.open('POST', 'https://huodong.10155.com/wo_activity/basketballChallengeTime/challengeResultRecord');
  setCommonHeaders(xhr);
  xhr.send(requestData);
}

// 定时执行函数:每0.5秒执行一次startChallenge
function startScheduledExecution() {
  // 立即执行第一次
  startChallenge();
  
  // 之后每0.5秒执行一次(1000毫秒 = 1秒)
  setInterval(() => {
    startChallenge();
  }, 500);

  // 提供查看结果的辅助函数
  window.getExecutionResults = function() {
    console.log('===== 所有执行结果汇总 =====');
    console.log(`累计执行次数:${executeCount}`);
    console.log('执行详情:', requestResults);
    return {
      totalCount: executeCount,
      results: requestResults
    };
  };

  console.log('定时任务已启动,每0.5秒执行一次!');
  console.log('可调用 getExecutionResults() 查看所有执行记录');
}

// 启动定时执行
startScheduledExecution();

📌 转载信息
转载时间:
2026/1/3 14:40:47

背景

• Gemini: 相同role的消息不能连续出现
• Claude: 一个tool call必须跟随一个tool result,tool call ID必须对齐
• Gemini3: tool call必须附带思维签名
……


在不知道第多少次被Rikkahub和RooCode的Tool Call报错折磨到之后 我选择开发了…

AnyToolCall

AnyToolCall是一个基于提示词注入的 openai兼容 LLM中间件,是LLM以高兼容性进行工具调用,也可以让不支持工具调用的模型强兼工具调用

Info

Key Features:
• 解析请求中的tool call/tool result/工具定义 并转化为提示词注入模式的工具调用
• 流式输出支持tool call
• 强健的tool调用格式约束(基于随机罕见Unicode字符 详见GitHub说明) 降低解析失败的可能

部署&使用详见
仓库地址:GitHub - AliyahZombie/AnyToolCall: A llm api midware offering prompt-based function call ability.

QAQ给孩子点个star呗

Danger

本项目是对LLM api的透明代理,这意味着部署者能看到 您的APIkey,聊天上下文… 推荐自行部署以保证数据安全


📌 转载信息
原作者:
curaalizm
转载时间:
2026/1/3 14:40:17

最近头发两边很炸,导致头看起来很大,很不美观,所以去剪掉,发现了每次发型师(无论专家还是总监)给我剪发的模板就是剪短推光两边,上面不动

后面通过 ai 和小红书研究了一番,发现其实这是行业不成文的规矩,主要是为了提高顾客的复购率,并且技术要求最低最简单,而且效果明显,这种剪法就是 3-4 周内必炸!

最后给大家的建议就是,如果条件允许懒得打理,每 2 周剪一次这种模板是最好的,否则就留长,然后用服帖药水自己烫两边.

Gemini bussiness 自动刷新逻辑,接入 yescapture 打码服务,做到了基本 100% 的成功率

【回顾】 在之前的自动化注册或者刷新中会出现验证码发送错误的情况,原因在于获取的谷歌验证分数太低导致发送接口错误

【探索】通过抓包发现,以下接口就是发送邮件的接口

https://accountverification.business.gemini.google/_/IdentityPlatformFrontendUI/data/batchexecute?rpcids=IjXaFf&source-path=%2Fv1%2Fverify-oob-code&bl=boq_cloud-identity-identityplatform-frontend_20251210.10_p1&hl=en&rt=c

请求体为
f.req:[[["IjXaFf",,null,"generic"]]]

里面的 0cAFcWe 就是开头的验证

因此,只需要接入打码服务即可完成再次发送,测试结果为成功率基本 100%

完整 nodejs 代码如下:

 const fs = require('fs');
const os = require('os');
const path = require('path');
const axios = require('axios');
const express = require('express');
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
const { ImapFlow } = require('imapflow');
const { simpleParser } = require('mailparser');

puppeteer.use(StealthPlugin());

const app = express();
const port = 3000;

// =============================== // 配置项 // =============================== const WEBSITE_KEY = '6Ld8dCcrAAAAAFVbDMVZy8aNRwCjakBVaDEdRUH8';
const WEBSITE_URL = 'https://accountverification.business.gemini.google';

const sleep = (ms) => new Promise(r => setTimeout(r, ms));

function createTempUserDataDir() {
  return fs.mkdtempSync(path.join(os.tmpdir(), 'pptr-profile-'));
}

/**
* 邮件获取类
*/
class GeminiFetcher { constructor(email, clientId, refreshToken) { this.email = email; this.clientId = clientId; this.refreshToken = refreshToken; } async getAccessToken() { const params = new URLSearchParams({ client_id: this.clientId, grant_type: 'refresh_token', refresh_token: this.refreshToken }); const res = await axios.post('https://login.microsoftonline.com/consumers/oauth2/v2.0/token', params.toString()); return res.data.access_token; } async tryFetchOnce() { const token = await this.getAccessToken(); const client = new ImapFlow({ host: 'outlook.office365.com', port: 993, secure: true, auth: { user: this.email, accessToken: token }, logger: false }); await client.connect(); let code = null; try { for (const mailbox of ['INBOX', 'Junk']) { let lock; try { lock = await client.getMailboxLock(mailbox); const messages = await client.search({ all: true }); const lastUids = messages.slice(-5).reverse(); const tenMinutesAgo = Date.now() - 10 * 60 * 1000; for (let uid of lastUids) { let msg = await client.fetchOne(uid, { source: true }); let parsed = await simpleParser(msg.source); if (parsed.date.getTime() > tenMinutesAgo && parsed.subject && (parsed.subject.includes('Gemini') || parsed.subject.includes('Google'))) { const content = parsed.text + (parsed.html || ""); const match = content.match(/[A-Z0-9]{6}/); if (match) { code = match[0]; break; } } } } catch (e) { } finally { if (lock) lock.release(); } if (code) break; } } finally { await client.logout(); } return code; } } async function startPollingForCode(email, clientId, refreshToken) { const fetcher = new GeminiFetcher(email, clientId, refreshToken); for (let i = 1; i <= 10; i++) { try { console.log(`[${email}] 尝试获取邮件验证码 (${i}/10)...`); const code = await fetcher.tryFetchOnce(); if (code) { console.log(`[${email}] 成功获取到验证码: ${code}`); return code; } } catch (err) { console.error(`[${email}] 获取验证码报错: ${err.message}`); } await sleep(4000); } return null; } async function getCaptchaToken(apiKey) { try { console.log('🤖 正在向 YesCaptcha 请求人机识别 Token...'); const createResp = await axios.post('https://api.yescaptcha.com/createTask', { clientKey: apiKey, task: { websiteURL: WEBSITE_URL, websiteKey: WEBSITE_KEY, pageAction: 'verify_oob_code', type: 'RecaptchaV3TaskProxylessM1' } }); const taskId = createResp.data.taskId; for (let i = 0; i < 20; i++) { await sleep(3000); const resultResp = await axios.post('https://api.yescaptcha.com/getTaskResult', { clientKey: apiKey, taskId: taskId }); if (resultResp.data.status === 'ready') { console.log('🤖 YesCaptcha Token 获取成功'); return resultResp.data.solution.gRecaptchaResponse; } } } catch (error) { console.error(`❌ YesCaptcha 错误: ${error.message}`); return null; } } function patchPayload(rawBody, newToken) { const params = new URLSearchParams(rawBody); let fReq = params.get('f.req'); if (!fReq) return rawBody; const tokenRegex = /0[3c]AFc[a-zA-Z0-9_\-]{50,}/g; if (tokenRegex.test(fReq)) { params.set('f.req', fReq.replace(tokenRegex, newToken)); } return params.toString(); } // =============================== // 核心任务逻辑 // =============================== async function runLoginTask(accountData, apiKey) { const parts = accountData.split('----'); const [email, password, clientId, refreshToken] = parts; const userDataDir = createTempUserDataDir(); console.log(`🚀 [任务开始] 账号: ${email}`); let resolveCodeSent; const codeSentBarrier = new Promise((resolve) => { resolveCodeSent = resolve; }); let resolveAuthBarrier; const authBarrier = new Promise((resolve) => { resolveAuthBarrier = resolve; }); console.log('🌐 正在启动浏览器...'); const browser = await puppeteer.launch({ headless: "new", userDataDir, args: ['--no-sandbox', '--disable-setuid-sandbox', '--window-size=1280,800'] }); try { const page = await browser.newPage(); await page.setRequestInterception(true); page.on('request', req => { if (['image', 'media', 'font'].includes(req.resourceType())) return req.abort().catch(() => {}); req.continue().catch(() => {}); }); page.on('response', async res => { const url = res.url(); if (url.includes('batchexecute')) { try { const text = await res.text(); // 监控验证码发送状态 if (text.includes('LookupVerifiedEmail') || text.includes('SendVerificationCode')) { console.log('✅ [网络监控] 监听到 SendVerificationCode 响应,服务器已发信!'); resolveCodeSent(); } // 监控验证码报错 if (text.includes('CAPTCHA_CHECK_FAILED')) { console.log('⚠️ [网络监控] 检测到人机验证码拦截,准备打码补丁...'); const newToken = await getCaptchaToken(apiKey); if (newToken) { const originalRequest = res.request(); const newPostData = patchPayload(originalRequest.postData(), newToken); console.log('🔄 [补丁] 正在重发带 Token 的 Payload...'); await page.evaluate(async (u, h, b) => { await fetch(u, { method: 'POST', headers: h, body: b }); }, url, originalRequest.headers(), newPostData); resolveAuthBarrier(); } } else if (text.includes('inner_api_status')) { resolveAuthBarrier(); } } catch (e) { } } }); console.log('🍪 正在设置初始 XSRF Cookie...'); await page.goto('https://auth.business.gemini.google/', { waitUntil: 'domcontentloaded' }); await page.setCookie( { name: '__Host-AP_SignInXsrf', value: 'KdLRzKwwBTD5wo8nUollAbY6cW0', domain: 'auth.business.gemini.google', path: '/', secure: true }, { name: '_GRECAPTCHA', value: '09ABCL...', domain: '.google.com', path: '/', secure: true } ); const targetUrl = `https://auth.business.gemini.google/login/email?continueUrl=https%3A%2F%2Fbusiness.gemini.google%2F&loginHint=${encodeURIComponent(email)}&xsrfToken=KdLRzKwwBTD5wo8nUollAbY6cW0`; console.log('🔗 正在加载登录页面...'); await page.goto(targetUrl, { waitUntil: 'domcontentloaded' }); console.log('🖱️ 正在点击 [通过电子邮件发送验证码] 按钮...'); await page.waitForSelector('#sign-in-with-email', { visible: true }); await page.click('#sign-in-with-email'); console.log('🔎 等待验证码输入框出现...'); const inputSelector = 'input[jsname="ovqh0b"]'; await page.waitForSelector(inputSelector, { timeout: 30000 }); console.log('⏳ [关键等待] 正在等待发信确认信号 (由网络响应触发)...'); const waitStart = Date.now(); await Promise.race([codeSentBarrier, sleep(12000)]); console.log(`⏱️ 等待结束,耗时: ${Date.now() - waitStart}ms`); console.log('📫 开始爬取邮箱验证码...'); const code = await startPollingForCode(email, clientId, refreshToken); if (!code) throw new Error("获取验证码超时"); console.log('⌨️ 正在输入验证码并提交...'); await page.type(inputSelector, code, { delay: 100 }); await page.click('button[jsname="XooR8e"]'); console.log('🔄 等待登录跳转...'); await sleep(4000); if (page.url().includes('/admin/create')) { console.log('📄 进入创建页面,检测是否有 [同意] 按钮...'); const btn = await page.waitForSelector('button.agree-button', { visible: true, timeout: 5000 }).catch(() => null); if (btn) { console.log('🖱️ 点击同意按钮'); await btn.click(); await sleep(2000); } } console.log('🍪 正在通过 CDP 协议提取全量 Cookie...'); let hostCoses = '', secureCSes = ''; for (let i = 0; i < 15; i++) { const client = await page.target().createCDPSession(); const { cookies } = await client.send('Network.getAllCookies'); await client.detach(); for (const c of cookies) { if (c.name === '__Host-C_OSES') hostCoses = c.value; if (c.name === '__Secure-C_SES') secureCSes = c.value; } if (hostCoses && secureCSes) { console.log('✨ 核心 Cookie 提取完毕'); break; } console.log(`📭 第 ${i+1} 次提取 Cookie 未果,继续轮询...`); await sleep(1000); if (i === 5) { console.log('🔄 提取超时,尝试跳转业务主域名促活 Cookie...'); await page.goto('https://business.gemini.google/', { waitUntil: 'networkidle2' }).catch(() => {}); } } if (!hostCoses || !secureCSes) throw new Error('Cookie 提取失败'); console.log('🏁 [任务成功] 正在返回结果'); return `${email}|${password}|${hostCoses.trim()}::${secureCSes.trim()}`; } finally { console.log('🧹 正在关闭浏览器并清理临时文件...'); await browser.close(); try { fs.rmSync(userDataDir, { recursive: true, force: true }); } catch (e) {} } } app.get('/login', async (req, res) => { const { data, apikey } = req.query; if (!data || !apikey) return res.status(400).send('Missing params'); try { const result = await runLoginTask(data, apikey); res.send(result); } catch (err) { console.error(`❌ 接口报错: ${err.message}`); res.status(500).send(`Error: ${err.message}`); } }); app.listen(port, () => { console.log(); console.log(`🚀 服务运行中: http://localhost:${port}`); console.log(`📝 请求示例: /login?data=email----pass----id----token&apikey=xxx`); console.log(); });

运行成功后请求示例为:
http://localhost:3000/login?data=CharlesHoward4117@outlook.com----rrom1652910----dbc8e03a-b00c-46bd-ae65-b683e7707cb0----M.C539_BL2.0.U.-Cn78r6PhJ0VI48KtRppemAxUI038pVKjd9Kyu*lxTsFW9cyZqou7zcWsKmg0DdlxCURrppr3paaCrZHMIY1gzBcD4p*p*TQHz4DqDF0xeUTOtkTlpy!B6leXglSy3O1Uj9hDUkq3f!75ha4A0VrC7agSXDzUaV!tPIJ7AA6fgdKeehLjzhNaHr5XqvTM39Gr1OR9H8VnYBB!K6yGDAhWWsYd5blEfhqVoAzG3TBDTcdrho3fwU2uR8V8oVtJfQ*aGyw9XwCj*gIRq1SdfWR5h!PSZ4TuFSJVqeLB7qbIOHLfWPtVkz*ya6jxMX4DqJASLQIIILno1lJznm1VAeQJPze!REpnD9L9F9t!dwRwpmd1235SbqnYAEG5rFXKSOqikRjSYSWeCVmZYBkWSWBLEuQ$&apikey=c169a2dbd972acac69b4f30c52c060bfbbe0854f84831

其中 data 就是邮箱的基本信息,这里只适配了微软邮箱
apikey 就是 yes 的 apikey

默认如果浏览器可以成功发送则不调用 yes 打码服务,返回示例

CharlesHoward4117@outlook.com|rrom1652910|COS.AW82PoFnmTPemeKaOLiqi59wSPBs5Q6mSkgqGsk2OwOP0c_vBgAo4L3k8IdN-La_tMrJbjbatQy9Uw6QllD2VL0xdhdbG8Ks2tlCGGEfxZgj3FhTP2fNSeEfk3ed1PL_l6oTfMiLL5ivKwKCYE5sbuI::CSE.AdwtfTBBmOWOWWkad3G32S_ut5NlmYWQhJCRohzHwevu9ZpYyKrRHpviThBTJNh8_KdlBLAUbSQT5zD6RGoggxO8air-J0l9qO_XtmbdLhXYDOCmMdNgDFq40G1HmpgeGfUtXEVB-cT2lf33J9xSx_F0w9ZmWrrI96KJk8ijQ_qc5T5nt3grRfI5B9YpHHOww_Uqo14z0Yg1899rHDdNpgg9y_X9xK3nDn8C3lYKerslcKak12VC_Rxq9JJDYNsysUjhfNmyt-tyU9xLaJV_vR_FG7nnkO7bmRL664yEpAzRD0dB9ZnxXrCb45q1Q7HRs3jjxoId1DVYw2aSbU5URDDY-q5bCDbHModdg36rRuICHmxQQRsz39YivNknIDe7kvOnubDYU1M9wmhX7bsLkKawILuj0jwsqNRb0jh-phY3D8DZhydEHROjjba99MAsIvkH8ZytY_x3Aw

COS 与 CSE 的 cookie 都有返回,一个账户大概 30s 左右,可并发,一分钟基本可以刷新或者注册 40 个账户(并发 20)

欢迎更多开发者去完善自动化注册和刷新的逻辑…


📌 转载信息
转载时间:
2026/1/3 12:07:27