904118851 преди 7 месеца
родител
ревизия
07bd2c2d56
променени са 40 файла, в които са добавени 1812 реда и са изтрити 675 реда
  1. 31 1
      0_RECEIVE_FACTORY/RECEIVE_FACTORY.js
  2. 42 41
      1_FILTER_FACTORY/FILTER_FACTORY.js
  3. 1 2
      2_PRODUCT_FACTORY/dz_batch_factory.js
  4. 0 1
      2_PRODUCT_FACTORY/fq_batch_factory.js
  5. 0 1
      2_PRODUCT_FACTORY/fq_mf_factory.js
  6. 1 2
      2_PRODUCT_FACTORY/hy_batch_factory.js
  7. 11 13
      2_PRODUCT_FACTORY/qm_batch_factory.js
  8. 9 7
      2_PRODUCT_FACTORY/yw_batch_factory.js
  9. 318 0
      2_PRODUCT_FACTORY/zh_batch_factory.js
  10. 9 2
      5_CREATE_LINK_FACTORY/yw_create_link_factory.js
  11. 100 0
      5_CREATE_LINK_FACTORY/zh_create_link.js
  12. 190 0
      5_CREATE_LINK_FACTORY/zh_create_link_factory.js
  13. 21 0
      MESSAGE_DISPATCH/MESSAGE_DISPATCH.js
  14. 25 0
      MESSAGE_DISPATCH/ocr_script.py
  15. 0 2
      PULL_DATA_FACTORY/PULL_DATA.js
  16. 4 5
      WX_GUA_JIAN_PARSE/WX_GUA_JIAN_PARSE.js
  17. 1 1
      WX_GUA_JIAN_PARSE/parse_wx_guajian.js
  18. BIN
      dump.rdb
  19. 2 1
      etc/config.json
  20. 229 578
      geetest_crack.py
  21. 5 4
      logs/combined.log
  22. 5 4
      logs/out.log
  23. 1 0
      node_modules/.bin/prebuild-install
  24. 1 0
      node_modules/.bin/rc
  25. 28 0
      pm2createlink.json
  26. 14 0
      pm2json.json
  27. BIN
      src/api/hy/qr.gif
  28. BIN
      src/api/hy/qr_resized.gif
  29. BIN
      src/api/yw/0.png
  30. BIN
      src/api/yw/1.png
  31. 15 0
      src/api/yw/tdc.js
  32. 105 0
      src/api/yw/yw_login.js
  33. 9 8
      src/api/yw/yw_search_book.js
  34. 46 0
      src/api/zh/get_zh_call_back_template.js
  35. 47 0
      src/api/zh/get_zh_recharge_template.js
  36. 52 0
      src/api/zh/zh_search_book.js
  37. 56 0
      src/helper.js
  38. 379 0
      test.py
  39. 5 2
      tg_factory_main.js
  40. 50 0
      tools.js

+ 31 - 1
0_RECEIVE_FACTORY/RECEIVE_FACTORY.js

@@ -269,6 +269,29 @@ CMD.get_yw_recharge_template = async function(res,yw_id) {
     res.end(JSON.stringify({ message:"成功!",data:data,code:10000}));
 }
 
+CMD.get_yw_open_sessid_status = async function(res) {
+    let OPENSESSID = await redis_help.getKeyValue("OPENSESSID")
+    console.log('get_yw_open_sessid_status:', OPENSESSID);
+    res.writeHead(200, {'Content-Type': 'application/json'});
+    res.end(JSON.stringify({ message:"成功!",data:OPENSESSID,code:10000}));
+}
+
+
+CMD.get_zh_callback_template = async function(res) {
+    let data = await require('../src/api/zh/get_zh_call_back_template').get_zh_call_back_template()
+    console.log('get_zh_callback_template:', data);
+    res.writeHead(200, {'Content-Type': 'application/json'});
+    res.end(JSON.stringify({ message:"成功!",data:data,code:10000}));
+ }
+ 
+ CMD.get_zh_recharge_template = async function(res,app_type) {
+    let data = await require('../src/api/zh/get_zh_recharge_template').get_zh_recharge_template(app_type)
+    console.log('get_zh_recharge_template:', data);
+    res.writeHead(200, {'Content-Type': 'application/json'});
+    res.end(JSON.stringify({ message:"成功!",data:data,code:10000}));
+ }
+
+
 var server = http.createServer(function(req,res){
 
     res.setHeader('Access-Control-Allow-Origin', '*'); // 允许所有域的请求,注意:在生产环境中应该限制为特定的域
@@ -358,7 +381,14 @@ var server = http.createServer(function(req,res){
                     }else if(jsonData['fun']=="get_yw_recharge_template"&&jsonData['cmd']=="yw_link"){
                         let yw_id = jsonData['data']['yw_id']
                         CMD.get_yw_recharge_template(res,yw_id) 
-                    } else{
+                    }else if(jsonData['fun']=="get_yw_open_sessid_status"&&jsonData['cmd']=="yw_link"){
+                        CMD.get_yw_open_sessid_status(res) 
+                    } else if(jsonData['fun']=="get_zh_recharge_template"&&jsonData['cmd']=="zh_link"){
+                        let app_type = jsonData['data']['app_type']
+                        CMD.get_zh_recharge_template(res,app_type) 
+                    }else if(jsonData['fun']=="get_zh_callback_template"&&jsonData['cmd']=="zh_link"){
+                        CMD.get_zh_callback_template(res) 
+                    }else{
                         res.writeHead(200, {'Content-Type': 'application/json'});
                         res.end(JSON.stringify({ message: 'fun 错误',code:100}));
                     }

+ 42 - 41
1_FILTER_FACTORY/FILTER_FACTORY.js

@@ -156,6 +156,30 @@ function getId(url) {
     }
 }
 
+function getCBID(url) {
+    try {
+        if (!url.includes('cbid=')) {
+            return null; // 或者返回其他你想要的默认值
+        }
+        const bookId = url.split('cbid=')[1].split('&')[0];
+        return bookId;
+    } catch (error) {
+        return null; // 出现任何错误都返回null
+    }
+}
+
+function get_work_id(url) {
+    try {
+        if (!url.includes('work_id=')) {
+            return null; // 或者返回其他你想要的默认值
+        }
+        const bookId = url.split('work_id=')[1].split('&')[0];
+        return bookId;
+    } catch (error) {
+        return null; // 出现任何错误都返回null
+    }
+}
+
 function filter_and_insert(origin_data,tg_config,PlatformConfig){
     if(origin_data.guajian_link.length<=0){
         return null
@@ -164,6 +188,7 @@ function filter_and_insert(origin_data,tg_config,PlatformConfig){
     if(helper.checkChannel(origin_data.guajian_link,"wx_gua_jian=1")){
         PlatformConfig = getWxPlatformConfig(PlatformConfig)
         let  parent_platform_id = getWxChannel(tg_config.platform_select,origin_data.guajian_link,PlatformConfig)
+        console.log("parent_platform_id:",parent_platform_id)
         if(parent_platform_id==null){
             return null
         }
@@ -182,6 +207,11 @@ function filter_and_insert(origin_data,tg_config,PlatformConfig){
             if(params['bookId'].length>6){
                 return null
             }
+        }else if(parent_platform_id==config.platform_yuewen){
+            params['bookId'] = getCBID(url);
+            if(params['bookId']==undefined||params['bookId']==null){
+                return null
+            }
         }
         
         let obj =  {video_id:origin_data.video_id,book_id:params['bookId'],source:params['name'].substring(0,params['name'].length>64?64:params['name'].length),video_link:origin_data.video_link,
@@ -235,10 +265,16 @@ function filter_and_insert(origin_data,tg_config,PlatformConfig){
         obj.book_id = obj.book_id
     }else if(obj.channel==6){ //"阅文"
         tg_platform_id = 6
-        obj.book_id = obj.cbid
+        obj.book_id = obj.book_id
     }else if(obj.channel==7){ //"番茄免費"
         tg_platform_id = 7
         obj.book_id = obj.code
+    }else if(obj.channel==15){ //"知乎"
+        tg_platform_id = 15
+        obj.book_id = obj.id
+        if(obj.book_id==undefined||obj.book_id==null){
+            return null
+        }
     }
     // console.log("评论数正确")
     return {video_id:origin_data.video_id,book_id:obj.book_id,channel:obj.channel,source:obj.source.substring(0,obj.source.length>64?64:obj.source.length),key:obj.key,video_link:origin_data.video_link,
@@ -249,7 +285,7 @@ function getWxPlatformConfig(PlatformConfig){
     let list = []
     for (let index = 0; index < PlatformConfig.length; index++) {
         const element = PlatformConfig[index];
-        if(element.mini_program_platform_id==2){
+        if(element.mini_program_platform_id==config.wx){
             list.push(element)
         }
     }
@@ -260,7 +296,7 @@ function getDyPlatformConfig(PlatformConfig){
     let list = []
     for (let index = 0; index < PlatformConfig.length; index++) {
         const element = PlatformConfig[index];
-        if(element.mini_program_platform_id==1){
+        if(element.mini_program_platform_id==config.dy){
             list.push(element)
         }
     }
@@ -292,11 +328,6 @@ function getWxChannel(platform_select,promotion,PlatformConfig){
 
 
 function getChannel(start_page,platform_select,promotion,PlatformConfig){
-    let isOpenHeiYan = false
-    let isOpenFanQie = false
-    let isOpenQiMao = false
-    let isOpenDianZhong = false
-    let isOpenFanQieMF = false
     let kv = new Map()
     PlatformConfig = getDyPlatformConfig(PlatformConfig)
     for (let index = 0; index < PlatformConfig.length; index++) {
@@ -306,29 +337,9 @@ function getChannel(start_page,platform_select,promotion,PlatformConfig){
 
     for (let index = 0; index < platform_select.length; index++) {
         const element = platform_select[index];
-        if(element==1){
-            isOpenHeiYan = true
-            kv.set(element,true)
-        }
-        if(element==2){
-            isOpenFanQie = true
-            kv.set(element,true)
-        }
-        if(element==3){
-            isOpenQiMao = true
-            kv.set(element,true)
-        }
-        if(element==4){
-            isOpenDianZhong = true
-            kv.set(element,true)
-        }
-        if(element==7){
-            isOpenFanQieMF = true
-            kv.set(element,true)
-        }
+        kv.set(element,true)
     }
 
-
     // console.log("PlatformConfig:",PlatformConfig,kv)
     for (let index = 0; index < PlatformConfig.length; index++) {
         const platform_info = PlatformConfig[index];
@@ -342,17 +353,6 @@ function getChannel(start_page,platform_select,promotion,PlatformConfig){
   
     }
     return null
-    // console.log("start_page:",isOpenHeiYan,isOpenFanQie,start_page)
-    if(helper.checkChannel(promotion,'pages/readPage/chapter')&&isOpenHeiYan){ //start_page=="pages/readPage/chapter?bookId"
-        return "黑岩"
-    }else if(helper.checkChannel(promotion,'pages/bookstore/index')&&isOpenFanQie){   //start_page=="pages/bookstore/index?aid"
-        return "番茄"
-    }else if(helper.checkChannel(promotion,'pages/reader/index?bookId=')&&isOpenDianZhong){ //点众  //start_page=="pages/reader/index?bookId"
-        return "点众"
-    }else if(helper.checkChannel(promotion,'channel=rd')&&isOpenQiMao){ //七猫
-        return "七猫"
-    }
-    return null
 }
 
 //解析挂件
@@ -371,8 +371,9 @@ function parsePromotion(promotion,source_list,platform_select,PlatformConfig){
                     params[obj.key] = obj.value
                 }
             }
+            console.log("params:",params['id'])
             let channel = getChannel(params['start_page'],platform_select,promotion,PlatformConfig)
-            console.log("source_list:",source_list,platform_select,channel,params['start_page'])
+            // console.log("source_list:",source_list,platform_select,channel,params['start_page'],params)
             if(channel!=null){
                 console.log("channel:",channel,promotion)
                 let item_info = {}

+ 1 - 2
2_PRODUCT_FACTORY/dz_batch_factory.js

@@ -4,7 +4,6 @@ const filter_data_controllers = require('../src/data_manager/Controllers/filter_
 const other_book_controllers = require('../src/data_manager/Controllers/other_book_controllers');
 const video_applet_product_controllers = require('../src/data_manager/Controllers/video_applet_product_controllers');
 const redis_help = require('../src/use_redis');
-const axios = require('axios');
 const config = require('../etc/config.json');
 const time_count = 1000;
 const task_queue = []
@@ -78,7 +77,7 @@ async function processTask(){
     }catch(e){
         console.error("processTask:",e)
     } finally{
-        global.setTimeout(processTask, 500);
+        global.setTimeout(processTask, 1000);
     }
 }
 

+ 0 - 1
2_PRODUCT_FACTORY/fq_batch_factory.js

@@ -4,7 +4,6 @@ const filter_data_controllers = require('../src/data_manager/Controllers/filter_
 const get_fq_book_tab_controllers = require('../src/data_manager/Controllers/get_fq_book_tab_controllers');
 const video_applet_product_controllers = require('../src/data_manager/Controllers/video_applet_product_controllers');
 const redis_help = require('../src/use_redis');
-const axios = require('axios');
 const config = require('../etc/config.json');
 const HttpClient = require('../src/HttpClient');
 const helper = require('../src/helper');

+ 0 - 1
2_PRODUCT_FACTORY/fq_mf_factory.js

@@ -5,7 +5,6 @@ const get_fq_mf_book_tab_controllers = require('../src/data_manager/Controllers/
 const video_applet_product_controllers = require('../src/data_manager/Controllers/video_applet_product_controllers');
 const other_book_controllers = require('../src/data_manager/Controllers/other_book_controllers');
 const redis_help = require('../src/use_redis');
-const axios = require('axios');
 const config = require('../etc/config.json');
 const HttpClient = require('../src/HttpClient');
 const helper = require('../src/helper');

+ 1 - 2
2_PRODUCT_FACTORY/hy_batch_factory.js

@@ -3,7 +3,6 @@ const video_product_controllers = require('../src/data_manager/Controllers/video
 const filter_data_controllers = require('../src/data_manager/Controllers/filter_data_controllers');
 const video_applet_product_controllers = require('../src/data_manager/Controllers/video_applet_product_controllers');
 const redis_help = require('../src/use_redis');
-const axios = require('axios');
 const config = require('../etc/config.json');
 const other_book_controllers = require('../src/data_manager/Controllers/other_book_controllers');
 const time_count = 1000;
@@ -132,7 +131,7 @@ async function processTask(){
     }catch(e){
         console.error("processTask error:",e)
     } finally{
-        global.setTimeout(processTask, 100);
+        global.setTimeout(processTask, 1000);
     }
 
 }

+ 11 - 13
2_PRODUCT_FACTORY/qm_batch_factory.js

@@ -1,15 +1,10 @@
 const CMD = {}
 const video_product_controllers = require('../src/data_manager/Controllers/video_product_controllers');
 const filter_data_controllers = require('../src/data_manager/Controllers/filter_data_controllers');
-const get_fq_book_tab_controllers = require('../src/data_manager/Controllers/get_fq_book_tab_controllers');
 const video_applet_product_controllers = require('../src/data_manager/Controllers/video_applet_product_controllers');
 const other_book_controllers = require('../src/data_manager/Controllers/other_book_controllers');
 const redis_help = require('../src/use_redis');
-const axios = require('axios');
 const config = require('../etc/config.json');
-const HttpClient = require('../src/HttpClient');
-const helper = require('../src/helper');
-const tools = require('../tools');
 const time_count = 1000;
 const task_queue = []
 
@@ -191,16 +186,19 @@ CMD.getMainInfoByAppid = function(MainConfig,app_id){
     return list
 }
 CMD.isGenre = function(FilterConfig,genre){
-    let is_need  = false
+    // let is_need  = false
     //判断长篇短篇是否是要的数据
-    for (let index = 0; index < FilterConfig.genre.length; index++) {
-        const element = FilterConfig.genre[index];
-        if(genre==element){
-            is_need = true
-            break
-        }
+    // for (let index = 0; index < FilterConfig.genre.length; index++) {
+    //     const element = FilterConfig.genre[index];
+    //     if(genre==element){
+    //         is_need = true
+    //         break
+    //     }
+    // }
+    if(genre==3||genre==1){
+        return true
     }
-    return is_need
+    return false
 }
 
 CMD.getPlatformInfo = function(PlatformConfig,tg_platform_id){

+ 9 - 7
2_PRODUCT_FACTORY/yw_batch_factory.js

@@ -1,14 +1,10 @@
 const CMD = {}
 const video_product_controllers = require('../src/data_manager/Controllers/video_product_controllers');
 const filter_data_controllers = require('../src/data_manager/Controllers/filter_data_controllers');
-const get_fq_book_tab_controllers = require('../src/data_manager/Controllers/get_fq_book_tab_controllers');
 const video_applet_product_controllers = require('../src/data_manager/Controllers/video_applet_product_controllers');
 const other_book_controllers = require('../src/data_manager/Controllers/other_book_controllers');
 const redis_help = require('../src/use_redis');
-const axios = require('axios');
 const config = require('../etc/config.json');
-const HttpClient = require('../src/HttpClient');
-const helper = require('../src/helper');
 const tools = require('../tools');
 const time_count = 1000;
 const task_queue = []
@@ -30,6 +26,12 @@ async function processTask(){
         if(isPauseTask=="true"){
             return
         }
+        await tools.getYwOPENSESSID()
+        let OPENSESSID = await redis_help.getKeyValue("OPENSESSID")
+        if(OPENSESSID==""){
+            console.error("阅文的OPENSESSID",OPENSESSID)
+            return
+        }
         let FilterConfig = await redis_help.getKeyValue("FilterConfig")
         FilterConfig = JSON.parse(FilterConfig)
         result = await filter_data_controllers.getFilterDataByStatusAndLimit({ status: 0 , tg_platform_id: config.platform_yuewen },500);
@@ -95,7 +97,7 @@ async function processTask(){
         //     await filter_data_controllers.updateFilterData({id:result.data.id},{status:2})
         // }
     } finally{
-        global.setTimeout(processTask, 100);
+        global.setTimeout(processTask, 1000);
     }
 
 }
@@ -116,7 +118,7 @@ async function processCreateTask(){
             FilterConfig = JSON.parse(FilterConfig)
             if(!product.success){
                 let tg_platform_id = result.data.tg_platform_id
-                if(tg_platform_id==config.platform_qimao){ //七猫
+                if(tg_platform_id==config.platform_yuewen){ //阅文
                     let yw_book_data = await require('../src/api/yw/yw_search_book').search_id(result.data.book_id)
                     if(yw_book_data==null){
                         result.data.genre = 999
@@ -242,7 +244,7 @@ CMD.update_filter_data = async function(FilterConfig,data) {
 
         PlatformConfig = JSON.parse(PlatformConfig)
 
-        let PlatformInfo = CMD.getPlatformInfo(PlatformConfig,config.platform_qimao)
+        let PlatformInfo = CMD.getPlatformInfo(PlatformConfig,config.platform_yuewen)
 
 
         let all_platform_list = CMD.getSharedPlatform(PlatformShared,PlatformInfo.platform_shared_id)

+ 318 - 0
2_PRODUCT_FACTORY/zh_batch_factory.js

@@ -0,0 +1,318 @@
+const CMD = {}
+const video_product_controllers = require('../src/data_manager/Controllers/video_product_controllers');
+const filter_data_controllers = require('../src/data_manager/Controllers/filter_data_controllers');
+const video_applet_product_controllers = require('../src/data_manager/Controllers/video_applet_product_controllers');
+const other_book_controllers = require('../src/data_manager/Controllers/other_book_controllers');
+const redis_help = require('../src/use_redis');
+const config = require('../etc/config.json');
+const time_count = 1000;
+const task_queue = []
+
+function getProductById(product_list,product_id){
+    for (let index = 0; index < product_list.length; index++) {
+        const product_item = product_list[index];
+        if(product_item.product_id==product_id){
+            return product_item
+        }
+    }
+    return null
+}
+
+async function processTask(){
+    let result = null
+    try{
+        let isPauseTask =  await redis_help.getKeyValue("isPauseTask")
+        if(isPauseTask=="true"){
+            return
+        }
+        let FilterConfig = await redis_help.getKeyValue("FilterConfig")
+        FilterConfig = JSON.parse(FilterConfig)
+        result = await filter_data_controllers.getFilterDataByStatusAndLimit({ status: 0 , tg_platform_id: config.platform_zhihu },500);
+        if(result.success){
+            let ProductIds = []
+            let FilterList = []
+            let Ids = []
+            if(result.data.length<=0){
+                throw '没有数据可处理!'
+            }
+            for (let index = 0; index < result.data.length; index++) {
+                let filter_item = result.data[index];
+                ProductIds.push(filter_item.book_id)
+                FilterList[filter_item.book_id] = filter_item
+            }
+
+            let product_list_result = await video_product_controllers.findNonExistentProductIds(ProductIds);
+            if(product_list_result.success){
+                for (let index = 0; index < result.data.length; index++) {
+                    let filter_item = result.data[index];
+                    if(product_list_result.data.existingProductIds.has(filter_item.book_id)){
+                        let info = filter_item;
+                        let product_item = getProductById(product_list_result.data.existingRecords,filter_item.book_id)
+                        if (!info) {
+                            throw `No filter info found for book_id: ${filter_item.book_id}`
+                        }
+                        if (!product_item) {
+                            throw `product_item is null: ${filter_item.book_id}`
+                        }
+                        info.words = product_item.words
+                        info.genre =  product_item.genre
+                        info.book_id = product_item.product_id
+                        info.book_name = product_item.product_name
+                        await CMD.update_filter_data(FilterConfig,info)
+                        await new Promise(resolve => setTimeout(resolve,100));
+                    } else{
+                        Ids.push(filter_item.id)
+                    }
+                }
+
+                let remove_product_list = []
+
+                for (let index = 0; index < product_list_result.data.nonExistentIds.length; index++) {
+                    const product_id = product_list_result.data.nonExistentIds[index];
+                    if(remove_product_list[product_id]==true){
+          
+                    }else{
+                        remove_product_list[product_id] = true
+                        task_queue.push({result:{data:FilterList[product_id]}})
+                    }
+                }
+                // if(Ids.length>0){
+                //     console.log("Ids:",Ids)
+                //     await filter_data_controllers.updateFilterDataByIds({ids:Ids},{status:1})
+                // }
+              
+            }
+            
+        }
+    }catch(e){
+        console.error("processTask error:",e)
+        // if(result!=null&&result!=undefined){
+        //     await filter_data_controllers.updateFilterData({id:result.data.id},{status:2})
+        // }
+    } finally{
+        global.setTimeout(processTask, 100);
+    }
+
+}
+
+async function processCreateTask(){
+    let result = null
+    try{
+        if(task_queue.length>0){
+            let task_queue_item = task_queue.pop()
+            result = task_queue_item.result
+            let other_book = await other_book_controllers.getData({product_id:task_queue_item.result.data.book_id})
+            if(other_book.success){
+                await filter_data_controllers.updateFilterData({id:result.data.id},{status:2,book_id:other_book.data.product_id,book_name:other_book.data.product_name,material_sync_status:2})
+                throw "处理了一条长篇或者筛选不要的书"
+            }
+            let product = await video_product_controllers.getProductData({ book_id: task_queue_item.result.data.book_id });
+            let FilterConfig = await redis_help.getKeyValue("FilterConfig")
+            FilterConfig = JSON.parse(FilterConfig)
+            if(!product.success){
+                let tg_platform_id = result.data.tg_platform_id
+                if(tg_platform_id==config.platform_zhihu){ //知乎
+                    let zh_book_data = await require('../src/api/zh/zh_search_book').search_id(result.data.book_id)
+                    if(zh_book_data==null){
+                        result.data.genre = 999
+                    }else{
+                        zh_book_data.tg_platform_id = result.data.tg_platform_id
+                        if(CMD.isGenre(FilterConfig,zh_book_data.genre)){
+                            await CMD.insert_product(zh_book_data)
+                        }
+                        result.data.words = zh_book_data.words
+                        result.data.genre = zh_book_data.genre
+                        result.data.book_id = zh_book_data.book_id
+                        result.data.book_name = zh_book_data.book_name
+                    }
+                }
+            }else{
+                result.data.words = product.words
+                result.data.genre = product.genre
+                result.data.book_id = product.product_id
+                result.data.book_name = product.product_name
+            }
+            await CMD.update_filter_data(FilterConfig,result.data)
+        }
+    }catch(e){
+        console.log("error::",e)
+        // if(result!=null){
+        //     await filter_data_controllers.updateFilterData({id:result.data.id},{status:2})
+        // }
+        console.log("processCreateTask:",e)
+    } finally{
+        global.setTimeout(processCreateTask, time_count);
+    }
+}
+CMD.init = async function(){
+    redis_help.connect((results)=>{
+        if(results){
+        }
+    })
+    processTask();
+    processCreateTask();
+}
+
+CMD.getAppInfoByPlatformId = function(AppConfig,tg_platform_id){
+    let temp = []
+    for (let index = 0; index < AppConfig.length; index++) {
+        const element = AppConfig[index];
+        if(element.tg_platform_id==tg_platform_id){
+            temp.push(element)
+        }
+    }
+    return temp
+}
+CMD.isBlackBook = function(all_black_books,book_id){
+    if(all_black_books==null){
+        return false
+    }
+    for (let index = 0; index < all_black_books.length; index++) {
+        const black_book = all_black_books[index];
+        if(black_book.product_id==book_id){
+            return true
+        }
+    }
+    return false
+}
+CMD.getMainInfoByAppid = function(MainConfig,app_id){
+    let list = []
+    for (let index = 0; index < MainConfig.length; index++) {
+        const element = MainConfig[index];
+        if(element.app_id==app_id){
+            list.push(element)
+        }
+    }
+    return list
+}
+CMD.isGenre = function(FilterConfig,genre){
+    let is_need  = false
+    //判断长篇短篇是否是要的数据
+    for (let index = 0; index < FilterConfig.genre.length; index++) {
+        const element = FilterConfig.genre[index];
+        if(genre==element){
+            is_need = true
+            break
+        }
+    }
+    return is_need
+}
+
+CMD.getPlatformInfo = function(PlatformConfig,tg_platform_id){
+    for (let index = 0; index < PlatformConfig.length; index++) {
+        const element = PlatformConfig[index];
+        if(element.tg_platform_id==tg_platform_id){
+            return element
+        }
+    }
+}
+CMD.getSharedPlatform = function(PlatformShared,platform_shared_id){
+    for (let index = 0; index < PlatformShared.length; index++) {
+        const element = PlatformShared[index];
+        if(element.id==platform_shared_id){
+            return JSON.parse(element.info)
+        }
+    }
+}
+
+CMD.update_filter_data = async function(FilterConfig,data) {
+    let isGenre = CMD.isGenre(FilterConfig,data.genre)
+    if(isGenre){
+        await filter_data_controllers.updateFilterData({id:data.id},{status:1,book_id:data.book_id,book_name:data.book_name,genre:data.genre,words:data.words})
+        //创建一个小程序    
+
+        let MainConfig = await redis_help.getKeyValue("MainConfig")
+
+        MainConfig = JSON.parse(MainConfig)
+
+        let AppConfig = await redis_help.getKeyValue("AppConfig")
+
+        AppConfig = JSON.parse(AppConfig)
+
+        let PlatformShared = await redis_help.getKeyValue("PlatformShared")
+
+        PlatformShared = JSON.parse(PlatformShared)
+        
+        let PlatformConfig = await redis_help.getKeyValue("PlatformConfig")
+
+        PlatformConfig = JSON.parse(PlatformConfig)
+
+        let PlatformInfo = CMD.getPlatformInfo(PlatformConfig,config.platform_zhihu)
+
+
+        let all_platform_list = CMD.getSharedPlatform(PlatformShared,PlatformInfo.platform_shared_id)
+
+
+        for (let i = 0; i < all_platform_list.length; i++) {
+            const tg_platform_id = all_platform_list[i];
+
+            let AppInfoList = CMD.getAppInfoByPlatformId(AppConfig,tg_platform_id)
+
+
+            for (let j = 0; j < AppInfoList.length; j++) {
+                const AppInfo = AppInfoList[j];
+                data.app_id = AppInfo.app_id
+
+                let list = CMD.getMainInfoByAppid(MainConfig,data.app_id)
+
+                if(list.length<0){
+                    return console.error("获取主体错误:",data)
+                }
+
+                let black_books = await redis_help.getKeyValue("all_black_books")
+                
+                if(black_books!=null){
+                    black_books = JSON.parse(black_books)
+                }
+        
+                for (let index = 0; index < list.length; index++) {
+                    const main_info = list[index];
+                    data.main_id = main_info.id
+                    data.main_name = main_info.main_name
+                    if(main_info.tg_link_config==undefined||main_info.tg_link_config==null){
+                        console.error("主体配置推广链接错误:",main_info)
+                    }else{
+                        if(!CMD.isBlackBook(black_books,data.book_id)){
+                            //符合标准进行创建表
+                            const result = await video_applet_product_controllers.getAppletProductData({book_id:data.book_id,main_id:data.main_id})
+                            if(!result.success){
+                                //没有找到 可以创建
+                                await video_applet_product_controllers.createAppletProductData({
+                                    book_platform:config.platform_zhihu,
+                                    product_name:data.book_name,
+                                    product_id:data.book_id,
+                                    dy_small_program_start:"",
+                                    dy_small_program_start_data:"",
+                                    dy_small_applet_app_id:data.app_id,
+                                    status:0,
+                                    main_id:data.main_id,
+                                    promotion_id:"",
+                                    wait_status:0,
+                                })
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        
+        
+    }else{
+        await other_book_controllers.createData({product_id:data.book_id,product_name:data.book_name,tg_platform_id:data.tg_platform_id})
+        await filter_data_controllers.updateFilterData({id:data.id},{status:2,book_id:data.book_id,book_name:data.book_name,genre:data.genre,material_sync_status:2})
+    }
+}
+
+
+CMD.insert_product = async function(data){
+    return await video_product_controllers.createProductData({
+        book_platform:data.tg_platform_id,
+        product_name:data.book_name,
+        genre:data.genre,
+        product_id:data.book_id,
+        words:data.words
+    })
+}
+
+CMD.init()

+ 9 - 2
5_CREATE_LINK_FACTORY/yw_create_link_factory.js

@@ -3,7 +3,8 @@ const time_count = 2000;
 const redis_help = require('../src/use_redis');
 const video_applet_product_controllers = require('../src/data_manager/Controllers/video_applet_product_controllers');
 var yw_create_link_task_list = []
-const config = require('../etc/config.json')
+const config = require('../etc/config.json');
+const tools = require('../tools');
 let finish_call_back = async function(new_data,error_info=null){
     try{
         if(new_data.start_page != null && error_info==null){
@@ -59,6 +60,12 @@ CMD.getSharedPlatform = function(PlatformShared,platform_shared_id){
 
 async function processTask(){
     try{
+        await tools.getYwOPENSESSID()
+        let OPENSESSID = await redis_help.getKeyValue("OPENSESSID")
+        if(OPENSESSID==""){
+            console.log("阅文的OPENSESSID超过7小时需要更新")
+            return
+        }
         let MainConfig = await redis_help.getKeyValue("MainConfig")
         MainConfig = JSON.parse(MainConfig)
         let PlatformConfig = await redis_help.getKeyValue("PlatformConfig")
@@ -134,7 +141,7 @@ async function processTask(){
             }
 
 
-            qm_create_link_task_list.push({
+            yw_create_link_task_list.push({
                 n_data:result.data,
                 main_info:main_info,
                 PlatformInfo:PlatformInfo

+ 100 - 0
5_CREATE_LINK_FACTORY/zh_create_link.js

@@ -0,0 +1,100 @@
+const fetch = require('node-fetch'); // Node.js 18以下版本需要安装 node-fetch
+const helper = require('../src/helper');
+const config = require('../etc/config.json');
+const redis_help = require('../src/use_redis');
+const CMD = {}
+CMD.runTask = async function(t_data,main_info,PlatformInfo,call_back){
+    try{
+        let timestamp = helper.getCurrentUnixTimestamp()
+        let tg_link_config = JSON.parse(main_info.tg_link_config)
+        let chongzhi_id = tg_link_config['chongzhi_id']
+        let huichuan_id = tg_link_config['huichuan_id']
+        let chongzhi_list = JSON.parse(PlatformInfo.chongzhi)
+        let huichuan_list = JSON.parse(PlatformInfo.huichuan)
+        let payment_template_id = ""
+        let callback_rule_id = ""
+        let section_id = main_info.zh_id
+        console.log("section_id:",section_id)
+        for (let index = 0; index < chongzhi_list.length; index++) {
+            const cz_obj = chongzhi_list[index];
+            if(cz_obj.id==chongzhi_id){
+                payment_template_id = cz_obj.value
+              break
+            }
+        }
+
+
+        for (let index = 0; index < huichuan_list.length; index++) {
+            const hc_obj = huichuan_list[index];
+            if(hc_obj.id==huichuan_id){
+                callback_rule_id = hc_obj.value
+              break
+            }
+          }
+
+        const headers = {
+            "accept": "application/json, text/plain, */*",
+            "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
+            "content-type": "application/json;charset=UTF-8",
+            "priority": "u=1, i",
+            "sec-ch-ua": "\"Not A(Brand\";v=\"8\", \"Chromium\";v=\"132\", \"Microsoft Edge\";v=\"132\"",
+            "sec-ch-ua-mobile": "?0",
+            "sec-ch-ua-platform": "\"Windows\"",
+            "sec-fetch-dest": "empty",
+            "sec-fetch-mode": "cors",
+            "sec-fetch-site": "same-origin",
+            "x-requested-with": "XMLHttpRequest",
+            "cookie": "_xsrf=7bCsoKjmfrIE0D36ajUc9s6CM9OxdPHQ; _zap=aa0b20fb-9951-4f27-9f66-dafff809f1a9; d_c0=ADASSwlwjxmPTrW0768vZ0BKygxzh_LFDDI=|1731902666; __zse_ck=004_RAwxSX7iODkHf=KcaqfMYfLtUB=liEm/v0fmgVHxEqKYzUEWDsJ4I5wkbR1yz3X=sAOsMm7PcwNXoQHjg5IxNkMc38yFAYS9Vcvho/AB5CWvfKZL7JZ6yb8JNCk=hJhs-xC0Hm7QqNBqkjTYlQ0LGKY50XF8sCD+ku+G/FUcrxDHtM9cxwgjtwoE7C9tAtkeLkUwyYVe6lZG1PpW/gndPL1lhcrZ53odQtwGooASih8FG2HI3W7Xpo3Bq+eqzshzK; Hm_lvt_98beee57fd2ef70ccdd5ca52b9740c49=1735787111,1735909747,1736148711; captcha_session_v2=2|1:0|10:1736148772|18:captcha_session_v2|88:YkZmazRKNXExV2dHaTkvaWIyQkJaUXpPZDFEbFNwWTR1bjNKK0dpUUdJRTcxZldZOUtFQlFSMUQzT1h1TFlNZw==|7015dcf5d48280e56784ef6d89a2b4d15352adcb1e257453c9907a664ceaa0d9; captcha_ticket_v2=2|1:0|10:1736148810|17:captcha_ticket_v2|728:eyJ2YWxpZGF0ZSI6IkNOMzFfKkxBeEhrcDFRYnhkeGxtTGRnSlZZWVdHbjhtLlc4R3BmQWl5dFNLYlA1ekZITXYydi5KRypfa3plX3poZGJRRExveDlSR3lFVDJJbmQ4Q1ZHNnJFSWkqdyppMDZKMzlXYVZJcFlIX0VHc0ZRRDBsa1BvblJrNVJldWVDNFRNeFBXRmZZcXVJWHB1Yip0ckY1SWFQdVNoS0YyKjZmUHhUKnFiOXpDaFVsc0djdHVBX1hjWENfYkNQemlxREwwSWpWekdSZHlqWUFWQ0d5aE5TaWFzZEhzdWRDWCprYy5QKkh6ajV6VU9nM2lQT296YVBxSzlXemYqcXZzRF9ENWVPU2dGQmhoUWd3d3RZb3ByT0t0M0ZpVSp3SUZsc1FmZTVMRDJwSDhTcHBOZUF4VkhWYkVEdC4zVU1USkl3SjhRWCoqb0xrKnlhU2RudDNXOElfV01mMV84RzF1d05NeUpJS0YqcmJ3MTBsejNDX2lnMFJYM2FMUTBZT04ySDFqRkpZYlNCMCpfVjJseENYdmRPMEtZanJqTEh2b3RFbERnc2xzTDF4eS5TSWZSU3owY3kxRmFpU3hadkNxV00ubUJvbExKQy5JR1BvMG1uLmtNZHRuNElMMGx6NE9DcmdKMXFHeV81aUhUMGhNajZxVTRmdFlSU1JlNUxlbkJGWWdzcnZWZnFxNVk3N192X2lfMSJ9|9a0d71076f5273fc22dd67c3e4eabb975e479e875191d79a44e51972d2d848cc; z_c0=2|1:0|10:1736148810|4:z_c0|92:Mi4xN1lrbldnQUFBQUFBTUJKTENYQ1BHU1lBQUFCZ0FsVk5TdFZvYUFCUDI3aTJFNk5MNThBZ215bXIyZVJIWXhvNXN3|707e10b967760748115ffeee34231345d9323f18a7e61ff853b0d3cf9967dae2; ecom_merchant=1857114769787609088; BEC=6c53268835aec2199978cd4b4f988f8c",
+            "Referer": "https://ecom.zhihu.com/vip-apps/out-distribution/contentLib/detail?&sectionId=1864766430517526529&workId=1864766430517526529&appId=tt2ffb5946b0cb808001&media_channel_type=5",
+            "Referrer-Policy": "no-referrer-when-downgrade"
+        };
+
+        let app_type = 4
+        if(PlatformInfo.mini_program_platform_id==config.wx){
+            app_type = 6
+        }else if(PlatformInfo.mini_program_platform_id==config.dy){
+            app_type = 4
+        }
+    
+        let postData = {
+            "multi_sections": "0",
+            "ad_link_name": t_data.product_name,
+            "media_channel_id":5,
+            "app_type": app_type,
+            "app_id": main_info.app_id,
+            "callback_type": 0,
+            "payment_template_id":`${payment_template_id}`,
+            "num": 1,
+            "callback_rule_id":`${callback_rule_id}`,
+            "section_id": section_id,
+        };
+        console.log("postData:",postData)
+
+        let response = await fetch("https://ecom.zhihu.com/api/km-agent/ad-link", {
+            method: "POST",
+            headers: headers,
+            body: JSON.stringify(postData)
+        });
+
+        const data = await response.json();
+        console.log('CMD.runTask:响应数据:', data);
+        if(data.success!=true){
+            throw data
+        }
+        let promotion_info = data.data
+        let t_params = promotion_info.ad_link.split('?')
+        let start_page = t_params[0]
+        let start_param = t_params[1]
+        t_data.promotion_id = promotion_info.ad_link_id
+        t_data.start_page = start_page
+        t_data.start_param = start_param
+        console.log('CMD.runTask::响应状态:', response.status);
+        console.log('t_data:',t_data)
+        call_back(t_data,null)
+    }catch(error){
+        console.error('CMD.runTask:请求错误:', error);
+        call_back(t_data,error)
+        return null;
+    }
+}
+module.exports = CMD;

+ 190 - 0
5_CREATE_LINK_FACTORY/zh_create_link_factory.js

@@ -0,0 +1,190 @@
+const CMD = {}
+const time_count = 2000;
+const redis_help = require('../src/use_redis');
+const video_applet_product_controllers = require('../src/data_manager/Controllers/video_applet_product_controllers');
+var zh_create_link_task_list = []
+const config = require('../etc/config.json');
+const tools = require('../tools');
+let finish_call_back = async function(new_data,error_info=null){
+    try{
+        if(new_data.start_page != null && error_info==null){
+            const result = await video_applet_product_controllers.updateData({id:new_data.id},{
+                dy_small_program_start:new_data.start_page,
+                dy_small_program_start_data:new_data.start_param,
+                promotion_id:new_data.promotion_id,
+                wait_status:1,
+                status:0
+            })
+        }else{
+            const result = await video_applet_product_controllers.updateData({id:new_data.id},{
+                status:2,
+                wait_status:2,
+                error_info:JSON.stringify(error_info)
+            })
+        }
+  
+    }catch(e){
+        console.log("finish_call_back error:",e)
+    }
+   
+}
+CMD.isBlackBook = function(all_black_books,book_id){
+    if(all_black_books==null){
+        return false
+    }
+    for (let index = 0; index < all_black_books.length; index++) {
+        const black_book = all_black_books[index];
+        if(black_book.product_id==book_id){
+            return true
+        }
+    }
+    return false
+}
+
+CMD.getPlatformInfo = function(PlatformConfig,tg_platform_id){
+    for (let index = 0; index < PlatformConfig.length; index++) {
+        const element = PlatformConfig[index];
+        if(element.tg_platform_id==tg_platform_id){
+            return element
+        }
+    }
+}
+CMD.getSharedPlatform = function(PlatformShared,platform_shared_id){
+    for (let index = 0; index < PlatformShared.length; index++) {
+        const element = PlatformShared[index];
+        if(element.id==platform_shared_id){
+            return JSON.parse(element.info)
+        }
+    }
+}
+
+async function processTask(){
+    try{
+        let MainConfig = await redis_help.getKeyValue("MainConfig")
+        MainConfig = JSON.parse(MainConfig)
+        let PlatformConfig = await redis_help.getKeyValue("PlatformConfig")
+        PlatformConfig = JSON.parse(PlatformConfig)
+        let black_books = await redis_help.getKeyValue("all_black_books")
+        if(black_books!=null){
+            black_books = JSON.parse(black_books)
+        }
+
+        let PlatformShared = await redis_help.getKeyValue("PlatformShared")
+
+        PlatformShared = JSON.parse(PlatformShared)
+
+        let platform_zhihu_info = CMD.getPlatformInfo(PlatformConfig,config.platform_zhihu)
+
+        let all_platform_list = CMD.getSharedPlatform(PlatformShared,platform_zhihu_info.platform_shared_id)
+
+        let result = null
+        
+        for (let index = 0; index < all_platform_list.length; index++) {
+            const tg_platform_id = all_platform_list[index];
+            let temp_result = await video_applet_product_controllers.getAppletProductDataByStatus({status:0,wait_status:0,book_platform:tg_platform_id})
+            if(temp_result.success){
+                result = temp_result
+                break
+            }
+        }
+
+        if(result==null){
+            return
+        }
+
+        if(result.success){
+            if(CMD.isBlackBook(black_books,result.data.product_id)){
+                await video_applet_product_controllers.updateData({id:result.data.id},{
+                    status:2,
+                    wait_status:2,
+                    error_info:JSON.stringify({msg:"当前书在黑名单"})
+                })
+                throw "当前书在黑名单"
+            }
+            let main_info = CMD.getMainInfoById(MainConfig,result.data.main_id)
+
+            let PlatformInfo = null
+            
+            PlatformInfo = CMD.getPlatformInfoById(PlatformConfig,main_info.tg_platform_id)
+
+            if(PlatformInfo==null){
+                await video_applet_product_controllers.updateData({id:result.data.id},{
+                    status:2,
+                    wait_status:2,
+                    error_info:JSON.stringify({msg:"平台配置错误:"})
+                })
+                console.error("平台配置错误:",result)
+                throw "平台配置错误:"
+            }
+  
+
+            if(main_info==null&&result.data.is_close_execution==1){
+                let CloseMainConfig = await redis_help.getKeyValue("CloseMainConfig")
+                CloseMainConfig = JSON.parse(CloseMainConfig)
+                main_info = CMD.getMainInfoById(CloseMainConfig,result.data.main_id)
+            }
+
+            if(main_info==null){
+                await video_applet_product_controllers.updateData({id:result.data.id},{
+                    status:2,
+                    wait_status:2,
+                    error_info:JSON.stringify({msg:"主体配置错误:"})
+                })
+                console.error("主体配置错误:",result)
+                throw "主体配置错误:"
+            }
+
+
+            zh_create_link_task_list.push({
+                n_data:result.data,
+                main_info:main_info,
+                PlatformInfo:PlatformInfo
+            })
+        }
+
+        console.log("zh_create_link_task_list:",zh_create_link_task_list)
+
+        if(zh_create_link_task_list.length>0){
+            let zh_create_link_task = zh_create_link_task_list.pop()
+            require('./zh_create_link').runTask(zh_create_link_task.n_data
+                ,zh_create_link_task.main_info,
+                zh_create_link_task.PlatformInfo,finish_call_back)
+        }
+    }catch(e){
+        console.log("zh_create_link_factory error:",e)
+        finish_call_back(null,{"MESSAGE":"yw_create_link_factory error:"})
+    } finally{
+        global.setTimeout(processTask,time_count)
+    }
+}
+CMD.init = function(){
+    redis_help.connect((results)=>{
+        if(results){
+        }
+    })
+    processTask()
+}
+
+
+
+CMD.getPlatformInfoById = function(PlatformConfig,tg_platform_id){
+    for (let index = 0; index < PlatformConfig.length; index++) {
+        const element = PlatformConfig[index];
+        if(element.tg_platform_id==tg_platform_id){
+            return element
+        }
+    }
+    return null
+}
+
+CMD.getMainInfoById = function(MainConfig,main_id){
+    // console.log("MainConfig:",MainConfig)
+    for (let index = 0; index < MainConfig.length; index++) {
+        const element = MainConfig[index];
+        if(element.id==main_id){
+            return element
+        }
+    }
+    return null
+}
+CMD.init()

+ 21 - 0
MESSAGE_DISPATCH/MESSAGE_DISPATCH.js

@@ -121,6 +121,23 @@ CMD.updateFqMfKeyList =  async function(){
     });
 }
 
+CMD.updateYwKeyList =  async function(){
+    const postData = {
+        cmd:"yw_book",
+        fun:"get_all_yw_key",
+        data:{}
+    };
+    let client = tools.getOneNewClinet()
+    client.post('http://127.0.0.1:9100/tg/back/api', postData)
+    .then(response => {
+        console.log("updateYwKeyList:",response.data.data)
+        redis_help.setKeyValue("all_yw_key",JSON.stringify(response.data.data))
+    })
+    .catch(error => {
+      console.error('all_yw_key error!', error);
+    });
+}
+
 CMD.updateFqKeyList = async function(){
     const postData = {
         cmd:"fq_book",
@@ -249,6 +266,10 @@ async function recvMessage(data){
             case "updateFqMfKeyList":
                 CMD.updateFqMfKeyList()
             break;
+            case "updateYwKeyList":
+               await CMD.updateYwKeyList()
+               await tools.getYwOPENSESSID()
+            break;
             case "updateBlackBooks":
                 CMD.updateBlackBooks()
             break;

+ 25 - 0
MESSAGE_DISPATCH/ocr_script.py

@@ -63,6 +63,25 @@ class MyRequestHandler(BaseHTTPRequestHandler):
                 self.send_header('Content-type', 'application/json')
                 self.end_headers()
                 self.wfile.write(response_str.encode('utf-8'))
+            elif cmd == "get_gap":
+                base64_image_data1 = data.get('base64_image_data1')  
+                base64_image_data0 = data.get('base64_image_data0')  
+                if not base64_image_data1:
+                    raise ValueError("Missing 'base64_image_data1' in JSON payload")
+                if not base64_image_data0:
+                    raise ValueError("Missing 'base64_image_data0' in JSON payload")
+                print("This is get_gap command.")
+                
+                response = {
+                    "message": "Data received successfully",
+                    "received_data": get_gap(key,None),
+                }
+                response_str = json.dumps(response)
+                self.send_response(200)
+                self.send_header('Content-type', 'application/json')
+                self.end_headers()
+                self.wfile.write(response_str.encode('utf-8'))
+
             else:
                 self.send_response(200)
                 self.send_header('Content-type', 'application/json')
@@ -202,6 +221,12 @@ def temp_chapterSparead(data):
     response = requests.get(url, params=params, cookies=cookies, headers=headers)
     return response.json()
 
+def get_gap(target_img:bytes, background_img:bytes):
+    det = ddddocr.DdddOcr(det=False, ocr=False, show_ad=False)
+    res = det.slide_match(target_img, background_img,simple_target=True)
+    print(res)
+    return res["target"][0]
+
 if __name__ == "__main__":
     import io  # 确保导入了 io 模块以处理内存中的图像
     run(port=8080)

+ 0 - 2
PULL_DATA_FACTORY/PULL_DATA.js

@@ -1,8 +1,6 @@
 const CMD = {}
-const time_count = 1000;
 const redis_help = require('../src/use_redis');
 const origin_data_controllers = require('../src/data_manager/Controllers/origin_data_controllers');
-const axios = require('axios')
 const config = require('../etc/config.json');
 const DailyRecordManager = require('./daily_records');
 const schedule = require('node-schedule');

+ 4 - 5
WX_GUA_JIAN_PARSE/WX_GUA_JIAN_PARSE.js

@@ -2,7 +2,6 @@ const CMD = {}
 const time_count = 2000;
 const redis_help = require('../src/use_redis');
 const origin_data_controllers = require('../src/data_manager/Controllers/origin_data_controllers');
-const filter_data_controllers = require('../src/data_manager/Controllers/filter_data_controllers');
 const helper = require('../src/helper');
 
 async function processTask(){
@@ -20,12 +19,12 @@ async function processTask(){
                 const element = origin_data_list[index];
                 if(helper.checkChannel(element.guajian_link,'title=%')){
                     let d = await require('./parse_wx_guajian').fetchData(element.guajian_link)
-                    console.log("d:",d)
+                    // console.log("d:",d)
                     if(d!=null){
                         let guajian_link = `wx_gua_jian=1$${d.wechat_microapp_jump_url}&name=${d.wechat_microapp_title}`
                         await origin_data_controllers.updateOriginData({id:element.id},{status:0,guajian_link:guajian_link})
                     }else{
-                        console.log("element:",element.id)
+                        // console.log("element:",element.id)
                         meaningless_Ids.push(element.id)
                     }
                     await new Promise(resolve => setTimeout(resolve, 500));
@@ -34,10 +33,10 @@ async function processTask(){
                 }
             }
 
-            console.log("meaningless_Ids:",meaningless_Ids)
+            // console.log("meaningless_Ids:",meaningless_Ids)
             
             if(meaningless_Ids.length>0){
-                console.log("meaningless_Ids:",meaningless_Ids)
+                // console.log("meaningless_Ids:",meaningless_Ids)
                 let update_result =  await origin_data_controllers.updateOriginDataByIds({ids:meaningless_Ids},{status:4})
                 if(update_result.success){
                     console.log(update_result.message)

+ 1 - 1
WX_GUA_JIAN_PARSE/parse_wx_guajian.js

@@ -49,7 +49,7 @@ async function fetchData(url1) {
         if (anchorData && anchorData.length > 0) {
             const item = anchorData[0];
             if(item.wechat_microapp_title==undefined||item.wechat_microapp_jump_url==undefined||item.wechat_microapp_jump_url.length<=0||item.wechat_microapp_title.length<=0){
-                console.log("item:",item)
+                // console.log("item:",item)
                 return null
             }
             return {

BIN
dump.rdb


+ 2 - 1
etc/config.json

@@ -134,5 +134,6 @@
     "platform_qimao":3,
     "platform_dianzhong":4,
     "platform_fanqiemf":7,
-    "platform_yuewen":6
+    "platform_yuewen":6,
+    "platform_zhihu":15
 }

+ 229 - 578
geetest_crack.py

@@ -1,591 +1,242 @@
-import os
-import time
-import logging
-import signal
-import subprocess
-import cv2
-import numpy as np
+import ddddocr
+
+
 from PIL import Image
 import io
+import numpy as np
+import time
+import json
 import base64
 import random
-from selenium import webdriver
-from selenium.webdriver.firefox.options import Options
-from selenium.webdriver.firefox.service import Service
-from selenium.webdriver.support.ui import WebDriverWait
-from selenium.webdriver.support import expected_conditions as EC
-from selenium.webdriver.common.by import By
-from selenium.webdriver.common.action_chains import ActionChains
-from contextlib import contextmanager
+import math
+import requests
+import urllib.parse
 
-# 配置日志
-logging.basicConfig(
-    level=logging.INFO,
-    format='%(asctime)s - %(levelname)s - %(message)s'
-)
-logger = logging.getLogger(__name__)
-
-class TimeoutException(Exception):
-    pass
-
-@contextmanager
-def time_limit(seconds):
-    def signal_handler(signum, frame):
-        raise TimeoutException("Timed out!")
-    signal.signal(signal.SIGALRM, signal_handler)
-    signal.alarm(seconds)
-    try:
-        yield
-    finally:
-        signal.alarm(0)
-
-class GeetestCracker:
+class TDCSimulator:
     def __init__(self):
-        self.driver = None
-        self.wait = None
-        logger.info("初始化 GeetestCracker")
-
-    def cleanup_processes(self):
-        """清理残留进程"""
-        try:
-            subprocess.run(['pkill', '-f', 'firefox'], stderr=subprocess.DEVNULL)
-            subprocess.run(['pkill', '-f', 'geckodriver'], stderr=subprocess.DEVNULL)
-            time.sleep(2)
-            logger.info("清理残留进程完成")
-        except Exception as e:
-            logger.error(f"清理进程时出错: {str(e)}")
-
-    def setup_driver(self):
-        """配置并初始化 WebDriver"""
-        try:
-            logger.info("开始设置浏览器驱动")
-            
-            # 首先清理可能的残留进程
-            self.cleanup_processes()
-            
-            # 检查 geckodriver
-            from shutil import which
-            geckodriver_path = which('geckodriver')
-            
-            if not geckodriver_path:
-                logger.error("未找到 geckodriver")
-                return False
-                
-            logger.info(f"找到 geckodriver: {geckodriver_path}")
-            
-            # Firefox 配置 - 修改配置以提高稳定性
-            options = Options()
-            
-            # 核心配置
-            options.set_preference('marionette', True)
-            options.set_preference('marionette.port', 2828)  # 固定端口
-            options.set_preference('network.http.connection-timeout', 10000)
-            options.set_preference('network.http.response.timeout', 10000)
-            
-            # 禁用 JavaScript JIT
-            options.set_preference('javascript.options.ion', False)
-            options.set_preference('javascript.options.baselinejit', False)
-            
-            # 禁用硬件加速
-            options.set_preference('layers.acceleration.disabled', True)
-            
-            # 禁用不必要的功能
-            options.set_preference('browser.cache.disk.enable', False)
-            options.set_preference('browser.cache.memory.enable', False)
-            options.set_preference('browser.cache.offline.enable', False)
-            options.set_preference('network.http.use-cache', False)
-            options.set_preference('browser.tabs.remote.autostart', False)
-            options.set_preference('browser.tabs.remote.autostart.2', False)
-            options.set_preference('dom.ipc.processCount', 1)
-            options.set_preference('browser.sessionstore.resume_from_crash', False)
-            
-            # 添加必要的参数
-            options.add_argument('--headless')
-            options.add_argument('--no-sandbox')
-            options.add_argument('--disable-dev-shm-usage')
-            options.add_argument('--disable-gpu')
-            options.add_argument('--disable-extensions')
-            options.add_argument('--disable-infobars')
-            options.add_argument('--disable-notifications')
-            options.add_argument('--window-size=1280,800')
-            
-            logger.info("创建 WebDriver 实例")
-            
-            # 使用临时目录
-            import tempfile
-            temp_dir = tempfile.mkdtemp()
-            
-            # Firefox 临时配置目录
-            options.set_preference('profile', temp_dir)
-            
-            # 创建 Service 对象,添加更多日志选项
-            service = Service(
-                geckodriver_path,
-                log_output=os.path.join(temp_dir, 'geckodriver.log'),
-                service_args=[
-                    '--log', 'trace',
-                    '--marionette-port', '2828'
-                ]
-            )
-            
-            # 设置超时限制并重试
-            max_attempts = 3
-            for attempt in range(max_attempts):
-                try:
-                    logger.info(f"尝试创建 WebDriver 实例 (尝试 {attempt + 1}/{max_attempts})")
-                    
-                    # 设置环境变量
-                    os.environ['MOZ_HEADLESS'] = '1'
-                    os.environ['DISPLAY'] = ':99'
-                    
-                    # 创建虚拟显示
-                    try:
-                        subprocess.run(['Xvfb', ':99', '-screen', '0', '1280x800x24'], 
-                                    start_new_session=True,
-                                    stdout=subprocess.DEVNULL,
-                                    stderr=subprocess.DEVNULL)
-                    except:
-                        logger.warning("Xvfb 启动失败,继续尝试")
-                    
-                    # 设置严格的超时
-                    with time_limit(20):  # 减少超时时间
-                        self.driver = webdriver.Firefox(
-                            service=service,
-                            options=options
-                        )
-                        
-                        # 设置页面加载超时
-                        self.driver.set_page_load_timeout(10)
-                        self.driver.set_script_timeout(10)
-                        self.wait = WebDriverWait(self.driver, 10)
-                        
-                        # 测试连接
-                        logger.info("测试浏览器连接")
-                        self.driver.get('about:blank')
-                        
-                        logger.info("浏览器驱动初始化成功")
-                        return True
-                        
-                except TimeoutException:
-                    logger.error(f"第 {attempt + 1} 次尝试超时")
-                except Exception as e:
-                    logger.error(f"第 {attempt + 1} 次尝试失败: {str(e)}")
-                
-                # 清理资源
-                self.cleanup_driver()
-                self.cleanup_processes()
-                
-                if attempt < max_attempts - 1:
-                    logger.info("等待后重试...")
-                    time.sleep(5)
-                else:
-                    raise Exception("在多次尝试后仍然失败")
+        self.start_time = int(time.time() * 1000)
+        self.data = {
+            "tdf": 1,
+            "ts": [],
+            "points": [],
+            "events": [],
+            "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
+            "tm": {
+                "st": self.start_time,
+                "ed": 0
+            }
+        }
+    
+    def add_point(self, x, y, t):
+        self.data["points"].append([x, y])
+        self.data["ts"].append(t - self.start_time)
+    
+    def add_event(self, type_, x, y):
+        t = int(time.time() * 1000) - self.start_time
+        self.data["events"].append({
+            "type": type_,
+            "t": t,
+            "x": x,
+            "y": y
+        })
+    
+    def get_data(self):
+        self.data["tm"]["ed"] = int(time.time() * 1000)
+        return base64.b64encode(json.dumps(self.data).encode()).decode()
+
+def generate_tracks(start_pos, target_pos):
+    tracks = []
+    distance = target_pos[0] - start_pos[0]
+    current = 0
+    t = int(time.time() * 1000)
+    v = 0  # 初始速度
+    
+    x = start_pos[0]
+    y = start_pos[1]
+    
+    while current < distance:
+        delta_t = 10  # 10ms
+        t += delta_t
         
-        except Exception as e:
-            logger.error(f"浏览器驱动初始化失败: {str(e)}")
-            self.cleanup_driver()
-            return False
-            
-        finally:
-            # 清理临时目录
-            try:
-                import shutil
-                shutil.rmtree(temp_dir, ignore_errors=True)
-            except:
-                pass
-            
-            # 停止虚拟显示
-            try:
-                subprocess.run(['pkill', 'Xvfb'], 
-                            stdout=subprocess.DEVNULL,
-                            stderr=subprocess.DEVNULL)
-            except:
-                pass
-
-    def cleanup_processes(self):
-        """更彻底地清理残留进程"""
-        try:
-            # 使用 pkill 清理进程
-            commands = [
-                ['pkill', '-f', 'firefox'],
-                ['pkill', '-f', 'geckodriver'],
-                ['pkill', '-f', 'Xvfb'],
-                ['killall', 'firefox'],
-                ['killall', 'geckodriver'],
-                ['killall', 'Xvfb']
-            ]
-            
-            for cmd in commands:
-                try:
-                    subprocess.run(cmd, 
-                                stdout=subprocess.DEVNULL,
-                                stderr=subprocess.DEVNULL)
-                except:
-                    continue
-            
-            # 使用 ps 查找并强制终止进程
-            try:
-                ps_output = subprocess.check_output(['ps', 'aux']).decode()
-                for line in ps_output.split('\n'):
-                    if 'firefox' in line or 'geckodriver' in line or 'Xvfb' in line:
-                        try:
-                            pid = int(line.split()[1])
-                            os.kill(pid, signal.SIGKILL)
-                        except:
-                            continue
-            except:
-                pass
-                
-            time.sleep(2)
-            logger.info("清理残留进程完成")
-        except Exception as e:
-            logger.error(f"清理进程时出错: {str(e)}")
-
-    def navigate_to_page(self):
-        """导航到目标页面"""
-        try:
-            logger.info("正在访问目标网页")
-            
-            max_attempts = 3
-            for attempt in range(max_attempts):
-                try:
-                    logger.info(f"尝试访问页面 (尝试 {attempt + 1}/{max_attempts})")
-                    
-                    # 增加页面加载超时时间
-                    self.driver.set_page_load_timeout(30)
-                    
-                    # 直接访问目标验证码页面
-                    target_url = 'https://open.yuewen.com/'
-                    logger.info(f"访问目标URL: {target_url}")
-                    self.driver.get(target_url)
-                    
-                    # 等待页面加载完成
-                    logger.info("等待页面加载")
-                    WebDriverWait(self.driver, 30).until(
-                        lambda driver: driver.execute_script("return document.readyState") == "complete"
-                    )
-                    
-                    # 检查页面是否正确加载
-                    try:
-                        # 验证是否存在验证码相关元素
-                        slide_button = WebDriverWait(self.driver, 10).until(
-                            EC.presence_of_element_located((By.CLASS_NAME, "geetest_slider_button"))
-                        )
-                        
-                        if not slide_button:
-                            raise Exception("未找到滑块验证码元素")
-                        
-                        logger.info("验证码页面加载成功")
-                        return True
-                        
-                    except Exception as e:
-                        logger.error(f"验证码元素检查失败: {str(e)}")
-                        raise
-                    
-                except Exception as e:
-                    logger.error(f"第 {attempt + 1} 次尝试失败: {str(e)}")
-                    if attempt < max_attempts - 1:
-                        logger.info("等待后重试...")
-                        
-                        # 重置浏览器状态
-                        try:
-                            self.driver.execute_script("""
-                                window.stop();
-                                window.location.href = 'about:blank';
-                            """)
-                        except:
-                            pass
-                        
-                        time.sleep(5)
-                    else:
-                        raise Exception("页面访问在多次尝试后仍然失败")
-                        
-        except Exception as e:
-            logger.error(f"页面访问失败: {str(e)}")
-            return False
+        # 加速阶段
+        if current < distance * 0.6:
+            v += (random.random() * 2 + 2)
+        # 减速阶段
+        else:
+            v *= 0.98
         
-        finally:
-            # 重置页面加载超时为默认值
-            try:
-                self.driver.set_page_load_timeout(30)
-            except:
-                pass
-
-
-
-    def setup_browser_config(self):
-        """配置浏览器网络设置"""
-        try:
-            # 配置网络设置
-            self.driver.execute_script("""
-                navigator.connection = {
-                    effectiveType: '4g',
-                    rtt: 50,
-                    downlink: 10,
-                    saveData: false
-                };
-            """)
-            
-            # 设置自定义请求头
-            self.driver.execute_cdp_cmd('Network.setUserAgentOverride', {
-                "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
-            })
-            
-            # 启用网络监控
-            self.driver.execute_cdp_cmd('Network.enable', {})
-            
-            # 设置网络条件
-            self.driver.execute_cdp_cmd('Network.emulateNetworkConditions', {
-                'offline': False,
-                'latency': 20,  # 延迟时间(毫秒)
-                'downloadThroughput': 780 * 1024 / 8,  # 下载速度(字节/秒)
-                'uploadThroughput': 330 * 1024 / 8,  # 上传速度(字节/秒)
-                'connectionType': 'wifi'
-            })
-            
-            return True
-        except Exception as e:
-            logger.error(f"配置浏览器网络设置失败: {str(e)}")
-            return False
-    def get_slider(self):
-        """获取滑块元素"""
-        try:
-            logger.info("寻找滑块元素")
-            
-            # 尝试多种定位方式
-            selectors = [
-                (By.CLASS_NAME, "gt_slider_knob"),
-                (By.CLASS_NAME, "geetest_slider_button"),
-                (By.CLASS_NAME, "gt_slider_knob_new"),
-                (By.CSS_SELECTOR, ".gt_slider_knob"),
-                (By.CSS_SELECTOR, ".geetest_slider_button"),
-                (By.XPATH, "//div[contains(@class, 'slider')]//div[contains(@class, 'knob')]")
-            ]
-            
-            # 等待任意一个元素出现
-            for selector in selectors:
-                try:
-                    logger.info(f"尝试使用选择器: {selector}")
-                    element = WebDriverWait(self.driver, 10).until(
-                        EC.presence_of_element_located(selector)
-                    )
-                    if element:
-                        logger.info(f"成功找到滑块元素: {selector}")
-                        return element
-                except:
-                    continue
-            
-            raise Exception("未能找到滑块元素")
-            
-        except Exception as e:
-            logger.error(f"获取滑块元素失败: {str(e)}")
-            return None
-
-    def get_slider_background(self):
-        """获取背景图片"""
-        try:
-            # 等待背景图片加载
-            background = WebDriverWait(self.driver, 10).until(
-                EC.presence_of_element_located((By.CLASS_NAME, "gt_box"))
-            )
-            # 获取背景图片的base64数据
-            canvas = self.driver.execute_script(
-                "return document.getElementsByClassName('gt_box')[0].toDataURL('image/png')"
-            )
-            # 转换base64为图片
-            canvas = canvas.split(',')[1]
-            image_data = base64.b64decode(canvas)
-            image = Image.open(io.BytesIO(image_data))
-            return image
-        except Exception as e:
-            logger.error(f"获取背景图片失败: {str(e)}")
-            return None
-
-    def get_slider_image(self):
-        """获取滑块图片"""
-        try:
-            # 等待滑块图片加载
-            slider = WebDriverWait(self.driver, 10).until(
-                EC.presence_of_element_located((By.CLASS_NAME, "gt_slice"))
-            )
-            # 获取滑块图片的base64数据
-            canvas = self.driver.execute_script(
-                "return document.getElementsByClassName('gt_slice')[0].toDataURL('image/png')"
-            )
-            # 转换base64为图片
-            canvas = canvas.split(',')[1]
-            image_data = base64.b64decode(canvas)
-            image = Image.open(io.BytesIO(image_data))
-            return image
-        except Exception as e:
-            logger.error(f"获取滑块图片失败: {str(e)}")
-            return None
-
-    def get_gap(self, bg_image, slider_image):
-        """计算滑块缺口位置"""
-        try:
-            # 转换图片格式
-            bg = cv2.cvtColor(np.array(bg_image), cv2.COLOR_RGB2BGR)
-            slider = cv2.cvtColor(np.array(slider_image), cv2.COLOR_RGB2BGR)
-            
-            # 计算差异
-            diff = cv2.absdiff(bg, slider)
-            mask = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY)
-            ret, mask = cv2.threshold(mask, 127, 255, cv2.THRESH_BINARY)
-            
-            # 查找轮廓
-            contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
-            
-            if contours:
-                # 获取最大轮廓
-                max_contour = max(contours, key=cv2.contourArea)
-                x, y, w, h = cv2.boundingRect(max_contour)
-                return x
-            return None
-        except Exception as e:
-            logger.error(f"计算缺口位置失败: {str(e)}")
-            return None
-
-    def generate_track(self, distance):
-        """生成移动轨迹"""
-        tracks = []
-        current = 0
-        mid = distance * 3 / 4
-        t = 0.2
-        v = 0
+        # 位移
+        delta_x = v * (delta_t / 1000)
+        current += delta_x
+        x += delta_x
         
-        while current < distance:
-            if current < mid:
-                a = 2
-            else:
-                a = -3
-            v0 = v
-            v = v0 + a * t
-            move = v0 * t + 1 / 2 * a * t * t
-            current += move
-            tracks.append(round(move))
+        # y轴抖动
+        y = start_pos[1] + (random.random() * 2 - 1)
         
-        # 微调
-        while sum(tracks) > distance:
-            tracks[-1] -= 1
-        while sum(tracks) < distance:
-            tracks.append(1)
-            
-        # 添加回退
-        tracks.extend([-1, -1, -2, -2, -1, -1])
-        return tracks
-
-    def move_slider(self, slider, tracks):
-        """移动滑块"""
-        try:
-            ActionChains(self.driver).click_and_hold(slider).perform()
-            for track in tracks:
-                ActionChains(self.driver).move_by_offset(track, random.randint(-1, 1)).perform()
-                time.sleep(random.uniform(0.01, 0.02))
-            time.sleep(0.5)
-            ActionChains(self.driver).release().perform()
-            return True
-        except Exception as e:
-            logger.error(f"移动滑块失败: {str(e)}")
-            return False
-
-    def crack_captcha(self):
-        """破解验证码主流程"""
-        try:
-            logger.info("开始破解验证码")
-            
-            # 获取滑块元素
-            slider = self.get_slider()
-            if not slider:
-                return False
-                
-            # 获取图片
-            bg_image = self.get_slider_background()
-            slider_image = self.get_slider_image()
-            if not bg_image or not slider_image:
-                return False
-                
-            # 计算缺口位置
-            gap = self.get_gap(bg_image, slider_image)
-            if not gap:
-                return False
-            logger.info(f"缺口位置: {gap}")
-            
-            # 生成轨迹
-            tracks = self.generate_track(gap)
-            logger.info(f"生成轨迹: {len(tracks)}个点")
-            
-            # 移动滑块
-            result = self.move_slider(slider, tracks)
-            if not result:
-                return False
-                
-            # 等待验证结果
-            time.sleep(2)
-            
-            # 检查是否验证成功
-            try:
-                success = WebDriverWait(self.driver, 5).until(
-                    EC.presence_of_element_located((By.CLASS_NAME, "gt_success"))
-                )
-                if success:
-                    logger.info("验证成功")
-                    return True
-            except:
-                logger.error("验证失败")
-                return False
-                
-        except Exception as e:
-            logger.error(f"验证码破解失败: {str(e)}")
-            return False
-
-    def cleanup_driver(self):
-        """清理 WebDriver 资源"""
-        if hasattr(self, 'driver') and self.driver:
-            try:
-                self.driver.quit()
-            except:
-                pass
-            finally:
-                self.driver = None
-
-    def run(self):
-        """运行主程序"""
-        try:
-            logger.info("开始破解验证码流程")
-            
-            # 初始化浏览器
-            if not self.setup_driver():
-                return False
-                
-            # 访问目标页面
-            if not self.navigate_to_page():
-                return False
-                
-            # 破解验证码
-            max_attempts = 3
-            for attempt in range(max_attempts):
-                logger.info(f"第 {attempt + 1} 次尝试破解验证码")
-                if self.crack_captcha():
-                    return True
-                time.sleep(2)
+        tracks.append({
+            "x": round(x),
+            "y": round(y),
+            "t": t
+        })
+    
+    # 确保最后到达目标位置
+    tracks.append({
+        "x": target_pos[0],
+        "y": target_pos[1],
+        "t": t + 10
+    })
+    
+    return tracks
+
+def get_collect(start_pos, target_pos):
+    tdc = TDCSimulator()
+    
+    # 开始事件
+    tdc.add_event("start", start_pos[0], start_pos[1])
+    
+    # 按下事件
+    tdc.add_event("down", start_pos[0], start_pos[1])
+    
+    # 移动轨迹
+    tracks = generate_tracks(start_pos, target_pos)
+    for track in tracks:
+        tdc.add_point(track["x"], track["y"], track["t"])
+    
+    # 松开事件
+    tdc.add_event("up", target_pos[0], target_pos[1])
+    
+    # 结束事件
+    tdc.add_event("end", target_pos[0], target_pos[1])
+    
+    return tdc.get_data()
+
+def get_gap(target_img:bytes, background_img:bytes):
+    det = ddddocr.DdddOcr(det=False, ocr=False, show_ad=False)
+    res = det.slide_match(target_img, background_img,simple_target=True)
+    print(res)
+    return res["target"][0]
+def parse_positions(data):
+    """
+    解析验证码位置信息
+    
+    参数:
+    data: _aq_88765 返回的数据
+    
+    返回:
+    {
+        "start_pos": [x, y],
+        "move_limit": [min_x, max_x],
+        "sess": session_id,
+        "image_urls": {
+            "bg": background_image_url,
+            "sprite": sprite_image_url
+        }
+    }
+    """
+    try:
+        # 获取滑块信息
+        fg_elem_list = data["data"]["dyn_show_info"]["fg_elem_list"]
+        slider_info = None
+        for elem in fg_elem_list:
+            if elem["id"] == 1:  # id为1的元素是滑块
+                slider_info = elem
+                break
                 
-            return False
-            
-        except Exception as e:
-            logger.error(f"程序执行失败: {str(e)}")
-            return False
-            
-        finally:
-            logger.info("程序执行结束")
-            self.cleanup_driver()
+        if not slider_info:
+            raise ValueError("未找到滑块信息")
+            
+        # 解析移动限制
+        track_limit = slider_info["move_cfg"]["track_limit"]  # "x>=50&&x<=552"
+        limits = track_limit.replace("x>=", "").replace("&&x<=", ",").split(",")
+        min_x = int(limits[0])
+        max_x = int(limits[1])
+        
+        # 获取图片URL
+        bg_img_url = data["data"]["dyn_show_info"]["bg_elem_cfg"]["img_url"]
+        sprite_url = data["data"]["dyn_show_info"]["sprite_url"]
+        
+        # 获取session
+        sess = data["sess"]
+        
+        return {
+            "start_pos": slider_info["init_pos"],  # [50, 226]
+            "move_limit": [min_x, max_x],          # [50, 552]
+            "sess": sess,
+            "image_urls": {
+                "bg": bg_img_url,
+                "sprite": sprite_url
+            }
+        }
+        
+    except Exception as e:
+        print(f"解析位置信息失败: {str(e)}")
+        return None
+
+def get_captcha_prehandle():
+    # 基础URL
+    base_url = "https://turing.captcha.qcloud.com/cap_union_prehandle"
+    
+    # 构造请求参数
+    params = {
+        "aid": "2026859470",
+        "protocol": "https",
+        "accver": "1",
+        "showtype": "popup",
+        "ua": "TW96aWxsYS81LjAgKFdpbmRvd3MgTlQgMTAuMDsgV2luNjQ7IHg2NCkgQXBwbGVXZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzEzMi4wLjAuMCBTYWZhcmkvNTM3",
+        "noheader": "1",
+        "fb": "1",
+        "aged": "0",
+        "enableAged": "0",
+        "enableDarkMode": "0",
+        "grayscale": "1",
+        "clientype": "2",
+        "cap_cd": "",
+        "uid": "",
+        "lang": "zh-cn",
+        "entry_url": "https://open.yuewen.com/",
+        "elder_captcha": "0",
+        "js": "/tcaptcha-frame.c055d939.js",
+        "login_appid": "",
+        "wb": "1",
+        "subsid": "5",
+        "callback": "_aq_88765",
+        "sess": ""
+    }
+    
+    # 设置请求头
+    headers = {
+        "accept": "*/*",
+        "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
+        "sec-ch-ua": '"Not A(Brand";v="8", "Chromium";v="132", "Microsoft Edge";v="132"',
+        "sec-ch-ua-mobile": "?0",
+        "sec-ch-ua-platform": '"Windows"',
+        "sec-fetch-dest": "script",
+        "sec-fetch-mode": "no-cors",
+        "sec-fetch-site": "cross-site",
+        "Referer": "https://open.yuewen.com/",
+        "Referrer-Policy": "strict-origin-when-cross-origin"
+    }
+    
+    try:
+        # 发送GET请求
+        response = requests.get(
+            base_url,
+            params=params,
+            headers=headers
+        )
+        
+        # 检查响应状态
+        response.raise_for_status()
+        
+        # 获取响应文本
+        return response.text
+        
+    except requests.exceptions.RequestException as e:
+        print(f"请求失败: {str(e)}")
+        return None
 
-    def __del__(self):
-        """析构函数,确保资源被清理"""
-        self.cleanup_driver()
-        self.cleanup_processes()
+# start_pos = [50, 226]    # 滑块初始位置
+# target_pos = [342, 226]  # 目标位置
 
-if __name__ == "__main__":
-    cracker = GeetestCracker()
-    result = cracker.run()
-    print("破解结果:", "成功" if result else "失败")
+# collect = get_collect(start_pos, target_pos)
+# print(collect)

+ 5 - 4
logs/combined.log

@@ -1,4 +1,5 @@
-2025-01-20T16:48:07: 
-2025-01-20T16:48:07: > heiyan@1.0.0 start
-2025-01-20T16:48:07: > node tg_factory_main.js
-2025-01-20T16:48:07: 
+2025-01-24T12:39:43: 
+2025-01-24T12:39:43: > heiyan@1.0.0 start
+2025-01-24T12:39:43: > node tg_factory_main.js
+2025-01-24T12:39:43: 
+2025-01-24T12:39:46: Connected to Redis

+ 5 - 4
logs/out.log

@@ -1,4 +1,5 @@
-2025-01-20T16:48:07: 
-2025-01-20T16:48:07: > heiyan@1.0.0 start
-2025-01-20T16:48:07: > node tg_factory_main.js
-2025-01-20T16:48:07: 
+2025-01-24T12:39:43: 
+2025-01-24T12:39:43: > heiyan@1.0.0 start
+2025-01-24T12:39:43: > node tg_factory_main.js
+2025-01-24T12:39:43: 
+2025-01-24T12:39:46: Connected to Redis

+ 1 - 0
node_modules/.bin/prebuild-install

@@ -0,0 +1 @@
+../prebuild-install/bin.js

+ 1 - 0
node_modules/.bin/rc

@@ -0,0 +1 @@
+../rc/cli.js

+ 28 - 0
pm2createlink.json

@@ -69,6 +69,34 @@
             "merge_logs": true,
             "exec_mode": "fork",
             "autorestart": true
+        },
+        {
+            "name": "yw_create_link_factory",
+            "script": "./5_CREATE_LINK_FACTORY/yw_create_link_factory.js",
+            "node_args": "--max-old-space-size=8192 --expose-gc",
+            "max_memory_restart": "4G",
+            "args": ["--type=data"],
+            "cwd": "/home/tg_factory",
+            "log_date_format": "YYYY-MM-DD HH:mm:ss",
+            "error_file": "./logs/create_link/yw_create_link_factory_error.log",
+            "out_file": "./logs/create_link/yw_create_link_factory_out.log",
+            "merge_logs": true,
+            "exec_mode": "fork",
+            "autorestart": true
+        },
+        {
+            "name": "zh_create_link_factory",
+            "script": "./5_CREATE_LINK_FACTORY/zh_create_link_factory.js",
+            "node_args": "--max-old-space-size=8192 --expose-gc",
+            "max_memory_restart": "4G",
+            "args": ["--type=data"],
+            "cwd": "/home/tg_factory",
+            "log_date_format": "YYYY-MM-DD HH:mm:ss",
+            "error_file": "./logs/create_link/zh_create_link_factory_error.log",
+            "out_file": "./logs/create_link/zh_create_link_factory_out.log",
+            "merge_logs": true,
+            "exec_mode": "fork",
+            "autorestart": true
         }
     ]
 }

+ 14 - 0
pm2json.json

@@ -139,6 +139,20 @@
             "exec_mode": "fork",
             "autorestart": true
         },
+        {
+            "name": "ZH_FACTORY",
+            "script": "./2_PRODUCT_FACTORY/zh_batch_factory.js",
+            "node_args": "--max-old-space-size=8192 --expose-gc",
+            "max_memory_restart": "4G",
+            "args": ["--type=data"],
+            "cwd": "/home/tg_factory",
+            "log_date_format": "YYYY-MM-DD HH:mm:ss",
+            "error_file": "./logs/zh_batch_factory_error.log",
+            "out_file": "./logs/zh_batch_factory_out.log",
+            "merge_logs": true,
+            "exec_mode": "fork",
+            "autorestart": true
+        },
         {
             "name": "7_CHECK_AND_CREATE_URL_FACTORY",
             "script": "./7_CHECK_AND_CREATE_URL_FACTORY/CHECK_AND_CREATE_URL_FACTORY.js",

BIN
src/api/hy/qr.gif


BIN
src/api/hy/qr_resized.gif


BIN
src/api/yw/0.png


BIN
src/api/yw/1.png


+ 15 - 0
src/api/yw/tdc.js

@@ -0,0 +1,15 @@
+const { Builder, By } = require('selenium-webdriver')
+
+;(async () => {
+  /**
+   * 打开验证码
+   */
+  // 打开浏览器
+  const driver = new Builder().forBrowser('chrome').build()
+  // 加载腾讯防水墙官网
+  await driver.get('https://007.qq.com/online.html');
+  // 点击体验验证码按钮
+  (await driver.findElement(By.id('code'))).click()
+  // 等验证码加载完
+  await driver.sleep(2000)
+})

+ 105 - 0
src/api/yw/yw_login.js

@@ -3,8 +3,113 @@ const fetch = require('node-fetch'); // Node.js 18以下版本需要安装 node-
 const tools = require('../../../tools');
 const config  = require('../../../etc/config.json');
 const redis_help = require('../../use_redis');
+const helper = require('../../helper');
+const fs = require('fs');
 const CMD = {}
+const folder = __dirname; // 
 
+const axios = require('axios');
+async function fetchImage(image,file_name) {
+    try {
+        // 1. 先获取新的session
+        const prehandleResponse = await axios({
+            url: 'https://turing.captcha.qcloud.com/cap_union_prehandle',
+            method: 'GET',
+            params: {
+                aid: '2026859470',
+                protocol: 'https',
+                accver: '1',
+                showtype: 'popup',
+                ua: 'TW96aWxsYS81LjAgKFdpbmRvd3MgTlQgMTAuMDsgV2luNjQ7IHg2NCkgQXBwbGVXZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzEzMi4wLjAuMCBTYWZhcmkvNTM3LjM2',
+                callback: '_aq_88765'
+            }
+        });
+
+        // 2. 解析JSONP响应获取新session
+        const jsonpData = prehandleResponse.data;
+        const jsonStr = jsonpData.replace(/^_aq_88765\((.*)\)$/, '$1');
+        const data = JSON.parse(jsonStr);
+        const newSess = data.sess;
+
+        console.log('New session:', newSess);
+
+        // 3. 使用新session请求图片
+        const response = await axios({
+            url: `https://turing.captcha.qcloud.com/cap_union_new_getcapbysig`,
+            method: 'GET',
+            params: {
+                img_index: file_name,
+                image: image,
+                sess: newSess
+            },
+            responseType: 'arraybuffer',
+            headers: {
+                'Accept': 'image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8',
+                'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
+                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36',
+                'Referer': 'https://turing.captcha.gtimg.com/',
+                'Origin': 'https://turing.captcha.gtimg.com',
+                'Connection': 'keep-alive',
+                'Cache-Control': 'no-cache'
+            },
+            timeout: 5000,
+            maxRedirects: 5,
+            validateStatus: function (status) {
+                return status >= 200 && status < 300;
+            }
+        });
+
+        console.log('Response status:', response.status);
+        console.log('Response headers:', response.headers);
+        console.log('Data length:', response.data.length);
+
+        if (response.data.length === 0) {
+            throw new Error('Empty response received');
+        }
+
+        // 保存图片
+        // await fs.writeFile('captcha.png', response.data);
+        console.log('Image saved successfully',response.data);
+        const imgPath = `${folder}/${file_name}.png`;
+        fs.writeFileSync(imgPath, response.data);
+        return response.data;
+
+    } catch (error) {
+        console.error('Error:', error);
+        if (error.response) {
+            console.error('Response status:', error.response.status);
+            console.error('Response headers:', error.response.headers);
+        }
+        throw error;
+    }
+}
+
+
+CMD.get_captcha = async function(){
+    let response = await fetch("https://turing.captcha.qcloud.com/cap_union_prehandle?aid=2026859470&protocol=https&accver=1&showtype=popup&ua=TW96aWxsYS81LjAgKFdpbmRvd3MgTlQgMTAuMDsgV2luNjQ7IHg2NCkgQXBwbGVXZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzEzMi4wLjAuMCBTYWZhcmkvNTM3LjM2IEVkZy8xMzIuMC4wLjA%3D&noheader=1&fb=1&aged=0&enableAged=0&enableDarkMode=0&grayscale=1&clientype=2&cap_cd=&uid=&lang=zh-cn&entry_url=https%3A%2F%2Fopen.yuewen.com%2F&elder_captcha=0&js=%2Ftcaptcha-frame.c055d939.js&login_appid=&wb=1&subsid=5&callback=_aq_88765&sess=", {
+      "headers": {
+        "accept": "*/*",
+        "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
+        "sec-ch-ua": "\"Not A(Brand\";v=\"8\", \"Chromium\";v=\"132\", \"Microsoft Edge\";v=\"132\"",
+        "sec-ch-ua-mobile": "?0",
+        "sec-ch-ua-platform": "\"Windows\"",
+        "sec-fetch-dest": "script",
+        "sec-fetch-mode": "no-cors",
+        "sec-fetch-site": "cross-site",
+        "Referer": "https://open.yuewen.com/",
+        "Referrer-Policy": "strict-origin-when-cross-origin"
+      },
+      "body": null,
+      "method": "GET"
+    });
+    // 获取响应文本
+    const text = await response.text();
+    console.log("response.data:",text)
+    let img_config = helper.parseJsonP(text)
+    fetchImage(img_config.img_url_0,0)
+    fetchImage(img_config.img_url_1,1)
+    console.log("img_config:",img_config)
+}
 CMD.yw_login = async function() {
   try{
       let OPENSESSID = await redis_help.getKeyValue("OPENSESSID")

+ 9 - 8
src/api/yw/yw_search_book.js

@@ -30,22 +30,23 @@ CMD.search_id = async function (bookId) {
         if(data.code!=0){
             throw data
         }
-        // console.log('响应状态:', response.status);
-        // console.log('响应数据:', data.data.list[0]);
+        console.log('响应状态:', response.status);
         if(data.data.list.length<=0){
             return null
         }
+        console.log('响应数据:', data.data.list[0]);
         let book_info = data.data.list[0]
         let info = {}
         info.words =  book_info.WordsCntShow
         info.book_name =  book_info.BookName
         info.book_id =  book_info.cbid
-        info.genre = 999;
-        if(book_info.CategoryName=="短篇"){  
-            info.genre = 3;
-        }else{
-            info.genre = 1;
-        }
+        // info.genre = 999;
+        info.genre = 3;
+        // if(book_info.CategoryName=="短篇"){  
+        //     info.genre = 3;
+        // }else{
+        //     info.genre = 1;
+        // }
         return info;
     } catch (error) {
         return null

+ 46 - 0
src/api/zh/get_zh_call_back_template.js

@@ -0,0 +1,46 @@
+const fetch = require('node-fetch'); // Node.js 18以下版本需要安装 node-fetch
+const tools = require('../../../tools');
+const config  = require('../../../etc/config.json');
+const redis_help = require('../../use_redis');
+const helper = require('../../helper');
+const CMD = {}
+
+CMD.get_zh_call_back_template = async function() {
+    try{
+        let timestamp = helper.getCurrentUnixTimestamp()
+        let url = `https://ecom.zhihu.com/api/km-agent/callback-rules?offset=0&limit=20`
+        const response =   await fetch(url, {
+            "headers": {
+                "accept": "application/json, text/plain, */*",
+                "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
+                "priority": "u=1, i",
+                "sec-ch-ua": "\"Not A(Brand\";v=\"8\", \"Chromium\";v=\"132\", \"Microsoft Edge\";v=\"132\"",
+                "sec-ch-ua-mobile": "?0",
+                "sec-ch-ua-platform": "\"Windows\"",
+                "sec-fetch-dest": "empty",
+                "sec-fetch-mode": "cors",
+                "sec-fetch-site": "same-origin",
+                "x-requested-with": "XMLHttpRequest",
+                "cookie": "_xsrf=7bCsoKjmfrIE0D36ajUc9s6CM9OxdPHQ; _zap=aa0b20fb-9951-4f27-9f66-dafff809f1a9; d_c0=ADASSwlwjxmPTrW0768vZ0BKygxzh_LFDDI=|1731902666; __zse_ck=004_RAwxSX7iODkHf=KcaqfMYfLtUB=liEm/v0fmgVHxEqKYzUEWDsJ4I5wkbR1yz3X=sAOsMm7PcwNXoQHjg5IxNkMc38yFAYS9Vcvho/AB5CWvfKZL7JZ6yb8JNCk=hJhs-xC0Hm7QqNBqkjTYlQ0LGKY50XF8sCD+ku+G/FUcrxDHtM9cxwgjtwoE7C9tAtkeLkUwyYVe6lZG1PpW/gndPL1lhcrZ53odQtwGooASih8FG2HI3W7Xpo3Bq+eqzshzK; Hm_lvt_98beee57fd2ef70ccdd5ca52b9740c49=1735787111,1735909747,1736148711; captcha_session_v2=2|1:0|10:1736148772|18:captcha_session_v2|88:YkZmazRKNXExV2dHaTkvaWIyQkJaUXpPZDFEbFNwWTR1bjNKK0dpUUdJRTcxZldZOUtFQlFSMUQzT1h1TFlNZw==|7015dcf5d48280e56784ef6d89a2b4d15352adcb1e257453c9907a664ceaa0d9; captcha_ticket_v2=2|1:0|10:1736148810|17:captcha_ticket_v2|728:eyJ2YWxpZGF0ZSI6IkNOMzFfKkxBeEhrcDFRYnhkeGxtTGRnSlZZWVdHbjhtLlc4R3BmQWl5dFNLYlA1ekZITXYydi5KRypfa3plX3poZGJRRExveDlSR3lFVDJJbmQ4Q1ZHNnJFSWkqdyppMDZKMzlXYVZJcFlIX0VHc0ZRRDBsa1BvblJrNVJldWVDNFRNeFBXRmZZcXVJWHB1Yip0ckY1SWFQdVNoS0YyKjZmUHhUKnFiOXpDaFVsc0djdHVBX1hjWENfYkNQemlxREwwSWpWekdSZHlqWUFWQ0d5aE5TaWFzZEhzdWRDWCprYy5QKkh6ajV6VU9nM2lQT296YVBxSzlXemYqcXZzRF9ENWVPU2dGQmhoUWd3d3RZb3ByT0t0M0ZpVSp3SUZsc1FmZTVMRDJwSDhTcHBOZUF4VkhWYkVEdC4zVU1USkl3SjhRWCoqb0xrKnlhU2RudDNXOElfV01mMV84RzF1d05NeUpJS0YqcmJ3MTBsejNDX2lnMFJYM2FMUTBZT04ySDFqRkpZYlNCMCpfVjJseENYdmRPMEtZanJqTEh2b3RFbERnc2xzTDF4eS5TSWZSU3owY3kxRmFpU3hadkNxV00ubUJvbExKQy5JR1BvMG1uLmtNZHRuNElMMGx6NE9DcmdKMXFHeV81aUhUMGhNajZxVTRmdFlSU1JlNUxlbkJGWWdzcnZWZnFxNVk3N192X2lfMSJ9|9a0d71076f5273fc22dd67c3e4eabb975e479e875191d79a44e51972d2d848cc; z_c0=2|1:0|10:1736148810|4:z_c0|92:Mi4xN1lrbldnQUFBQUFBTUJKTENYQ1BHU1lBQUFCZ0FsVk5TdFZvYUFCUDI3aTJFNk5MNThBZ215bXIyZVJIWXhvNXN3|707e10b967760748115ffeee34231345d9323f18a7e61ff853b0d3cf9967dae2; ecom_merchant=1857114769787609088; BEC=46faae78ffea44ab7c29d705bdab5c18",
+                "Referer": "https://ecom.zhihu.com/vip-apps/out-distribution/promotion/callback/list",
+                "Referrer-Policy": "no-referrer-when-downgrade"
+            },
+            "body": null,
+            "method": "GET"
+          });
+    
+          const data =  await response.json();
+          if(data.data!=undefined&&data.data.length<=0){
+            throw data
+        }
+          console.log('get_zh_call_back_template::响应状态:', response.status);
+          console.log('get_zh_call_back_template::响应数据:', data);
+          return data.data;
+    }catch(error){
+        console.error('get_zh_call_back_template::请求错误:', error);
+        return null;
+    }
+
+}
+
+module.exports = CMD;

+ 47 - 0
src/api/zh/get_zh_recharge_template.js

@@ -0,0 +1,47 @@
+const fetch = require('node-fetch'); // Node.js 18以下版本需要安装 node-fetch
+const tools = require('../../../tools');
+const config  = require('../../../etc/config.json');
+const redis_help = require('../../use_redis');
+const helper = require('../../helper');
+const CMD = {}
+// app_type = 4 是抖音
+// app_type = 6 是微信
+CMD.get_zh_recharge_template = async function(app_type) {
+    try{
+        let timestamp = helper.getCurrentUnixTimestamp()
+        let url = `https://ecom.zhihu.com/api/km-agent/payment_templates?app_type=${app_type}&app_id=&media_channel_type=5`
+        const response =   await fetch(url, {
+            "headers": {
+            "accept": "application/json, text/plain, */*",
+            "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
+            "priority": "u=1, i",
+            "sec-ch-ua": "\"Not A(Brand\";v=\"8\", \"Chromium\";v=\"132\", \"Microsoft Edge\";v=\"132\"",
+            "sec-ch-ua-mobile": "?0",
+            "sec-ch-ua-platform": "\"Windows\"",
+            "sec-fetch-dest": "empty",
+            "sec-fetch-mode": "cors",
+            "sec-fetch-site": "same-origin",
+            "x-requested-with": "XMLHttpRequest",
+            "cookie": "_xsrf=7bCsoKjmfrIE0D36ajUc9s6CM9OxdPHQ; _zap=aa0b20fb-9951-4f27-9f66-dafff809f1a9; d_c0=ADASSwlwjxmPTrW0768vZ0BKygxzh_LFDDI=|1731902666; __zse_ck=004_RAwxSX7iODkHf=KcaqfMYfLtUB=liEm/v0fmgVHxEqKYzUEWDsJ4I5wkbR1yz3X=sAOsMm7PcwNXoQHjg5IxNkMc38yFAYS9Vcvho/AB5CWvfKZL7JZ6yb8JNCk=hJhs-xC0Hm7QqNBqkjTYlQ0LGKY50XF8sCD+ku+G/FUcrxDHtM9cxwgjtwoE7C9tAtkeLkUwyYVe6lZG1PpW/gndPL1lhcrZ53odQtwGooASih8FG2HI3W7Xpo3Bq+eqzshzK; Hm_lvt_98beee57fd2ef70ccdd5ca52b9740c49=1735787111,1735909747,1736148711; captcha_session_v2=2|1:0|10:1736148772|18:captcha_session_v2|88:YkZmazRKNXExV2dHaTkvaWIyQkJaUXpPZDFEbFNwWTR1bjNKK0dpUUdJRTcxZldZOUtFQlFSMUQzT1h1TFlNZw==|7015dcf5d48280e56784ef6d89a2b4d15352adcb1e257453c9907a664ceaa0d9; captcha_ticket_v2=2|1:0|10:1736148810|17:captcha_ticket_v2|728:eyJ2YWxpZGF0ZSI6IkNOMzFfKkxBeEhrcDFRYnhkeGxtTGRnSlZZWVdHbjhtLlc4R3BmQWl5dFNLYlA1ekZITXYydi5KRypfa3plX3poZGJRRExveDlSR3lFVDJJbmQ4Q1ZHNnJFSWkqdyppMDZKMzlXYVZJcFlIX0VHc0ZRRDBsa1BvblJrNVJldWVDNFRNeFBXRmZZcXVJWHB1Yip0ckY1SWFQdVNoS0YyKjZmUHhUKnFiOXpDaFVsc0djdHVBX1hjWENfYkNQemlxREwwSWpWekdSZHlqWUFWQ0d5aE5TaWFzZEhzdWRDWCprYy5QKkh6ajV6VU9nM2lQT296YVBxSzlXemYqcXZzRF9ENWVPU2dGQmhoUWd3d3RZb3ByT0t0M0ZpVSp3SUZsc1FmZTVMRDJwSDhTcHBOZUF4VkhWYkVEdC4zVU1USkl3SjhRWCoqb0xrKnlhU2RudDNXOElfV01mMV84RzF1d05NeUpJS0YqcmJ3MTBsejNDX2lnMFJYM2FMUTBZT04ySDFqRkpZYlNCMCpfVjJseENYdmRPMEtZanJqTEh2b3RFbERnc2xzTDF4eS5TSWZSU3owY3kxRmFpU3hadkNxV00ubUJvbExKQy5JR1BvMG1uLmtNZHRuNElMMGx6NE9DcmdKMXFHeV81aUhUMGhNajZxVTRmdFlSU1JlNUxlbkJGWWdzcnZWZnFxNVk3N192X2lfMSJ9|9a0d71076f5273fc22dd67c3e4eabb975e479e875191d79a44e51972d2d848cc; z_c0=2|1:0|10:1736148810|4:z_c0|92:Mi4xN1lrbldnQUFBQUFBTUJKTENYQ1BHU1lBQUFCZ0FsVk5TdFZvYUFCUDI3aTJFNk5MNThBZ215bXIyZVJIWXhvNXN3|707e10b967760748115ffeee34231345d9323f18a7e61ff853b0d3cf9967dae2; ecom_merchant=1857114769787609088; BEC=46faae78ffea44ab7c29d705bdab5c18",
+            "Referer": "https://ecom.zhihu.com/vip-apps/out-distribution/promotion/recharge-template/list",
+            "Referrer-Policy": "no-referrer-when-downgrade"
+            },
+            "body": null,
+            "method": "GET"
+          });
+    
+          const data =  await response.json();
+          console.log('get_zh_recharge_template::响应数据:', data);
+          if(data.data!=undefined&&data.data.length<=0){
+              throw data
+          }
+          console.log('get_zh_recharge_template::响应状态:', response.status);
+          return data.data;
+    }catch(error){
+        console.error('get_zh_recharge_template::请求错误:', error);
+        return null;
+    }
+
+}
+
+module.exports = CMD;

+ 52 - 0
src/api/zh/zh_search_book.js

@@ -0,0 +1,52 @@
+const fetch = require('node-fetch'); // Node.js 18以下版本需要安装 node-fetch
+const tools = require('../../../tools');
+const config  = require('../../../etc/config.json');
+const redis_help = require('../../use_redis');
+const helper = require('../../helper');
+const CMD = {}
+CMD.search_id = async function(bookId) {
+    try{
+        let timestamp = helper.getCurrentUnixTimestamp()
+        let url = `https://ecom.zhihu.com/api/km-agent/works?work=${bookId}&channel_type=&theme_id=&app_type=4&app_id=tt2ffb5946b0cb808001&has_ad_link=&online_time_start=&online_time_end=&recommend_reason=&recommend_level=&order_by=&multi_sections=&offset=0&limit=20&labels=undefined`
+        const response =   await fetch(url, {
+            "headers": {
+                "accept": "application/json, text/plain, */*",
+                "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
+                "priority": "u=1, i",
+                "sec-ch-ua": "\"Not A(Brand\";v=\"8\", \"Chromium\";v=\"132\", \"Microsoft Edge\";v=\"132\"",
+                "sec-ch-ua-mobile": "?0",
+                "sec-ch-ua-platform": "\"Windows\"",
+                "sec-fetch-dest": "empty",
+                "sec-fetch-mode": "cors",
+                "sec-fetch-site": "same-origin",
+                "x-requested-with": "XMLHttpRequest",
+                "cookie": "_xsrf=7bCsoKjmfrIE0D36ajUc9s6CM9OxdPHQ; _zap=aa0b20fb-9951-4f27-9f66-dafff809f1a9; d_c0=ADASSwlwjxmPTrW0768vZ0BKygxzh_LFDDI=|1731902666; __zse_ck=004_RAwxSX7iODkHf=KcaqfMYfLtUB=liEm/v0fmgVHxEqKYzUEWDsJ4I5wkbR1yz3X=sAOsMm7PcwNXoQHjg5IxNkMc38yFAYS9Vcvho/AB5CWvfKZL7JZ6yb8JNCk=hJhs-xC0Hm7QqNBqkjTYlQ0LGKY50XF8sCD+ku+G/FUcrxDHtM9cxwgjtwoE7C9tAtkeLkUwyYVe6lZG1PpW/gndPL1lhcrZ53odQtwGooASih8FG2HI3W7Xpo3Bq+eqzshzK; Hm_lvt_98beee57fd2ef70ccdd5ca52b9740c49=1735787111,1735909747,1736148711; captcha_session_v2=2|1:0|10:1736148772|18:captcha_session_v2|88:YkZmazRKNXExV2dHaTkvaWIyQkJaUXpPZDFEbFNwWTR1bjNKK0dpUUdJRTcxZldZOUtFQlFSMUQzT1h1TFlNZw==|7015dcf5d48280e56784ef6d89a2b4d15352adcb1e257453c9907a664ceaa0d9; captcha_ticket_v2=2|1:0|10:1736148810|17:captcha_ticket_v2|728:eyJ2YWxpZGF0ZSI6IkNOMzFfKkxBeEhrcDFRYnhkeGxtTGRnSlZZWVdHbjhtLlc4R3BmQWl5dFNLYlA1ekZITXYydi5KRypfa3plX3poZGJRRExveDlSR3lFVDJJbmQ4Q1ZHNnJFSWkqdyppMDZKMzlXYVZJcFlIX0VHc0ZRRDBsa1BvblJrNVJldWVDNFRNeFBXRmZZcXVJWHB1Yip0ckY1SWFQdVNoS0YyKjZmUHhUKnFiOXpDaFVsc0djdHVBX1hjWENfYkNQemlxREwwSWpWekdSZHlqWUFWQ0d5aE5TaWFzZEhzdWRDWCprYy5QKkh6ajV6VU9nM2lQT296YVBxSzlXemYqcXZzRF9ENWVPU2dGQmhoUWd3d3RZb3ByT0t0M0ZpVSp3SUZsc1FmZTVMRDJwSDhTcHBOZUF4VkhWYkVEdC4zVU1USkl3SjhRWCoqb0xrKnlhU2RudDNXOElfV01mMV84RzF1d05NeUpJS0YqcmJ3MTBsejNDX2lnMFJYM2FMUTBZT04ySDFqRkpZYlNCMCpfVjJseENYdmRPMEtZanJqTEh2b3RFbERnc2xzTDF4eS5TSWZSU3owY3kxRmFpU3hadkNxV00ubUJvbExKQy5JR1BvMG1uLmtNZHRuNElMMGx6NE9DcmdKMXFHeV81aUhUMGhNajZxVTRmdFlSU1JlNUxlbkJGWWdzcnZWZnFxNVk3N192X2lfMSJ9|9a0d71076f5273fc22dd67c3e4eabb975e479e875191d79a44e51972d2d848cc; z_c0=2|1:0|10:1736148810|4:z_c0|92:Mi4xN1lrbldnQUFBQUFBTUJKTENYQ1BHU1lBQUFCZ0FsVk5TdFZvYUFCUDI3aTJFNk5MNThBZ215bXIyZVJIWXhvNXN3|707e10b967760748115ffeee34231345d9323f18a7e61ff853b0d3cf9967dae2; ecom_merchant=1857114769787609088; BEC=46faae78ffea44ab7c29d705bdab5c18",
+                "Referer": "https://ecom.zhihu.com/vip-apps/out-distribution/contentLib/list",
+                "Referrer-Policy": "no-referrer-when-downgrade"
+            },
+            "body": null,
+            "method": "GET"
+          });
+    
+        const data =  await response.json();
+        console.log('知乎书籍::响应数据:', data);
+        if(data.data!=undefined&&data.data.length<=0){
+            throw data
+        }
+        console.log('知乎书籍::响应状态:', response.status);
+        let book_info = data.data[0]
+        let info = {}
+        info.words =  book_info.word_count
+        info.book_name =  book_info.work_title
+        info.book_id =  book_info.work_id
+        // info.genre = 999;
+        info.genre = 3;
+        return info;
+    }catch(error){
+        console.error('知乎书籍::请求错误:', error);
+        return null;
+    }
+
+}
+
+module.exports = CMD;

+ 56 - 0
src/helper.js

@@ -344,4 +344,60 @@ helper.getPaginationParams = function(total, pageSize = 500) {
     
     return result;
 }
+
+helper.parseJsonP = function(jsonpStr){
+    // 去除JSONP包装,提取纯JSON字符串
+    const jsonStr = jsonpStr.replace(/^_aq_88765\((.*)\)$/, '$1');
+    // 解析JSON
+    try {
+        const data = JSON.parse(jsonStr);
+        
+        // 现在可以访问解析后的数据
+        console.log('Status:', data.state);
+        console.log('Session:', data.sess);
+        
+        // 访问验证码配置
+        const captchaConfig = data.data.comm_captcha_cfg;
+        console.log('TDC Path:', captchaConfig.tdc_path);
+        
+        // 访问显示信息
+        const showInfo = data.data.dyn_show_info;
+        console.log('Language:', showInfo.lang);
+        console.log('Instruction:', showInfo.instruction);
+        
+
+        // 访问背景元素配置
+        const bgConfig = showInfo.bg_elem_cfg;
+        const img_url_1 = bgConfig.img_url
+        // const img_url_1 = showInfo.sprite_url.replace('img_index=0', 'img_index=1');
+        const img_url_0 = showInfo.sprite_url
+        console.log('Background Size:', bgConfig.size_2d);
+        
+        // 访问前景元素列表
+        showInfo.fg_elem_list.forEach((elem, index) => {
+            console.log(`Element ${index + 1}:`, {
+                id: elem.id,
+                size: elem.size_2d,
+                position: elem.init_pos
+            });
+        });
+        
+        // 访问绑定配置
+        showInfo.fg_binding_list.forEach((binding, index) => {
+            console.log(`Binding ${index + 1}:`, {
+                master: binding.master,
+                slave: binding.slave,
+                type: binding.bind_type
+            });
+        });
+        return {img_url_1:img_url_1.match(/image=([^&]+)/)[1],img_url_0:img_url_0.match(/image=([^&]+)/)[1]}
+
+    } catch (error) {
+        console.error('Error parsing JSON:', error);
+        return null
+    }
+}
+
+
+
 module.exports = helper;

+ 379 - 0
test.py

@@ -0,0 +1,379 @@
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.support.wait import WebDriverWait
+from selenium.webdriver.support import expected_conditions as EC
+import time
+from PIL import Image
+import cv2
+from selenium.webdriver import ActionChains
+import requests
+from io import BytesIO
+ 
+ 
+class MaoYanCode(object):
+    # 初始化
+    def __init__(self):
+        self.url = 'https://open.yuewen.com/'
+        self.browser = webdriver.Chrome()
+        self.wait = WebDriverWait(self.browser, 10)
+        
+    def open(self):
+        # 打开网页
+        self.browser.get(self.url)
+    
+    # 定位背景图
+    def bg_img_src(self):
+        try:
+                # 定位背景图片元素
+                bg_element = self.wait.until(
+                    EC.presence_of_element_located((
+                        By.CSS_SELECTOR,
+                        'div.tc-bg-img#slideBg'
+                    ))
+                )
+                
+                # 获取style属性
+                style = bg_element.get_attribute('style')
+                
+                # 提取background-image URL
+                import re
+                url_match = re.search(r'background-image: url\("([^"]+)"\)', style)
+                if url_match:
+                    return url_match.group(1)
+                
+                return None
+                
+        except Exception as e:
+            print(f"获取背景图片URL失败: {str(e)}")
+            return None
+ 
+    # 定位缺块
+    def jpp_img_src(self):
+        try:
+            # 等待元素加载
+            target_element = self.wait.until(
+                EC.presence_of_element_located((
+                    By.CSS_SELECTOR,
+                    'div.tc-fg-item[aria-label="拖动下方滑块完成拼图"]'
+                ))
+            )
+            
+            # 获取背景图片URL
+            style = target_element.get_attribute('style')
+            
+            # 从style中提取background-image的url
+            import re
+            url_match = re.search(r'background-image: url\("([^"]+)"\)', style)
+            if url_match:
+                return url_match.group(1)
+                
+            return None
+                
+        except Exception as e:
+            print(f"获取图片URL失败: {str(e)}")
+            return None
+    
+    # 获取背景和缺块图片
+    def get_img(self):
+        bg_src = self.bg_img_src()
+        jpp_src = self.jpp_img_src()
+        response1 = requests.get(bg_src)
+        image1 = Image.open(BytesIO(response1.content))
+        image1.save('bg_img.png')
+ 
+        response2 = requests.get(jpp_src)
+        image2 = Image.open(BytesIO(response2.content))
+        image2.save('jpp_img.png')
+        return image1, image2
+    
+    # 定位滑块
+    def slider_element(self):
+        time.sleep(2)
+        slider = self.wait.until(EC.presence_of_element_located(
+            (By.XPATH,
+             '//*[@class="tc-drag-thumb"]')))
+        return slider
+    # 登录按钮
+    def login_btn_element(self):
+        time.sleep(2)
+        btn = self.wait.until(EC.presence_of_element_located(
+            (By.XPATH,
+             '//*[@class="op-header-info"]')))
+        return btn
+    def input_username(self, username="zywz2024013@163.com", retries=3):
+        time.sleep(2)
+        """
+        输入用户名
+        
+        Args:
+            username: 要输入的用户名
+            retries: 重试次数
+        """
+        for attempt in range(retries):
+            try:
+                # 多种定位方式
+                selectors = [
+                    (By.CSS_SELECTOR, 'input[name="username"]'),
+                    (By.CSS_SELECTOR, 'input[placeholder="用户名"]'),
+                    (By.CSS_SELECTOR, '.el-input__inner[name="username"]'),
+                    (By.XPATH, '//input[@name="username"]'),
+                ]
+
+                # 尝试每种定位方式
+                for selector in selectors:
+                    try:
+                        # 等待元素可见和可交互
+                        input_element = self.wait.until(
+                            EC.element_to_be_clickable(selector)
+                        )
+                        
+                        # 清除现有内容
+                        input_element.clear()
+                        
+                        # 输入用户名
+                        input_element.send_keys(username)
+                        
+                        # 验证输入是否成功
+                        if self.verify_input(input_element, username):
+                            self.logger.info("用户名输入成功")
+                            return True
+                            
+                    except Exception as e:
+                        self.logger.warning(f"使用选择器 {selector} 失败: {str(e)}")
+                        continue
+
+            except Exception as e:
+                self.logger.error(f"输入尝试 {attempt + 1}/{retries} 失败: {str(e)}")
+                if attempt < retries - 1:
+                    time.sleep(1)
+                continue
+                
+        self.logger.error("所有输入尝试均失败")
+        return False
+    def input_password(self, password="Huang888888", retries=3):
+        time.sleep(2)
+        """
+        输入密码
+        
+        Args:
+            password: 要输入的密码
+            retries: 重试次数
+        """
+        for attempt in range(retries):
+            try:
+                # 多种定位方式
+                selectors = [
+                    (By.CSS_SELECTOR, 'input[name="password"]'),
+                    (By.CSS_SELECTOR, 'input[type="password"]'),
+                    (By.CSS_SELECTOR, '.el-input__inner[name="password"]'),
+                    (By.XPATH, '//input[@name="password"]'),
+                ]
+
+                for selector in selectors:
+                    try:
+                        # 等待密码输入框可交互
+                        password_input = self.wait.until(
+                            EC.element_to_be_clickable(selector)
+                        )
+                        
+                        # 清除现有内容
+                        password_input.clear()
+                        
+                        # 安全输入密码
+                        self.safe_input_password(password_input, password)
+                        
+                        # 验证输入
+                        if self.verify_password_input(password_input, password):
+                            self.logger.info("密码输入成功")
+                            return True
+                            
+                    except Exception as e:
+                        self.logger.warning(f"使用选择器 {selector} 失败: {str(e)}")
+                        continue
+
+            except Exception as e:
+                self.logger.error(f"密码输入尝试 {attempt + 1}/{retries} 失败: {str(e)}")
+                if attempt < retries - 1:
+                    time.sleep(1)
+                continue
+                
+        self.logger.error("所有密码输入尝试均失败")
+        return False
+    def click_login_button(self, retries=3):
+        time.sleep(2)
+        """
+        点击登录按钮
+        
+        Args:
+            retries: 重试次数
+        Returns:
+            bool: 是否点击成功
+        """
+        for attempt in range(retries):
+            try:
+                # 多种定位方式
+                selectors = [
+                    (By.CSS_SELECTOR, '.el-button.loginin'),
+                    (By.CSS_SELECTOR, '[data-v-0101b427].el-button'),
+                    (By.XPATH, "//button[contains(@class, 'loginin')]"),
+                    (By.XPATH, "//button[.//span[text()='登录']]"),
+                ]
+
+                for selector in selectors:
+                    try:
+                        # 等待按钮可点击
+                        button = self.wait.until(
+                            EC.element_to_be_clickable(selector)
+                        )
+                        
+                        # 确保按钮在视图中
+                        self.scroll_to_element(button)
+                        
+                        # 尝试点击
+                        if self.safe_click(button):
+                            self.logger.info("登录按钮点击成功")
+                            
+                            # 等待页面响应
+                            if self.wait_for_response():
+                                return True
+                            
+                    except ElementClickInterceptedException:
+                        # 如果按钮被遮挡,尝试JavaScript点击
+                        self.logger.warning("按钮被遮挡,尝试JavaScript点击")
+                        if self.js_click(button):
+                            return True
+                            
+                    except Exception as e:
+                        self.logger.warning(f"使用选择器 {selector} 失败: {str(e)}")
+                        continue
+
+            except Exception as e:
+                self.logger.error(f"点击尝试 {attempt + 1}/{retries} 失败: {str(e)}")
+                if attempt < retries - 1:
+                    time.sleep(1)
+                continue
+                
+        self.logger.error("所有点击尝试均失败")
+        return False
+    def jump_login_view(self):
+        time.sleep(2)
+        login_btn = self.wait.until(
+            EC.element_to_be_clickable((By.CSS_SELECTOR, '[data-v-0101b427]'))
+        )
+        login_btn.click()
+    # 识别缺口
+    def get_gap(self, gap_img):
+        bg_img = cv2.imread('bg_img.png')
+        tp_img = cv2.imread('jpp_img.png')
+ 
+        # 识别图片边缘
+        bg_edge = cv2.Canny(bg_img, 100, 200)
+        tp_edge = cv2.Canny(tp_img, 100, 200)
+ 
+        # 转换图片格式
+        # 灰度图片转为RGB彩色图片
+        bg_pic = cv2.cvtColor(bg_edge, cv2.COLOR_GRAY2RGB)
+        tp_pic = cv2.cvtColor(tp_edge, cv2.COLOR_GRAY2RGB)
+ 
+        # 缺口匹配
+        res = cv2.matchTemplate(bg_pic, tp_pic, cv2.TM_CCOEFF_NORMED)
+        min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)  # 寻找最优匹配
+ 
+        # 绘制方框
+        # img.shape[:2] 获取图片的长、宽
+        height, width = tp_pic.shape[:2]
+        tl = max_loc  # 左上角点的坐标
+        # cv2.rectangle(img, (x1, y1), (x2, y2), RGB颜色值, 边框宽度--->若为负则填充整个矩形)
+        cv2.rectangle(bg_img, tl, (tl[0] + width - 15, tl[1] + height - 15),
+                      (0, 0, 255), 2)  # 绘制矩形
+        cv2.imwrite(gap_img, bg_img)  # 保存在本地
+        # cv2.imshow('Show', bg_img)
+        # cv2.waitKey(0)
+        # cv2.destroyAllWindows()
+        # 返回缺口的X坐标
+        return tl[0]
+    
+    # 构造移动轨迹
+    def get_track(self, distance):
+        # 移动轨迹
+        track = []
+        # 当前位移
+        current = 0
+        # 减速阈值
+        mid = distance * 4 / 5
+        # 计算间隔
+        t = 0.2
+        # 初速度
+        v = 0
+ 
+        while current < distance:
+            if current < mid:
+                # 加速度为正5
+                a = 5
+            else:
+                # 加速度为负3
+                a = -3
+            # 初速度v0
+            v0 = v
+            # 当前速度v = v0 + at
+            v = v0 + a * t
+            # 移动距离x = v0t + 1/2 * a * t^2
+            move = v0 * t + 1 / 2 * a * t * t
+            # 当前位移
+            current += move
+            # 加入轨迹
+            track.append(round(move))
+        return track
+    
+    # 移动滑块
+    def move_to_gap(self, slider, track):
+        # click_and_hold()按住底部滑块
+        ActionChains(self.browser).click_and_hold(slider).perform()
+        for x in track:
+            ActionChains(self.browser).move_by_offset(xoffset=x,
+                                                      yoffset=0).perform()
+        time.sleep(0.5)
+        # release()松开鼠标
+        ActionChains(self.browser).release().perform()
+ 
+    def login(self):
+        self.open()
+        time.sleep(2)
+        # 网速原因可能导致网页加载不完全,致使iframe报错
+        iframe = self.wait.until(
+            EC.presence_of_all_elements_located((By.TAG_NAME, 'iframe')))
+        self.wait.until(
+            EC.frame_to_be_available_and_switch_to_it(iframe[1]))
+        
+
+        login_btn = self.login_btn_element()
+        login_btn.click()
+   
+        self.jump_login_view()
+
+        self.input_username()
+
+        self.input_password()
+
+        self.click_login_button()
+
+        self.get_img()
+        slider = self.slider_element()
+        slider.click()
+        gap = self.get_gap('result.png')
+        # 页面为360*360,图片为680*390,更改比例,减去初始位移
+        gap_end = int((gap - 40) / 2)
+        # 获取缺口
+        print('缺口位置', gap_end)
+        # 减去缺块白边
+        gap_end -= 10
+        # 获取移动轨迹
+        track = self.get_track(gap_end)
+        print('滑动轨迹', track)
+        # 拖动滑块
+        self.move_to_gap(slider, track)
+ 
+ 
+if __name__ == '__main__':
+    crack = MaoYanCode()
+    crack.login()

+ 5 - 2
tg_factory_main.js

@@ -38,8 +38,11 @@ if(config.isDebug){
             if(isInit){
                 return
             }
-            // isInit = true
-            // await redis_help.setKeyValue("OPENSESSID","e1eb930167e205912909fcfaa6e5c5c8")
+            isInit = true
+            // let yw_login = await require('./src/api/yw/yw_login').get_captcha()
+            // let zh_book_data = await require('./src/api/zh/zh_search_book').search_id("1864355654438887424")
+            // console.log("zh_book_data:",zh_book_data)
+            // await redis_help.setKeyValue("OPENSESSID","9077ffcc5ca974e2c0e78502a24c9053")
             // await require('./src/api/yw/get_book_tg_chapter_id').get_book_tg_chapter_id("20879682908817006")
             // await redis_help.setKeyValue("OPENSESSID","ada39ae2c1286d1be3bd32e300951fff")
 

+ 50 - 0
tools.js

@@ -2,6 +2,7 @@ const crypto = require('crypto');
 const config = require('./etc/config.json');
 const { default: axios } = require('axios');
 const http = require('http');
+const moment = require('moment');
 class tools {
     constructor(redis_help){
         this.redis_help = redis_help
@@ -158,6 +159,55 @@ class tools {
         return this.getRandomElement(temp)
     }
 
+
+
+    async getYwOPENSESSID() {
+        let open_sessid = '9077ffcc5ca974e2c0e78502a24c9053'
+        let list =  await this.redis_help.getKeyValue("all_yw_key")
+        if(list == null){
+            return open_sessid
+        }
+        list = JSON.parse(list)
+        if(list.length<=0){
+            return open_sessid
+        }
+        for (let index = 0; index < list.length; index++) {
+            const yw_data = list[index];
+            const old_time = yw_data.create_time;
+            const current_time = moment();
+            const past_time = moment(old_time);
+            // 计算时间差(小时)
+            const diff_hours = current_time.diff(past_time, 'hours');
+            // 判断是否小于7小时
+            const isLessThan7Hours = diff_hours < 7;
+
+            if(isLessThan7Hours){
+                this.redis_help.setKeyValue("OPENSESSID",yw_data.open_sessid)
+            }else{
+                this.redis_help.setKeyValue("OPENSESSID","")
+            }
+            return open_sessid
+        }
+    }
+
+    getOneNewClinetBuffer(headers=null){
+        return axios.create({
+            timeout: 30000,
+            headers:headers||{},
+            responseType:"arraybuffer",
+            // 使用独立的 agent,不影响其他连接
+            httpAgent: new http.Agent({ 
+                keepAlive: true,
+                maxSockets: 5, // 允许适度的并发
+                maxFreeSockets: 2,
+                timeout: 30000
+            }),
+            validateStatus: function (status) {
+                return status >= 200 && status < 300;
+            }
+        });
+    }
+
     getOneNewClinet(headers=null){
         return axios.create({
             timeout: 30000,