大家不小心把k12绑到自己大号的可以用这个脚本一键下车

Luminet 2026-07-07 01:08 1

此脚本可以自动获取当前账号所有空间,一键从当前账户所有空间跑路,使用Gemini 3.5 Flash改的。(没有加入判断跑路的空间是否是k12)

注意:推荐先切到个人账户,避免脚本使用的空间AT因为从空间跑路导致401,导致非常不好的体验。

已经被402封禁的空间无法退出。


(async function runrunrun() {
try {
// 1. 获取基础会话信息
const session = await fetch('/api/auth/session').then((r) => r.json());
const accessToken = session?.accessToken;
const user_id = session?.user?.id;

if (!accessToken || !user_id) {
console.log('获取 Token 或用户 ID 失败,请确认登录状态。');
return;
}

// 2. 获取账号列表
console.log('正在获取账号列表...');
const checkResponse = await fetch(
'https://chatgpt.com/backend-api/accounts/check/v4-2023-04-27?timezone_offset_min=-480',
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
}
);

if (!checkResponse.ok) {
console.log(`无法获取账号列表,HTTP 状态码: ${checkResponse.status}`);
return;
}

const checkData = await checkResponse.json();
const accountOrdering = checkData?.account_ordering || [];

if (accountOrdering.length === 0) {
console.log('未检测到可处理的账号列表。');
return;
}

console.log(`获取成功,共检测到 ${accountOrdering.length} 个账号。开始处理...`);

const results = {
success: [],
failed: []
};

// 3. 循环发送 DELETE 请求
for (const account_id of accountOrdering) {
try {
const response = await fetch(
`https://chatgpt.com/backend-api/accounts/${account_id}/users/${user_id}`,
{
method: 'DELETE',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
}
);

let responseText = '';
try {
responseText = await response.text();
} catch (e) {
// 读取文本失败时不中断主流程
}

let isSuccess = false;

// 判断第一遍请求是否成功
if (response.status === 200) {
try {
const data = JSON.parse(responseText);
if (data?.success) {
isSuccess = true;
results.success.push(account_id);
console.log(`[成功] 账号 ${account_id} 已成功下车`);
}
} catch (e) {
// JSON 解析异常
}
}

// 若不成功,检查是否由于工作区(workspace)限制导致
if (!isSuccess) {
const isWorkspaceError = responseText.includes('Must use workspace account for this operation');

if (isWorkspaceError) {
console.log(`[检测到工作区限制] 账号 ${account_id} 需要切换空间 Token,正在换取...`);

try {
// 换取该空间的专有 token
const wsSessionRes = await fetch(
`https://chatgpt.com/api/auth/session?exchange_workspace_token=true&workspace_id=${account_id}&reason=setCurrentAccount`
);

if (wsSessionRes.ok) {
const wsSession = await wsSessionRes.json();
const wsAccessToken = wsSession?.accessToken;

if (wsAccessToken) {
console.log(`[成功] 已获取空间 ${account_id} 的临时 Token,开始重新尝试删除...`);

// 使用空间 token 重试删除请求
const retryResponse = await fetch(
`https://chatgpt.com/backend-api/accounts/${account_id}/users/${user_id}`,
{
method: 'DELETE',
headers: {
Authorization: `Bearer ${wsAccessToken}`,
'Content-Type': 'application/json',
},
}
);

let retryText = '';
try {
retryText = await retryResponse.text();
} catch (e) {}

if (retryResponse.status === 200) {
try {
const retryData = JSON.parse(retryText);
if (retryData?.success) {
isSuccess = true;
results.success.push(account_id);
console.log(`[成功] 切换临时 Token 后,账号 ${account_id} 下车成功`);
}
} catch (e) {}
}

if (!isSuccess) {
results.failed.push({ account_id, status: retryResponse.status, reason: '切换 Token 后重试失败', detail: retryText });
console.log(`[失败] 切换临时 Token 重试后依然失败,状态码: ${retryResponse.status}`);
}
} else {
results.failed.push({ account_id, reason: '空间 session 返回数据中未包含 accessToken' });
console.log(`[失败] 无法从空间 ${account_id} 的 session 响应中提取到 Token`);
}
} else {
results.failed.push({ account_id, status: wsSessionRes.status, reason: '请求空间 Token 接口失败' });
console.log(`[失败] 请求空间临时 Token 失败,状态码: ${wsSessionRes.status}`);
}
} catch (wsErr) {
results.failed.push({ account_id, reason: '获取空间 Token 过程中发生错误', error: wsErr.message });
console.log(`[错误] 获取账号 ${account_id} 空间 Token 时发生异常: ${wsErr.message}`);
}
} else {
// 非工作区错误,直接记录并跳过
results.failed.push({ account_id, status: response.status, detail: responseText });
console.log(`[跳过] 账号 ${account_id} 失败(非工作区错误),状态码: ${response.status}`);
}
}

} catch (err) {
// 捕获单次循环内的任何其他异常,确保主循环继续
results.failed.push({ account_id, error: err.message || String(err) });
console.log(`[错误] 处理账号 ${account_id} 时发生异常,已跳过并继续: ${err.message || err}`);
}
}

// 4. 输出汇总结果
console.log('\n========= 处理结果汇总 =========');
console.log(`成功下车数: ${results.success.length}`);
if (results.success.length > 0) {
console.log('成功账号 ID 列表:', results.success);
}
console.log(`失败/跳过数: ${results.failed.length}`);
if (results.failed.length > 0) {
console.log('失败详情列表:', results.failed);
}
console.log('=================================');

} catch (globalErr) {
console.error('脚本初始化阶段发生未捕获的错误:', globalErr);
}
})();
最新回复 (5)
  • ycoroy 07-07 01:09
    1

    比较好奇是否可以通过先退出再加入空间实现额度手动重置呢

  • Luminet 楼主 07-07 01:10
    2

    大概率不行吧,我觉得Codex后端计费绑定的是workspeace_id+user_id(user_id在所有空间都一致)

  • yxbj 07-07 01:12
    3

    以前到期的空间可以用这个退出么?


    我到期了好几个空间挂哪里进也进不去,退也退不了

  • dulirivps 07-07 01:29
    4

    为什么要退出 不退出又什么后果吗

  • fuduxixi 07-07 01:31
    5

    已经有别名帐号挂了 不止是空间无了 号给噶了

* 帖子来源Linux.do
返回