Przeglądaj źródła

feat: enhance BaijiahaoAdapter to handle modal dialogs and improve description input handling

ethanfly 3 tygodni temu
rodzic
commit
02253412d8
2 zmienionych plików z 103 dodań i 204 usunięć
  1. 21 190
      .omc/project-memory.json
  2. 82 14
      server/src/automation/platforms/baijiahao.ts

+ 21 - 190
.omc/project-memory.json

@@ -1,7 +1,7 @@
 {
   "version": "1.0.0",
-  "lastScanned": 1777429590245,
-  "projectRoot": "C:\\workspace\\multi-platform-media-manage",
+  "lastScanned": 1778574570448,
+  "projectRoot": "c:\\workspace\\multi-platform-media-manage",
   "techStack": {
     "languages": [
       {
@@ -56,7 +56,7 @@
       "path": "client",
       "purpose": null,
       "fileCount": 5,
-      "lastAccessed": 1777429590213,
+      "lastAccessed": 1778574570403,
       "keyFiles": [
         "electron-builder.json",
         "index.html",
@@ -69,7 +69,7 @@
       "path": "database",
       "purpose": null,
       "fileCount": 1,
-      "lastAccessed": 1777429590214,
+      "lastAccessed": 1778574570404,
       "keyFiles": [
         "schema.sql"
       ]
@@ -78,7 +78,7 @@
       "path": "docs",
       "purpose": "Documentation",
       "fileCount": 18,
-      "lastAccessed": 1777429590221,
+      "lastAccessed": 1778574570410,
       "keyFiles": [
         "baijiahao-api-complete.md",
         "baijiahao-api-debug.md",
@@ -91,7 +91,7 @@
       "path": "minimax-output",
       "purpose": null,
       "fileCount": 27,
-      "lastAccessed": 1777429590222,
+      "lastAccessed": 1778574570411,
       "keyFiles": [
         "accounts-layout-fix.png",
         "accounts-layout-hover-fix.png",
@@ -104,7 +104,7 @@
       "path": "scripts",
       "purpose": "Build/utility scripts",
       "fileCount": 7,
-      "lastAccessed": 1777429590222,
+      "lastAccessed": 1778574570412,
       "keyFiles": [
         "capture-ui-screenshots.mjs",
         "check-captcha-image-src.mjs",
@@ -117,7 +117,7 @@
       "path": "server",
       "purpose": null,
       "fileCount": 11,
-      "lastAccessed": 1777429590222,
+      "lastAccessed": 1778574570413,
       "keyFiles": [
         "docker-compose.yml",
         "Dockerfile",
@@ -130,7 +130,7 @@
       "path": "shared",
       "purpose": null,
       "fileCount": 2,
-      "lastAccessed": 1777429590223,
+      "lastAccessed": 1778574570413,
       "keyFiles": [
         "package.json",
         "tsconfig.json"
@@ -140,14 +140,14 @@
       "path": "uploads",
       "purpose": null,
       "fileCount": 0,
-      "lastAccessed": 1777429590223,
+      "lastAccessed": 1778574570414,
       "keyFiles": []
     },
     "client\\build": {
       "path": "client\\build",
       "purpose": "Build output",
       "fileCount": 1,
-      "lastAccessed": 1777429590224,
+      "lastAccessed": 1778574570414,
       "keyFiles": [
         "icon.png"
       ]
@@ -156,7 +156,7 @@
       "path": "client\\dist",
       "purpose": "Distribution/build output",
       "fileCount": 3,
-      "lastAccessed": 1777429590224,
+      "lastAccessed": 1778574570415,
       "keyFiles": [
         "favicon.svg",
         "index.html",
@@ -167,14 +167,14 @@
       "path": "client\\node_modules",
       "purpose": "Dependencies",
       "fileCount": 0,
-      "lastAccessed": 1777429590225,
+      "lastAccessed": 1778574570415,
       "keyFiles": []
     },
     "client\\public": {
       "path": "client\\public",
       "purpose": "Public files",
       "fileCount": 2,
-      "lastAccessed": 1777429590225,
+      "lastAccessed": 1778574570416,
       "keyFiles": [
         "favicon.svg",
         "tray-icon.svg"
@@ -184,7 +184,7 @@
       "path": "database\\migrations",
       "purpose": "Database migrations",
       "fileCount": 20,
-      "lastAccessed": 1777429590225,
+      "lastAccessed": 1778574570416,
       "keyFiles": [
         "add_exposure_count_to_user_day_statistics.sql",
         "add_fields_to_user_day_statistics.sql",
@@ -195,7 +195,7 @@
       "path": "server\\dist",
       "purpose": "Distribution/build output",
       "fileCount": 4,
-      "lastAccessed": 1777429590227,
+      "lastAccessed": 1778574570420,
       "keyFiles": [
         "app.d.ts",
         "app.d.ts.map",
@@ -206,14 +206,14 @@
       "path": "server\\node_modules",
       "purpose": "Dependencies",
       "fileCount": 0,
-      "lastAccessed": 1777429590227,
+      "lastAccessed": 1778574570421,
       "keyFiles": []
     },
     "server\\src": {
       "path": "server\\src",
       "purpose": "Source code",
       "fileCount": 1,
-      "lastAccessed": 1777429590227,
+      "lastAccessed": 1778574570421,
       "keyFiles": [
         "app.ts"
       ]
@@ -222,7 +222,7 @@
       "path": "shared\\dist",
       "purpose": "Distribution/build output",
       "fileCount": 4,
-      "lastAccessed": 1777429590228,
+      "lastAccessed": 1778574570431,
       "keyFiles": [
         "index.d.ts",
         "index.d.ts.map",
@@ -233,181 +233,12 @@
       "path": "shared\\src",
       "purpose": "Source code",
       "fileCount": 1,
-      "lastAccessed": 1777429590228,
+      "lastAccessed": 1778574570433,
       "keyFiles": [
         "index.ts"
       ]
     }
   },
-  "hotPaths": [
-    {
-      "path": "server\\src\\services\\HeadlessBrowserService.ts",
-      "accessCount": 56,
-      "lastAccessed": 1777431958453,
-      "type": "file"
-    },
-    {
-      "path": "server\\src",
-      "accessCount": 9,
-      "lastAccessed": 1777431605918,
-      "type": "directory"
-    },
-    {
-      "path": "server\\src\\services\\login\\BaseLoginService.ts",
-      "accessCount": 6,
-      "lastAccessed": 1777430456122,
-      "type": "file"
-    },
-    {
-      "path": "server\\src\\automation\\browser.ts",
-      "accessCount": 4,
-      "lastAccessed": 1777430493051,
-      "type": "file"
-    },
-    {
-      "path": "server\\src\\app.ts",
-      "accessCount": 4,
-      "lastAccessed": 1777431578166,
-      "type": "file"
-    },
-    {
-      "path": "server\\src\\utils\\logger.ts",
-      "accessCount": 3,
-      "lastAccessed": 1777431717984,
-      "type": "file"
-    },
-    {
-      "path": "",
-      "accessCount": 2,
-      "lastAccessed": 1777429637491,
-      "type": "directory"
-    },
-    {
-      "path": "server\\src\\websocket\\index.ts",
-      "accessCount": 2,
-      "lastAccessed": 1777429869955,
-      "type": "file"
-    },
-    {
-      "path": "server\\src\\services\\TaskQueueService.ts",
-      "accessCount": 2,
-      "lastAccessed": 1777429869968,
-      "type": "file"
-    },
-    {
-      "path": "server\\src\\scheduler\\index.ts",
-      "accessCount": 2,
-      "lastAccessed": 1777429881860,
-      "type": "file"
-    },
-    {
-      "path": "server\\src\\services\\login\\LoginServiceManager.ts",
-      "accessCount": 2,
-      "lastAccessed": 1777429939199,
-      "type": "file"
-    },
-    {
-      "path": "package.json",
-      "accessCount": 1,
-      "lastAccessed": 1777429621794,
-      "type": "file"
-    },
-    {
-      "path": "server\\package.json",
-      "accessCount": 1,
-      "lastAccessed": 1777429621848,
-      "type": "file"
-    },
-    {
-      "path": "server\\src\\services\\taskExecutors.ts",
-      "accessCount": 1,
-      "lastAccessed": 1777429650571,
-      "type": "file"
-    },
-    {
-      "path": "server\\src\\config\\redis.ts",
-      "accessCount": 1,
-      "lastAccessed": 1777429661380,
-      "type": "file"
-    },
-    {
-      "path": "server\\src\\automation\\platforms\\index.ts",
-      "accessCount": 1,
-      "lastAccessed": 1777429661434,
-      "type": "file"
-    },
-    {
-      "path": "server\\src\\routes\\index.ts",
-      "accessCount": 1,
-      "lastAccessed": 1777429661472,
-      "type": "file"
-    },
-    {
-      "path": "server\\src\\models\\index.ts",
-      "accessCount": 1,
-      "lastAccessed": 1777429661478,
-      "type": "file"
-    },
-    {
-      "path": "server\\src\\automation\\cookie.ts",
-      "accessCount": 1,
-      "lastAccessed": 1777429661559,
-      "type": "file"
-    },
-    {
-      "path": "server\\src\\ai\\index.ts",
-      "accessCount": 1,
-      "lastAccessed": 1777429661730,
-      "type": "file"
-    },
-    {
-      "path": "server\\src\\config\\index.ts",
-      "accessCount": 1,
-      "lastAccessed": 1777429684839,
-      "type": "file"
-    },
-    {
-      "path": "server\\src\\utils\\workCoverCache.ts",
-      "accessCount": 1,
-      "lastAccessed": 1777429684857,
-      "type": "file"
-    },
-    {
-      "path": "server\\src\\services\\login\\index.ts",
-      "accessCount": 1,
-      "lastAccessed": 1777429684903,
-      "type": "file"
-    },
-    {
-      "path": "server\\src\\middleware\\error.ts",
-      "accessCount": 1,
-      "lastAccessed": 1777429684916,
-      "type": "file"
-    },
-    {
-      "path": "server\\src\\middleware\\auth.ts",
-      "accessCount": 1,
-      "lastAccessed": 1777429684977,
-      "type": "file"
-    },
-    {
-      "path": "server\\src\\utils\\platformWorkCover.ts",
-      "accessCount": 1,
-      "lastAccessed": 1777429695591,
-      "type": "file"
-    },
-    {
-      "path": "server\\src\\services",
-      "accessCount": 1,
-      "lastAccessed": 1777429695668,
-      "type": "directory"
-    },
-    {
-      "path": "server\\src\\services\\BrowserLoginService.ts",
-      "accessCount": 1,
-      "lastAccessed": 1777429939210,
-      "type": "file"
-    }
-  ],
+  "hotPaths": [],
   "userDirectives": []
 }

+ 82 - 14
server/src/automation/platforms/baijiahao.ts

@@ -362,6 +362,7 @@ export class BaijiahaoAdapter extends BasePlatformAdapter {
       return { status: 'failed', message: promptText };
     }
 
+    // 尝试点击确认类按钮继续发布
     const confirmSelector = await this.clickFirstVisible([
       'button:has-text("确认发布")',
       'button:has-text("继续发布")',
@@ -377,6 +378,40 @@ export class BaijiahaoAdapter extends BasePlatformAdapter {
       return { status: 'continued', message: promptText };
     }
 
+    // 无确认按钮时,尝试关闭/取消弹窗(活动通知、提示等),不要直接报错
+    const dismissSelector = await this.clickFirstVisible([
+      'button:has-text("关闭")',
+      'button:has-text("取消")',
+      'button:has-text("暂不")',
+      'button:has-text("暂不开启")',
+      'button:has-text("以后再说")',
+      'button:has-text("不了")',
+      'button:has-text("不用了")',
+      '.Dialog-close',
+      '.modal-close',
+      '[class*="dialog"] [class*="close"]',
+      '[class*="modal"] [class*="close"]',
+      '[aria-label="关闭"]',
+      '[aria-label="close"]',
+      '.ant-modal-close',
+      '.close-btn',
+    ]);
+
+    if (dismissSelector) {
+      logger.info(`[Baijiahao Publish] Dismissed benign dialog via ${dismissSelector}: ${promptText}`);
+      return { status: 'continued', message: promptText };
+    }
+
+    // 最后尝试 ESC 键关闭
+    const hasModal = await this.page!.locator('[class*="dialog"], [class*="modal"], [role="dialog"]').count();
+    if (hasModal > 0) {
+      logger.info(`[Baijiahao Publish] Trying ESC to dismiss dialog: ${promptText}`);
+      await this.page!.keyboard.press('Escape');
+      await this.page!.waitForTimeout(500);
+      return { status: 'continued', message: promptText };
+    }
+
+    // 对话框无法自动关闭,才标记为需要手动处理
     return { status: 'failed', message: `发布页面提示需要手动处理:${promptText}` };
   }
 
@@ -765,20 +800,53 @@ export class BaijiahaoAdapter extends BasePlatformAdapter {
         for (const frame of frames) {
           if (descFilled) break;
           try {
-            const descInput = frame.locator(
-              'textarea[placeholder*="简介"], textarea[placeholder*="描述"], ' +
-              'textarea[placeholder*="介绍"], textarea[placeholder*="说明"], ' +
-              '[class*="desc"] textarea, [class*="intro"] textarea, ' +
-              '[class*="content"] textarea, [class*="editor"] textarea, ' +
-              'textarea[name*="desc"], textarea[name*="description"], ' +
-              'textarea[name*="intro"], textarea[name*="content"], ' +
-              'div[contenteditable="true"], ' +
-              '[data-placeholder*="简介"], [data-placeholder*="描述"]'
-            ).first();
-            if (await descInput.count() > 0 && await descInput.isVisible().catch(() => false)) {
-              await descInput.fill(params.description);
-              descFilled = true;
-              logger.info(`[Baijiahao Publish] Description filled in frame: ${frame.url()}`);
+            // 按优先级排列选择器:textarea(最可靠)→ contenteditable div → data 属性
+            const descSelectors = [
+              // 百家号视频简介专属选择器
+              'textarea[placeholder*="简介"]',
+              'textarea[placeholder*="描述"]',
+              'textarea[placeholder*="介绍"]',
+              'textarea[placeholder*="说明"]',
+              // 按 name 属性匹配
+              'textarea[name*="desc"]',
+              'textarea[name*="description"]',
+              'textarea[name*="intro"]',
+              'textarea[name*="content"]',
+              // 按 class 匹配 textarea
+              '[class*="desc"] textarea',
+              '[class*="intro"] textarea',
+              '[class*="content"] textarea',
+              '[class*="editor"] textarea',
+              // data-placeholder(百家号可能使用)
+              '[data-placeholder*="简介"]',
+              '[data-placeholder*="描述"]',
+              // 最后才尝试 contenteditable(避免误匹配到其他可编辑区域)
+              '[class*="editor"] div[contenteditable="true"]',
+              '[class*="desc"] div[contenteditable="true"]',
+              '[class*="content"] div[contenteditable="true"]',
+              'div[contenteditable="true"][data-placeholder]',
+            ];
+
+            let matchedSelector = '';
+            for (const selector of descSelectors) {
+              try {
+                const el = frame.locator(selector).first();
+                if (await el.count() > 0 && await el.isVisible().catch(() => false)) {
+                  // 先清空再填充,确保替换默认占位文字
+                  await el.click({ timeout: 3000 });
+                  await el.fill('');
+                  await el.fill(params.description);
+                  descFilled = true;
+                  matchedSelector = selector;
+                  break;
+                }
+              } catch {
+                continue;
+              }
+            }
+
+            if (descFilled) {
+              logger.info(`[Baijiahao Publish] Description filled via "${matchedSelector}" in frame: ${frame.url()}`);
             }
           } catch { /* continue to next frame */ }
         }