Переглянути джерело

Merge branch 'main' of http://gitlab.pubdata.cn/hlm/multi-platform-media-manage

# Conflicts:
#	server/src/scheduler/index.ts
Ethanfly 15 годин тому
батько
коміт
34f34b0471

+ 52 - 0
server/python/app.py

@@ -70,6 +70,7 @@ from flask_cors import CORS
 
 from platforms import get_publisher, PLATFORM_MAP
 from platforms.base import PublishParams
+from platforms.weixin import WeixinPublisher
 
 
 def parse_datetime(date_str: str):
@@ -1316,5 +1317,56 @@ def main():
     app.run(host=args.host, port=args.port, debug=True, threaded=True, use_reloader=False)
 
 
+@app.route('/auto-reply', methods=['POST'])
+def auto_reply():
+    """
+    微信视频号自动回复私信
+    POST /auto-reply
+    Body: {
+        "platform": "weixin",
+        "cookie": "..."
+    }
+    """
+    try:
+        data = request.json
+        platform = data.get('platform', '').lower()
+        cookie = data.get('cookie', '')
+        
+        if platform != 'weixin':
+            return jsonify({
+                'success': False,
+                'error': '只支持微信视频号平台'
+            }), 400
+        
+        if not cookie:
+            return jsonify({
+                'success': False,
+                'error': '缺少 Cookie'
+            }), 400
+        
+        print(f"[API] 接收自动回复请求: platform={platform}")
+        
+        # 创建 Publisher 实例
+        publisher = WeixinPublisher(headless=HEADLESS_MODE)
+        
+        # 执行自动回复
+        loop = asyncio.new_event_loop()
+        asyncio.set_event_loop(loop)
+        result = loop.run_until_complete(publisher.auto_reply_private_messages(cookie))
+        loop.close()
+        
+        print(f"[API] 自动回复结果: {result}")
+        
+        return jsonify(result)
+    
+    except Exception as e:
+        print(f"[API] 自动回复异常: {e}")
+        traceback.print_exc()
+        return jsonify({
+            'success': False,
+            'error': str(e)
+        }), 500
+
+
 if __name__ == '__main__':
     main()

BIN
server/python/platforms/__pycache__/weixin.cpython-313.pyc


Різницю між файлами не показано, бо вона завелика
+ 769 - 110
server/python/platforms/weixin.py


+ 85 - 1
server/src/scheduler/index.ts

@@ -21,7 +21,7 @@ export class TaskScheduler {
   private isDyImportRunning = false; // 抖音导入锁,防止任务重叠执行
   private isBjImportRunning = false; // 百家号导入锁,防止任务重叠执行
   private isWxImportRunning = false; // 视频号导入锁,防止任务重叠执行
-  
+  private isAutoReplying = false; // 私信回复锁,防止任务重叠执行
   /**
    * 启动调度器
    * 
@@ -48,6 +48,7 @@ export class TaskScheduler {
     // 每天 12:30:批量导出视频号“数据中心-各子菜单-增长详情(数据详情)-近30天-下载表格”,导入 user_day_statistics
     this.scheduleJob('wx-video-data-center-import', '30 12 * * *', this.importWeixinVideoDataCenterLast30Days.bind(this));
     
+    this.scheduleJob('auto-reply-messages', '* * * * *', this.autoReplyMessages.bind(this));
     // 注意:账号刷新由客户端定时触发,不在服务端自动执行
     // 这样可以确保只刷新当前登录用户的账号,避免处理其他用户的数据
     
@@ -60,6 +61,7 @@ export class TaskScheduler {
     logger.info('[Scheduler]   - dy-account-overview-import:  daily at 12:10 (10 12 * * *)');
     logger.info('[Scheduler]   - bj-content-overview-import: daily at 12:20 (20 12 * * *)');
     logger.info('[Scheduler]   - wx-video-data-center-import: daily at 12:30 (30 12 * * *)');
+    logger.info('[Scheduler]   - auto-reply-messages: every minute (* * * * *)');
     logger.info('[Scheduler] Note: Account refresh is triggered by client, not server');
     logger.info('[Scheduler] ========================================');
     
@@ -352,6 +354,88 @@ export class TaskScheduler {
       this.isDyImportRunning = false;
     }
   }
+  
+  /**
+   * 自动回复私信(每5分钟执行一次)
+   * 只处理微信视频号平台的账号
+   */
+  private async autoReplyMessages(): Promise<void> {
+    // 检查是否正在执行回复任务
+    if (this.isAutoReplying) {
+      logger.info('[Scheduler] Auto reply is already running, skipping this cycle...');
+      return;
+    }
+    
+    // 获取锁
+    this.isAutoReplying = true;
+    logger.debug('[Scheduler] Acquired auto reply lock');
+    
+    try {
+      const accountRepository = AppDataSource.getRepository(PlatformAccount);
+      
+      // 只获取微信视频号的活跃账号
+      const accounts = await accountRepository.find({
+        where: {
+          platform: 'weixin_video',
+          status: 'active',
+        },
+      });
+      
+      if (accounts.length === 0) {
+        logger.info('[Scheduler] No active weixin accounts for auto reply');
+        return;
+      }
+      
+      logger.info(`[Scheduler] Starting auto reply for ${accounts.length} weixin accounts...`);
+      
+      let successCount = 0;
+      let failCount = 0;
+      
+      // 为每个账号执行自动回复
+      for (const account of accounts) {
+        try {
+          logger.info(`[Scheduler] Auto replying for account: ${account.accountName} (${account.id})`);
+          
+          // 调用 Python 服务执行自动回复
+          const response = await fetch('http://localhost:5005/auto-reply', {
+            method: 'POST',
+            headers: {
+              'Content-Type': 'application/json',
+            },
+            body: JSON.stringify({
+              platform: 'weixin',
+              cookie: account.cookieData || '',
+            }),
+            signal: AbortSignal.timeout(120000), // 2分钟超时
+          });
+          
+          if (!response.ok) {
+            throw new Error(`HTTP ${response.status}`);
+          }
+          
+          const result = await response.json();
+          
+          if (result.success) {
+            successCount++;
+            logger.info(`[Scheduler] Auto reply success for ${account.accountName}: ${result.replied_count} messages`);
+          } else {
+            failCount++;
+            logger.error(`[Scheduler] Auto reply failed for ${account.accountName}: ${result.error}`);
+          }
+          
+        } catch (error) {
+          failCount++;
+          logger.error(`[Scheduler] Auto reply error for account ${account.id}:`, error);
+        }
+      }
+      
+      logger.info(`[Scheduler] Auto reply completed: ${successCount} success, ${failCount} failed`);
+    } finally {
+      // 释放锁
+      this.isAutoReplying = false;
+      logger.debug('[Scheduler] Released auto reply lock');
+    }
+  }
 
   /**
    * 百家号:内容分析-基础数据导出(近30天)→ 导入 user_day_statistics

Деякі файли не було показано, через те що забагато файлів було змінено