| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- /**
- * 百家号登录服务
- * @module services/login/BaijiahaoLoginService
- *
- * 登录流程(按用户要求):
- * 1. 打开 https://baijiahao.baidu.com/builder/theme/bjh/login
- * 2. 等待用户扫码登录,跳转到 /builder/rc/home 或 AI 识别已登录
- * 3. 监听 API /builder/app/appinfo 获取:头像(user.avatar)、昵称(user.name)、百家号ID(user.app_id)
- * 4. 监听 API /cms-ui/rights/growth/get_info 获取:粉丝数(total_fans)
- * 5. 跳转到 https://baijiahao.baidu.com/builder/rc/content 作品管理页
- * 6. 监听 API /pcui/article/lists 获取作品列表,取 list 数量作为作品数
- * 7. 完成后发送事件,账号ID使用 bjh_ 前缀
- */
- import { logger } from '../../utils/logger.js';
- import { BaseLoginService, type ApiInterceptConfig } from './BaseLoginService.js';
- import type { AccountInfo, LoginSession } from './types.js';
- export class BaijiahaoLoginService extends BaseLoginService {
- constructor() {
- super({
- platform: 'baijiahao',
- displayName: '百家号',
- loginUrl: 'https://baijiahao.baidu.com/builder/theme/bjh/login',
- successIndicators: ['/builder/rc/home', 'baijiahao.baidu.com/builder/rc'],
- cookieDomain: '.baidu.com',
- accountIdPrefix: 'bjh_',
- });
- }
- /**
- * API 拦截配置
- */
- protected override getApiInterceptConfigs(): ApiInterceptConfig[] {
- return [
- // 应用信息接口 - 获取头像、昵称、app_id
- {
- urlPattern: '/builder/app/appinfo',
- dataKey: 'appInfo',
- handler: (data: any) => {
- const user = data.data?.user || data.user || {};
- return {
- appId: user.app_id,
- name: user.name,
- avatar: user.avatar?.startsWith('http') ? user.avatar : `https:${user.avatar}`,
- userId: user.userid,
- };
- },
- },
- // 成长信息接口 - 获取粉丝数
- {
- urlPattern: '/cms-ui/rights/growth/get_info',
- dataKey: 'growthInfo',
- handler: (data: any) => ({
- totalFans: data.data?.total_fans || data.total_fans || 0,
- }),
- },
- // 文章列表接口 - 获取作品数
- {
- urlPattern: '/pcui/article/lists',
- dataKey: 'articleList',
- handler: (data: any) => {
- // 处理分散的响应格式
- const dataList = data.data?.article_list || data.data?.list || data.list || [];
- return { list: dataList, count: dataList.length };
- },
- },
- ];
- }
- /**
- * 收集账号信息
- */
- protected override async collectAccountInfo(session: LoginSession): Promise<AccountInfo | null> {
- try {
- // 关键步骤:确保在主页面(首页)获取完整Cookie上下文
- logger.info('[百家号] 确保在主页面获取完整Cookie...');
- const currentUrl = session.page.url();
- if (!currentUrl.includes('/builder/rc/home')) {
- logger.info(`[百家号] 当前不在主页: ${currentUrl},跳转到主页...`);
- await session.page.goto('https://baijiahao.baidu.com/builder/rc/home', {
- waitUntil: 'domcontentloaded',
- timeout: 30000
- });
- await new Promise(resolve => setTimeout(resolve, 3000)); // 等待页面完全加载
- }
- // 步骤3: 等待 appinfo API
- logger.info('[百家号] 等待 appinfo API...');
- let appInfo = await this.waitForApiData(session, 'appInfo', 10000);
- if (!appInfo) {
- logger.info('[百家号] 未拿到 appinfo,刷新页面重试...');
- await session.page.reload({ waitUntil: 'domcontentloaded' });
- await new Promise(resolve => setTimeout(resolve, 3000)); // 增加等待时间
- appInfo = await this.waitForApiData(session, 'appInfo', 15000);
- }
- if (!appInfo?.appId) {
- logger.error('[百家号] 无法获取百家号ID');
- return null;
- }
- logger.info('[百家号] 基本信息:', appInfo);
- // 步骤4: 等待 growth 信息(可能首页已触发)
- logger.info('[百家号] 等待粉丝数据...');
- const growthInfo = await this.waitForApiData(session, 'growthInfo', 5000);
- const fansCount = growthInfo?.totalFans || 0;
- logger.info(`[百家号] 粉丝数: ${fansCount}`);
- // 步骤5+6: 跳转到作品管理页,等待文章列表 API
- logger.info('[百家号] 跳转到作品管理页...');
- const contentUrl = 'https://baijiahao.baidu.com/builder/rc/content';
-
- // 尝试多次获取作品列表(百家号可能有分散认证问题)
- let articleData = null;
- for (let attempt = 1; attempt <= 3; attempt++) {
- try {
- logger.info(`[百家号] 获取作品列表(第${attempt}次尝试)...`);
- articleData = await this.navigateAndWaitForApi(session, contentUrl, 'articleList', 15000);
-
- if (articleData) {
- break;
- }
-
- if (attempt < 3) {
- logger.info(`[百家号] 第${attempt}次尝试失败,等待${attempt * 2}秒后重试...`);
- await new Promise(resolve => setTimeout(resolve, attempt * 2000));
- }
- } catch (error) {
- logger.warn(`[百家号] 第${attempt}次尝试获取作品列表失败:`, error);
- if (attempt < 3) {
- await new Promise(resolve => setTimeout(resolve, attempt * 2000));
- }
- }
- }
- const worksCount = articleData?.count || 0;
- logger.info(`[百家号] 作品数: ${worksCount}`);
- // 步骤7: 组装账号信息,使用 bjh_ 前缀
- return {
- accountId: `bjh_${appInfo.appId}`,
- accountName: appInfo.name || '百家号用户',
- avatarUrl: appInfo.avatar || '',
- fansCount,
- worksCount,
- };
- } catch (error) {
- logger.error('[百家号] 收集账号信息失败:', error);
- return null;
- }
- }
- }
- // 导出单例
- export const baijiahaoLoginService = new BaijiahaoLoginService();
|