Files
geo/apps/desktop-client/src/main/platform-auth-adapters.ts
T
root 8dcd60b604 fix(desktop/weixin-gzh): tolerate numeric appMsgId and classify login timeout
The MP draft API sometimes returns appMsgId as a number or under the
appmsgid alias; coerce both shapes before treating the response as a
success. Also extend the platform auth adapter to recognize 登录超时/
请重新登录 wording so the runtime drives the account back to re-auth.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 17:11:19 +08:00

391 lines
12 KiB
TypeScript

import { aiPlatformCatalog } from "@geo/shared-types";
import {
probePublishAccountSession,
silentRefreshPublishAccountSession,
type PublishAccountIdentity,
} from "./account-binder";
import type {
AuthProbeResult,
PlatformFailureClassification,
} from "./auth-types";
export interface PlatformFailureInput {
summary?: string | null;
error?: Record<string, unknown> | null;
message?: string | null;
}
export interface PlatformAdapter {
probe(account: PublishAccountIdentity, partition: string): Promise<AuthProbeResult>;
silentRefresh(account: PublishAccountIdentity, partition: string): Promise<boolean>;
classifyFailure(input: PlatformFailureInput): PlatformFailureClassification;
}
function normalizeFailureText(input: PlatformFailureInput): string {
const values = [
input.summary,
input.message,
typeof input.error?.code === "string" ? input.error.code : null,
typeof input.error?.reason_code === "string" ? input.error.reason_code : null,
typeof input.error?.message === "string" ? input.error.message : null,
typeof input.error?.detail === "string" ? input.error.detail : null,
typeof input.error?.page_error === "string" ? input.error.page_error : null,
typeof input.error?.verify_scene === "string" ? input.error.verify_scene : null,
];
return values
.filter((value): value is string => typeof value === "string" && Boolean(value.trim()))
.join(" | ")
.toLowerCase();
}
function classifyFailure(input: PlatformFailureInput): PlatformFailureClassification {
const text = normalizeFailureText(input);
if (!text) {
return "ok";
}
if (
/(verify_scene|verification required|captcha|challenge|验证码|滑块|人机验证|安全验证|请完成验证|短信验证|扫码验证)/i.test(text)
) {
return "challenge";
}
if (
/(not_logged_in|unauthorized|unauthenticated|login redirect|login required|signin|sign in|expired|401|登录态失效|登录超时|请重新登录|请先登录|未登录)/i
.test(text)
) {
return "auth_failure";
}
if (
/(network|timeout|timed out|failed to fetch|econnreset|econnrefused|enotfound|dns|socket hang up|502|503|504)/i
.test(text)
) {
return "network";
}
return "ok";
}
function classifyDoubaoFailure(input: PlatformFailureInput): PlatformFailureClassification {
const text = normalizeFailureText(input);
if (!text) {
return "ok";
}
if (
/(rate limited|rate limit|too many requests|request limit|frequency limit|请求过于频繁|频率过快|访问过于频繁|限流|风控|服务繁忙|请稍后|稍后再试)/i
.test(text)
) {
return "risk_control";
}
return classifyFailure(input);
}
function classifyWenxinFailure(input: PlatformFailureInput): PlatformFailureClassification {
const text = normalizeFailureText(input);
if (!text) {
return "ok";
}
if (
/(当前访问环境存在异常|访问环境存在异常|环境存在异常|更换浏览器再尝试提问|当前环境异常|访问受限|环境异常)/i
.test(text)
) {
return "risk_control";
}
return classifyFailure(input);
}
function classifyBaijiahaoFailure(input: PlatformFailureInput): PlatformFailureClassification {
const code = typeof input.error?.code === "string" ? input.error.code : "";
if (code === "baijiahao_challenge_required") {
return "challenge";
}
if (code === "baijiahao_not_logged_in" || code === "baijiahao_token_missing") {
return "auth_failure";
}
if (code.startsWith("baijiahao_")) {
return "ok";
}
return classifyFailure(input);
}
function classifySohuhaoFailure(input: PlatformFailureInput): PlatformFailureClassification {
const code = typeof input.error?.code === "string" ? input.error.code : "";
if (code === "sohuhao_not_logged_in") {
return "auth_failure";
}
if (code === "sohuhao_challenge_required") {
return "challenge";
}
if (code.startsWith("sohuhao_")) {
return "ok";
}
return classifyFailure(input);
}
function classifyQiehaoFailure(input: PlatformFailureInput): PlatformFailureClassification {
const code = typeof input.error?.code === "string" ? input.error.code : "";
if (code === "qiehao_challenge_required") {
return "challenge";
}
if (code === "qiehao_not_logged_in" || code === "qiehao_cookie_missing") {
return "auth_failure";
}
if (code.startsWith("qiehao_")) {
return "ok";
}
return classifyFailure(input);
}
function classifyBilibiliFailure(input: PlatformFailureInput): PlatformFailureClassification {
const code = typeof input.error?.code === "string" ? input.error.code : "";
if (code === "bilibili_not_logged_in" || code === "bilibili_csrf_missing") {
return "auth_failure";
}
if (code.startsWith("bilibili_")) {
return "ok";
}
return classifyFailure(input);
}
function classifyJuejinFailure(input: PlatformFailureInput): PlatformFailureClassification {
const code = typeof input.error?.code === "string" ? input.error.code : "";
if (code === "juejin_not_logged_in") {
return "auth_failure";
}
if (code.startsWith("juejin_")) {
return "ok";
}
return classifyFailure(input);
}
function classifyWangyihaoFailure(input: PlatformFailureInput): PlatformFailureClassification {
const code = typeof input.error?.code === "string" ? input.error.code : "";
if (code === "wangyihao_not_logged_in" || code === "wangyihao_token_missing") {
return "auth_failure";
}
if (code === "wangyihao_challenge_required") {
return "challenge";
}
if (code.startsWith("wangyihao_")) {
return "ok";
}
return classifyFailure(input);
}
function classifyWeixinGzhFailure(input: PlatformFailureInput): PlatformFailureClassification {
const code = typeof input.error?.code === "string" ? input.error.code : "";
if (code === "weixin_gzh_not_logged_in") {
return "auth_failure";
}
if (code === "weixin_gzh_challenge_required") {
return "challenge";
}
if (code.startsWith("weixin_gzh_")) {
return "ok";
}
return classifyFailure(input);
}
function classifyZolFailure(input: PlatformFailureInput): PlatformFailureClassification {
const code = typeof input.error?.code === "string" ? input.error.code : "";
if (code === "zol_not_logged_in") {
return "auth_failure";
}
if (code === "zol_challenge_required") {
return "challenge";
}
if (code.startsWith("zol_")) {
return "ok";
}
return classifyFailure(input);
}
function classifyDongchediFailure(input: PlatformFailureInput): PlatformFailureClassification {
const code = typeof input.error?.code === "string" ? input.error.code : "";
if (code === "dongchedi_not_logged_in") {
return "auth_failure";
}
if (code === "dongchedi_challenge_required") {
return "challenge";
}
if (code.startsWith("dongchedi_")) {
return "ok";
}
return classifyFailure(input);
}
const genericAdapter: PlatformAdapter = {
async probe(account, partition) {
return await probePublishAccountSession(account, partition);
},
async silentRefresh(account, partition) {
return await silentRefreshPublishAccountSession(account, partition);
},
classifyFailure,
};
const doubaoAdapter: PlatformAdapter = {
async probe(account, partition) {
return await probePublishAccountSession(account, partition);
},
async silentRefresh(account, partition) {
return await silentRefreshPublishAccountSession(account, partition);
},
classifyFailure: classifyDoubaoFailure,
};
const wenxinAdapter: PlatformAdapter = {
async probe(account, partition) {
return await probePublishAccountSession(account, partition);
},
async silentRefresh(account, partition) {
return await silentRefreshPublishAccountSession(account, partition);
},
classifyFailure: classifyWenxinFailure,
};
const baijiahaoAdapter: PlatformAdapter = {
async probe(account, partition) {
return await probePublishAccountSession(account, partition);
},
async silentRefresh(account, partition) {
return await silentRefreshPublishAccountSession(account, partition);
},
classifyFailure: classifyBaijiahaoFailure,
};
const sohuhaoAdapter: PlatformAdapter = {
async probe(account, partition) {
return await probePublishAccountSession(account, partition);
},
async silentRefresh(account, partition) {
return await silentRefreshPublishAccountSession(account, partition);
},
classifyFailure: classifySohuhaoFailure,
};
const qiehaoAdapter: PlatformAdapter = {
async probe(account, partition) {
return await probePublishAccountSession(account, partition);
},
async silentRefresh(account, partition) {
return await silentRefreshPublishAccountSession(account, partition);
},
classifyFailure: classifyQiehaoFailure,
};
const bilibiliAdapter: PlatformAdapter = {
async probe(account, partition) {
return await probePublishAccountSession(account, partition);
},
async silentRefresh(account, partition) {
return await silentRefreshPublishAccountSession(account, partition);
},
classifyFailure: classifyBilibiliFailure,
};
const wangyihaoAdapter: PlatformAdapter = {
async probe(account, partition) {
return await probePublishAccountSession(account, partition);
},
async silentRefresh(account, partition) {
return await silentRefreshPublishAccountSession(account, partition);
},
classifyFailure: classifyWangyihaoFailure,
};
const juejinAdapter: PlatformAdapter = {
async probe(account, partition) {
return await probePublishAccountSession(account, partition);
},
async silentRefresh(account, partition) {
return await silentRefreshPublishAccountSession(account, partition);
},
classifyFailure: classifyJuejinFailure,
};
const weixinGzhAdapter: PlatformAdapter = {
async probe(account, partition) {
return await probePublishAccountSession(account, partition);
},
async silentRefresh(account, partition) {
return await silentRefreshPublishAccountSession(account, partition);
},
classifyFailure: classifyWeixinGzhFailure,
};
const zolAdapter: PlatformAdapter = {
async probe(account, partition) {
return await probePublishAccountSession(account, partition);
},
async silentRefresh(account, partition) {
return await silentRefreshPublishAccountSession(account, partition);
},
classifyFailure: classifyZolFailure,
};
const dongchediAdapter: PlatformAdapter = {
async probe(account, partition) {
return await probePublishAccountSession(account, partition);
},
async silentRefresh(account, partition) {
return await silentRefreshPublishAccountSession(account, partition);
},
classifyFailure: classifyDongchediFailure,
};
const adapterRegistry = new Map<string, PlatformAdapter>(
[
...aiPlatformCatalog.map((platform) => platform.id),
"toutiaohao",
"baijiahao",
"sohuhao",
"qiehao",
"zhihu",
"wangyihao",
"jianshu",
"bilibili",
"juejin",
"smzdm",
"weixin_gzh",
"zol",
"dongchedi",
].map((platform) => [
platform,
platform === "doubao"
? doubaoAdapter
: platform === "wenxin"
? wenxinAdapter
: platform === "baijiahao"
? baijiahaoAdapter
: platform === "sohuhao"
? sohuhaoAdapter
: platform === "qiehao"
? qiehaoAdapter
: platform === "wangyihao"
? wangyihaoAdapter
: platform === "bilibili"
? bilibiliAdapter
: platform === "juejin"
? juejinAdapter
: platform === "weixin_gzh"
? weixinGzhAdapter
: platform === "zol"
? zolAdapter
: platform === "dongchedi"
? dongchediAdapter
: genericAdapter,
]),
);
export function getPlatformAdapter(platformId: string): PlatformAdapter {
return adapterRegistry.get(platformId) ?? genericAdapter;
}