feat(desktop/weixin-gzh): drive editor to publish with draft fallback on verify
Replace the masssend HTTP path with a Playwright-driven editor flow that clicks "群发" and resolves the publish response, then polls appmsgpublish for the public mp.weixin.qq.com/s URL. When the dialog asks for admin verification / scan-code authorization, swallow it as a successful draft save and surface fallback_reason=publish_authorization_required so the operator can finish from the console. Also broaden token extraction in account-binder and treat the draft list URL (cgi-bin/appmsg) as the console landing page. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -249,7 +249,8 @@ const TOUTIAO_LOGIN_URL = "https://mp.toutiao.com/auth/page/login";
|
||||
const TOUTIAO_CONSOLE_HOME_URL = "https://mp.toutiao.com/profile_v4/index";
|
||||
const TOUTIAO_WORKBENCH_URL_PATTERN = /^https:\/\/mp\.toutiao\.com\/profile_v4\//i;
|
||||
const WEIXIN_GZH_HOME_URL = "https://mp.weixin.qq.com/";
|
||||
const WEIXIN_GZH_CONSOLE_HOME_URL = "https://mp.weixin.qq.com/cgi-bin/home";
|
||||
const WEIXIN_GZH_LOGIN_URL = "https://mp.weixin.qq.com/cgi-bin/loginpage";
|
||||
const WEIXIN_GZH_DRAFT_LIST_URL = "https://mp.weixin.qq.com/cgi-bin/appmsg";
|
||||
const BILIBILI_API_ORIGIN = "https://api.bilibili.com";
|
||||
const ZOL_API_ORIGIN = "https://open-api.zol.com.cn";
|
||||
const ZOL_REFERER = "https://post.zol.com.cn/";
|
||||
@@ -444,7 +445,33 @@ function extractToutiaoAccount(response: ToutiaoMediaInfoResponse | null | undef
|
||||
}
|
||||
|
||||
function extractWeixinGzhToken(html: string): string {
|
||||
return html.match(/data:\s*\{[\s\S]*?t:\s*["']([^"']+)["']/)?.[1] ?? "";
|
||||
const patterns = [
|
||||
/data:\s*\{[\s\S]*?t:\s*["']([^"']+)["']/,
|
||||
/(?:\?|&|&)token=(\d+)(?:&|&|$)/,
|
||||
/["']?token["']?\s*[:=]\s*["']?(\d+)["']?/,
|
||||
];
|
||||
|
||||
for (const pattern of patterns) {
|
||||
const token = html.match(pattern)?.[1]?.trim();
|
||||
if (token) {
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
function buildWeixinGzhDraftListURL(token?: string): string {
|
||||
const url = new URL(WEIXIN_GZH_DRAFT_LIST_URL);
|
||||
url.searchParams.set("begin", "0");
|
||||
url.searchParams.set("count", "10");
|
||||
url.searchParams.set("type", "77");
|
||||
url.searchParams.set("action", "list_card");
|
||||
if (token) {
|
||||
url.searchParams.set("token", token);
|
||||
}
|
||||
url.searchParams.set("lang", "zh_CN");
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
function isWeixinGzhLoggedOutText(text: string): boolean {
|
||||
@@ -504,14 +531,10 @@ async function resolveWeixinGzhConsoleURL(session: Session): Promise<string> {
|
||||
|
||||
const token = extractWeixinGzhToken(html);
|
||||
if (!token) {
|
||||
return WEIXIN_GZH_HOME_URL;
|
||||
return WEIXIN_GZH_LOGIN_URL;
|
||||
}
|
||||
|
||||
const url = new URL(WEIXIN_GZH_CONSOLE_HOME_URL);
|
||||
url.searchParams.set("t", "home/index");
|
||||
url.searchParams.set("lang", "zh_CN");
|
||||
url.searchParams.set("token", token);
|
||||
return url.toString();
|
||||
return buildWeixinGzhDraftListURL(token);
|
||||
}
|
||||
|
||||
async function resolvePublishAccountConsoleURL(
|
||||
@@ -3065,8 +3088,8 @@ const publishBindingDefinitions: Record<string, PublishPlatformBindingDefinition
|
||||
weixin_gzh: {
|
||||
id: "weixin_gzh",
|
||||
label: "微信公众号",
|
||||
loginUrl: "https://mp.weixin.qq.com/cgi-bin/loginpage",
|
||||
consoleUrl: WEIXIN_GZH_CONSOLE_HOME_URL,
|
||||
loginUrl: WEIXIN_GZH_LOGIN_URL,
|
||||
consoleUrl: buildWeixinGzhDraftListURL(),
|
||||
detect: detectWeixinGzh,
|
||||
},
|
||||
zol: {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user